The property object is what you are actually thinking of as a property. Consider this example:
class Foo(object):
def __init__(self):
self._bar = 0
@property
def bar(self):
return self._bar + 5
Foo.bar is a property object which has a __get__ method. When you write something like
x = Foo()
print(x.bar)
the lookup for x.bar finds that type(x).bar has a __get__ method, and so the attribute lookup becomes equivalent to
type(x).bar.__get__(x, type(x))
which produces the value x._bar + 5.
The use of property as a decorator somewhat obscures the fact that bar is a property object. An equivalent defintion is
class Foo(object):
def __init__(self):
self._bar = 0
bar = property(lambda self: self._bar + 5)
which shows more explicitly that you are creating a property object with the given lambda expression as the getter for that property, and binding the object to the class attribute bar.
The property class (along with instance methods, class methods, and static methods) is a specific application of Python's general descriptor protocol, which defines the behavior of class attributes with __get__, __set__, and/or __del__ methods.
The property object is what you are actually thinking of as a property. Consider this example:
class Foo(object):
def __init__(self):
self._bar = 0
@property
def bar(self):
return self._bar + 5
Foo.bar is a property object which has a __get__ method. When you write something like
x = Foo()
print(x.bar)
the lookup for x.bar finds that type(x).bar has a __get__ method, and so the attribute lookup becomes equivalent to
type(x).bar.__get__(x, type(x))
which produces the value x._bar + 5.
The use of property as a decorator somewhat obscures the fact that bar is a property object. An equivalent defintion is
class Foo(object):
def __init__(self):
self._bar = 0
bar = property(lambda self: self._bar + 5)
which shows more explicitly that you are creating a property object with the given lambda expression as the getter for that property, and binding the object to the class attribute bar.
The property class (along with instance methods, class methods, and static methods) is a specific application of Python's general descriptor protocol, which defines the behavior of class attributes with __get__, __set__, and/or __del__ methods.
class MyClass:
def __init__(self,*costs):
self.costs = costs
def item_cost(self):
return sum(self.costs)
now you can do
MyClass(1,2,3,4).item_cost() #prints 10
but we can make it a property
class MyClass:
def __init__(self,*costs):
self.costs = costs
@property
def item_cost(self):
return sum(self.costs)
and now we can access it as a simple variable
MyClass(1,2,3,4).item_cost
you could also create a setter for the value with
...
@item_cost.setter
def set_item_cost(self,value):
pass #do something with value
...
MyClass(1,2,3,4).item_cost = "yellow"
In general I find them to be sort of an anti-pattern... but some folks like em
(side note you could also make it a property using it as a regular function instead of a decorator MyClass.item_cost_prop = property(MyClass.item_cost) )
Videos
Hi guys, I'm new to Python and I was curious if I define a class, what is the fuss with getattr, setattr and @property? I've read couple topics on stackexchange, but neither did motivate why I should use them. I mean I can define own functions to set something, and just use car.wheels to get the amount of wheels. No need to define an extra function car.getattr(wheels). What am I missing.
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)