Here's a more thorough example of why you would want to have a private attribute: data type validation (as pointed out by the other answers).
Let's say that you've been using the Dog class for a few weeks, and a new bug has come up that happens when the name attribute is (for whatever reason) set to a value that's not a string. You now need to change the code in order to enforce that a Dog's name is indeed a string, by raising an informative Exception. How do you do that?
Your first idea may be to simply check the name's data type after creating a new Dog (using the isinstance builtin function):
my_dog = Dog(name='Max', age=5)
if not isinstance(my_dog.name, str):
raise ValueError("The Dog's name is not a string")
This works, but there's a clear issue here: you need to 1) change every line of code where a Dog is created, and 2) you need to tell anyone who ever uses your Dog class that, by the way, remember to check for the name's data type. This is clearly a lot of work, and not future-proof by any means - what if you or another user forgets to do this check? What if no one ever reads your documentation on the Dog class?
You may then think to perform this check in the __init__ method:
def class Dog:
def __init__(self, name, age):
if not isinstance(name, str):
raise ValueError("The Dog's name is not a string")
self.__name = name
self.age = age
This cuts down on the work, but it doesn't stop someone from modifying the Dog's name after it was instantiated:
my_dog = Dog(name='Max', age=5)
my_dog.name = 10 # oops, wrong value!
What we want to do is to actually move this type-checking functionality to a method that runs every time the name attribute gets set - which is exactly what the setter method (the one decorated with the @name.setter decorator) does:
def class Dog:
def __init__(self, name, age):
self.__name = name
self.age = age
@property
def name(self):
return self.__name
@name.setter
def name(self, new_name)
if not isinstance(new_name, str):
raise ValueError("The Dog's name is not a string")
self.__name = new_name
Now the name's data type is always checked for, both during instantiation (in the __init__ method) and afterwards.
Data type validation is simply an example. The point of using private attributes and their getters/setters is to combine attribute access with some custom functionality. If you desire no functionality whatsoever when accessing/modifying the attribute, you're correct in your observation that you don't need to bother making it private and writing the getter/setter methods in the first place.
Answer from jfaccioni on Stack ExchangeHere's a more thorough example of why you would want to have a private attribute: data type validation (as pointed out by the other answers).
Let's say that you've been using the Dog class for a few weeks, and a new bug has come up that happens when the name attribute is (for whatever reason) set to a value that's not a string. You now need to change the code in order to enforce that a Dog's name is indeed a string, by raising an informative Exception. How do you do that?
Your first idea may be to simply check the name's data type after creating a new Dog (using the isinstance builtin function):
my_dog = Dog(name='Max', age=5)
if not isinstance(my_dog.name, str):
raise ValueError("The Dog's name is not a string")
This works, but there's a clear issue here: you need to 1) change every line of code where a Dog is created, and 2) you need to tell anyone who ever uses your Dog class that, by the way, remember to check for the name's data type. This is clearly a lot of work, and not future-proof by any means - what if you or another user forgets to do this check? What if no one ever reads your documentation on the Dog class?
You may then think to perform this check in the __init__ method:
def class Dog:
def __init__(self, name, age):
if not isinstance(name, str):
raise ValueError("The Dog's name is not a string")
self.__name = name
self.age = age
This cuts down on the work, but it doesn't stop someone from modifying the Dog's name after it was instantiated:
my_dog = Dog(name='Max', age=5)
my_dog.name = 10 # oops, wrong value!
What we want to do is to actually move this type-checking functionality to a method that runs every time the name attribute gets set - which is exactly what the setter method (the one decorated with the @name.setter decorator) does:
def class Dog:
def __init__(self, name, age):
self.__name = name
self.age = age
@property
def name(self):
return self.__name
@name.setter
def name(self, new_name)
if not isinstance(new_name, str):
raise ValueError("The Dog's name is not a string")
self.__name = new_name
Now the name's data type is always checked for, both during instantiation (in the __init__ method) and afterwards.
Data type validation is simply an example. The point of using private attributes and their getters/setters is to combine attribute access with some custom functionality. If you desire no functionality whatsoever when accessing/modifying the attribute, you're correct in your observation that you don't need to bother making it private and writing the getter/setter methods in the first place.
The advantage of properties is that from the viewpoint of the user of your class, it's syntactically the same as plain attribute access. So you can add logic (validation etc.) at any time without changing the class's interface and breaking the code that uses it. You can even swap in different versions of the class (e.g. with properties that do logging during development, without properties for production) and the caller won't even notice.
Contrast this to a language like Java where best practice is to always write getter/setter methods even if you don't need them, because you might need them later, but can't add them later without changing the API and thereby breaking code that uses the class.
BTW, there are no truly private attributes in Python. The leading __ is intended to keep names from conflicting among subclasses of the base class, where that might be a problem.
properties - How does the @property decorator work in Python? - Stack Overflow
When not to use @property decorator?
Use of "@property" decorator
They are functionally equivalent, yes.
First, consider what happens without the decorator. An object has properties, which can be either attributes or functions. If you remove the @property decorator from your class definition and change the line above that to self.foo = 10, then the line a = A() creates a new object that has two attributes: a.foo and a.value. Now, a.foo is an int, which is a static sort of object. For one thing, you can't call it. a.foo() would be a syntax error. a.value, on the other hand, is a function, and so a.value() does make sense, and what python does is finds the function named a.value, runs it, and returns the value.
Now, when you add a @property decorator, it makes python think that a.value is actually an attribute property, not a function property. All that really changes is the syntax. Now you don't have to call a.value(), you just reference a.value as though it were some static sort of value. Why would you want to do this? Well, imagine that you are working with a number of classes, and it makes sense for all of them to have some similar property. For example, say you are making a phone directory of professors that will return the current phone number when requested. Usually it's fine for you to just reference prof.number, since it stays the same. But say you want to add a new person who spends half their time in Australia and half in Arizona. You could completely re-write the professor class to change the sort of thing that prof.number is, and let it look up the number when needed. If you already have a lot of code, though, that can be problematic, since you need to go through and change everything. So an alternative is to create a new subclass of professor that has a @property that will do some sort of calculation to determine the correct number at this time of year. Now everywhere that you referenced prof.number will still work just as before, but you can still do that sort of calculation.
To close: a word of warning. Keep in mind that using the @property decorator still means that you're repeatedly calling a function. It's easy to forget this, and you end up referencing prof.number a lot, and if the calculation takes a long time, then you're re-doing it every time. This can get to be a big waste of time.
class property decorators
Why do you want to do this? Having short aliases for longer things is good when you're designing a user interface, but in a class you typically want to have a single source of truth. These aliases make your code significantly more complex so that you can save a little bit of typing elsewhere.
More on reddit.comVideos
The property() function returns a special descriptor object:
>>> property()
<property object at 0x10ff07940>
It is this object that has extra methods:
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
These act as decorators too. They return a new property object:
>>> property().getter(None)
<property object at 0x10ff079f0>
that is a copy of the old object, but with one of the functions replaced.
Remember, that the @decorator syntax is just syntactic sugar; the syntax:
@property
def foo(self): return self._foo
really means the same thing as
def foo(self): return self._foo
foo = property(foo)
so foo the function is replaced by property(foo), which we saw above is a special object. Then when you use @foo.setter(), what you are doing is call that property().setter method I showed you above, which returns a new copy of the property, but this time with the setter function replaced with the decorated method.
The following sequence also creates a full-on property, by using those decorator methods.
First we create some functions:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
Then, we create a property object with only a getter:
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
Next we use the .setter() method to add a setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
Last we add a deleter with the .deleter() method:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
Last but not least, the property object acts as a descriptor object, so it has .__get__(), .__set__() and .__delete__() methods to hook into instance attribute getting, setting and deleting:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
The Descriptor Howto includes a pure Python sample implementation of the property() type:
class Property: "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
The documentation says it's just a shortcut for creating read-only properties. So
@property
def x(self):
return self._x
is equivalent to
def getx(self):
return self._x
x = property(getx)
The @property decorator comes with a whole system with getters and setters.
However I have been using it only to provide a cleaner access to a method which doesn't take any arguments and not for anything else.
If the method has to accept arguments in future, I remove the decorator and update the usages.
Are there any downsides to this? What are some other scenarios to not use @property?