I want to create an attribute in class that connected to another attribute. for example biz
is bar+1
.
The next code is working:
class Foo:
def __init__(self, bar, biz=None):
self.bar = bar
biz = property(lambda self: self.bar + 1)
print(Foo(0).biz+1)
and print 2
. But when I move the biz
declaretion into the initializer, for example to apply a condition, I get an error:
class Foo:
def __init__(self, bar, biz=None):
self.bar = bar
if biz is None:
self.biz = property(lambda self: self.bar + 1)
else:
self.biz = biz
print(Foo(0).biz+1)
return TypeError: unsupported operand type(s) for +: 'property' and 'int'
Why python relate to the attribute as 'property' type and not as 'int'?
EDIT:
I found that if I use self.__class__.biz=property(...)
it's working, but I'm still asking why I can't use property locally?
Properties are implemented using descriptors. As per the docs:
A descriptor is what we call any object that defines
__get__()
,__set__()
, or__delete__()
.Optionally, descriptors can have a
__set_name__()
method. This is only used in cases where a descriptor needs to know either the class where it was created or the name of class variable it was assigned to. (This method, if present, is called even if the class is not a descriptor.)Descriptors get invoked by the dot operator during attribute lookup. If a descriptor is accessed indirectly with
vars(some_class)[descriptor_name]
, the descriptor instance is returned without invoking it.Descriptors only work when used as class variables. When put in instances, they have no effect.
The main motivation for descriptors is to provide a hook allowing objects stored in class variables to control what happens during attribute lookup.
Traditionally, the calling class controls what happens during lookup. Descriptors invert that relationship and allow the data being looked-up to have a say in the matter.
Descriptors are used throughout the language. It is how functions turn into bound methods. Common tools like
classmethod()
,staticmethod()
,property()
, andfunctools.cached_property()
are all implemented as descriptors.
In the first case, you set the property on the class level, which is why it works. This is also why it works when you do self.__class__.biz=property(...)
. However, you should not modify the class definition from an instance constructor, as creating multiple instances will overwrite the property being set on the class.
To achieve your goal, I would instead write the property like this:
class Foo:
def __init__(self, bar, biz=None):
self.bar = bar
self._biz = biz
@property
def biz(self):
if self._biz is None:
return self.bar + 1
else:
return self._biz