-
Notifications
You must be signed in to change notification settings - Fork 258
property_manifesto
Sources for this manifesto are PEP 8, and also page 241-2 of the book programming in the .NET environment. Quotes, summary below.
The basic principle is that a property should appear to behave more or less exactly as a standard object attribute.
In summary, we propose that properties are only OK in the following two situations:
-
the property only makes an attribute read only OR
-
the property appears to the user as attribute access, with the following restrictions:
- the setter code does not change the attribute value being set AND
- the get and set code is very short (in the order of 5 lines) AND
- the get and set do not set off demanding computation (think IPython tab completion) AND
- the get and set methods are private by convention or otherwise AND
- accessing the property has no effect on the public-facing API of the instance, other than the setter changing the value of the property.
It is OK for a property setter to check the value being passed in, in order to raise an error for incorrect values.
Specifically, these examples are OK:
class C(object): def __init__(self, arg1, arg2): self._arg1= arg1 self._arg2 = None self.arg2 = arg2 # just read only @property def arg1(self): return self._arg1 # just checks but does not change input # uses a nice pattern to hide the get/set methods def arg2(): def fget(self): return self._arg2 def fset(self, arg2): if arg2 not in (1,2,3,4): raise ValueError('arg should be in range 1..4') self._arg2 = arg2 doc = 'argument 2' return locals() arg2 = property(\*\*arg2())
But, this would be a bit goofy:
class D(object): def __init__(self, arg3): self._arg3 = None self.arg3 = arg3 # OK so far - as a above def get_arg3(self): # ouch, public getter, more than one way to do it return self._arg3 def set_arg3(self, arg3): # ditto above, and self._arg3 = arg3 + 1 # odd... arg3 = property(get_arg3, set_arg3, '', 'argument 3')
Giving the following odd results:
d = D(3) print(d.arg3) # gives 4 d.arg3 = 3 print(d.arg3) # 4 again
This is a silly example, but I've written property setters that do
fairly complex transformations in the set_...
method, resulting in
oddnesses like the above.
Quoting from programming in the .NET environment: 'developers have come to think of properties as being synonymous with fields. They do not think of a field as being able to perform some operation; rather a field simply holds information.' The book continues with:
Do use a property if the member has a logical backing store.
Don't use a property (use a method) if:
- the method is a conversion;
- the operation is expensive (orders of magnitude slower than a field set would be);
- the get accessor has an observable side effect;
- calling the member twice in succession produces different results;
- the order of execution is important;
- the member is static but returns a mutable value;
- the member must return an array;
I guess the the 'static' comment refers to .NET syntax in particular. But, they point out that if you want to make an array immutable from the property accessor, you may want to return an array copy, and that can get very expensive if you don't realize it's a copy and start looping over it, as in:
for i in range(len(my_object.arr_property)): j += my_object.arr_property[i]
although on the surface that looks like an unlikely pattern in Python.
This is the property info in PEP 8:
For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax.
Note 1: Properties only work on new-style classes.
Note 2: Try to keep the functional behavior side-effect free, although side-effects such as caching are generally fine.
Note 3: Avoid using properties for computationally expensive operations; the attribute notation makes the caller believe that access is (relatively) cheap.