Here is a pure Python-specific design question:
class MyClass(object):
...
def get_my_attr(self):
...
def set_my_attr(self, value):
...
and
class MyClass(object):
...
@property
def my_attr(self):
...
@my_attr.setter
def my_attr(self, value):
...
Python lets us to do it either way. If you would design a Python program, which approach would you use and why?
I think both have their place. One issue with using
@property
is that it is hard to extend the behaviour of getters or setters in subclasses using standard class mechanisms. The problem is that the actual getter/setter functions are hidden in the property.You can actually get hold of the functions, e.g. with
you can access the getter and setter functions as
C.p.fget
andC.p.fset
, but you can't easily use the normal method inheritance (e.g. super) facilities to extend them. After some digging into the intricacies of super, you can indeed use super in this way:Using super() is, however, quite clunky, since the property has to be redefined, and you have to use the slightly counter-intuitive super(cls,cls) mechanism to get an unbound copy of p.
I feel like properties are about letting you get the overhead of writing getters and setters only when you actually need them.
Java Programming culture strongly advise to never give access to properties, and instead, go through getters and setters, and only those which are actually needed. It's a bit verbose to always write these obvious pieces of code, and notice that 70% of the time they are never replaced by some non-trivial logic.
In Python, people actually care for that kind of overhead, so that you can embrace the following practice :
@property
to implement them without changing the syntax of the rest of your code.Using properties is to me more intuitive and fits better into most code.
Comparing
vs.
is to me quite obvious which is easier to read. Also properties allows for private variables much easier.
Both
@property
and traditional getters and setters have their advantages. It depends on your use case.Advantages of
@property
You don't have to change the interface while changing the implementation of data access. When your project is small, you probably want to use direct attribute access to access a class member. For example, let's say you have an object
foo
of typeFoo
, which has a membernum
. Then you can simply get this member withnum = foo.num
. As your project grows, you may feel like there needs to be some checks or debugs on the simple attribute access. Then you can do that with a@property
within the class. The data access interface remains the same so that there is no need to modify client code.Cited from PEP-8:
Using
@property
for data access in Python is regarded as Pythonic:It can strengthen your self-identification as a Python (not Java) programmer.
It can help your job interview if your interviewer thinks Java-style getters and setters are anti-patterns.
Advantages of traditional getters and setters
Traditional getters and setters allow for more complicated data access than simple attribute access. For example, when you are setting a class member, sometimes you need a flag indicating where you would like to force this operation even if something doesn't look perfect. While it is not obvious how to augment a direct member access like
foo.num = num
, You can easily augment your traditional setter with an additionalforce
parameter:Traditional getters and setters make it explicit that a class member access is through a method. This means:
What you get as the result may not be the same as what is exactly stored within that class.
Even if the access looks like a simple attribute access, the performance can vary greatly from that.
Unless your class users expect a
@property
hiding behind every attribute access statement, making such things explicit can help minimize your class users surprises.As mentioned by @NeilenMarais and in this post, extending traditional getters and setters in subclasses is easier than extending properties.
Traditional getters and setters have been widely used for a long time in different languages. If you have people from different backgrounds in your team, they look more familiar than
@property
. Also, as your project grows, if you may need to migrate from Python to another language that doesn't have@property
, using traditional getters and setters would make the migration smoother.Caveats
Neither
@property
nor traditional getters and setters makes the class member private, even if you use double underscore before its name:Using properties lets you begin with normal attribute accesses and then back them up with getters and setters afterwards as necessary.
Prefer properties. It's what they're there for.
The reason is that all attributes are public in Python. Starting names with an underscore or two is just a warning that the given attribute is an implementation detail that may not stay the same in future versions of the code. It doesn't prevent you from actually getting or setting that attribute. Therefore, standard attribute access is the normal, Pythonic way of, well, accessing attributes.
The advantage of properties is that they are syntactically identical to attribute access, so you can change from one to another without any changes to client code. You could even have one version of a class that uses properties (say, for code-by-contract or debugging) and one that doesn't for production, without changing the code that uses it. At the same time, you don't have to write getters and setters for everything just in case you might need to better control access later.