Why does @foo.setter in Python not work for me?
You seem to be using classic old-style classes in python 2. In order for properties to work correctly you need to use new-style classes instead (in python 2 you must inherit from object
). Just declare your class as MyClass(object)
:
class testDec(object):
@property
def x(self):
print 'called getter'
return self._x
@x.setter
def x(self, value):
print 'called setter'
self._x = value
It works:
>>> k = testDec()
>>> k.x
called getter
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/devel/class_test.py", line 6, in x
return self._x
AttributeError: 'testDec' object has no attribute '_x'
>>> k.x = 5
called setter
>>> k.x
called getter
5
>>>
Another detail that might cause problems is that both methods need the same name for the property to work. If you define the setter with a different name like this it won't work:
@x.setter
def x_setter(self, value):
...
And one more thing that is not completely easy to spot at first, is the order: The getter must be defined first. If you define the setter first, you get name 'x' is not defined
error.
@property setter not called in python 3
The setter is only invoked when you try to assign to the property. In the line
pozza = pizzo.getCiao('setter')
you first access the value of the property with pizzo.getCiao
(which calls the getter and returns the string "getter"
, then attempts to call the string as a function with the argument 'setter'
.
You have basially created a read-only property:
>>> pizzo = DumbClass('getter')
>>> pizzo.getCiao
'getter'
>>> pizzo.getCiao = 'foo'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
This is because you didn't use the same name when defining both the getter and setter, so your property doesn't have a setter. (Arguably, getCiao.setter
should probably raise an error right away, since the name matters, but it doesn't. C'est la vie.)
So the right thing to do is use the same name for both. The usual convention is to use an private variable with the "same" name as the property to store the underlying data for simple properties like this.
class DumbClass:
def __init__(self, p):
self._ciao = p
@property
def ciao(self):
return self._ciao
@ciao.setter
def ciao(self, v):
self._ciao = v
Now you should be able to both get and set the value of the propery.
>>> d = DumbClass("hi")
>>> d.ciao
'hi'
>>> d.ciao = 'bye'
>>> d.ciao
'bye'
Python @property does not work as I want it to
If you'd use C
properly (ie create an instance of it) you will get what you expect:
c = C()
c.x = 5
print(c.x)
outputs
setter
getter
5
Why is my python 2.7 getter acting like a setter?
Add object
as parent class.
class Testclass(object):
def __init__(self):
self._testvar = None
@property
def testvar(self):
return self._testvar
@testvar.setter
def testvar(self, value):
self._testvar = value
testObj = Testclass()
print(testObj.testvar)
testObj.testvar = "test"
print(testObj.testvar)
>>> None
>>> test
Property setter gives an error in Python
A setter is used with the assignment operator =
:
c.foo = 2
A further example how c.foo(2)
works:
In [7]: c = C(lambda x: x*x)
In [8]: c.foo(2)
Out[8]: 4
c.foo(…)
first gets the value in c.foo
, then invokes the function.
Python's property decorator does not work as expected
The property
decorator only works with new-style classes (see also). Make Silly
extend from object
explicitly to make it a new-style class. (In Python 3, all classes are new-style classes)
class Silly(object):
@property
def silly(self):
# ...
# ...
why the use of self.foo = self.foo in __init__ in Python class?
With a bit more code, its use may make some sense in two separate cases, although it's still not always the best way to achieve what it achieves:
class MyClass:
baz = 1
def __init__(self):
self.baz = self.baz
self._foo = None
self.bar = None
self.foo = self.foo
@property
def foo(self):
if self._foo is None:
self._foo = 1
return self._foo
@foo.setter
def foo(self, value):
self.bar = 0
self._foo = value
m1 = MyClass()
m2 = MyClass()
print(m1.foo, m1.bar, m1.baz)
m1.baz = 2
MyClass.baz = 3
print(m1.baz, m2.baz, MyClass.baz)
A bit contrived, but what self.foo = self.foo
would achieve is that both the getter and setter for the .foo
property will have been called.
People saying .foo
cannot be accessed at that point and would raise an AttributeError
are right in the limited case you're sharing, but of course defining a property makes it available in the constructor as well, as the example here shows. I kept the code a bit cleaner by defining ._foo
first, but of course you could just first define it in the setter, that's not recommended though.
That setters and getters are run is mainly important if they have side effects, which is one reason to use properties (another common one being that you want to limit access, or restrict possible values).
Edit: The example in networkx
is reassigning a class attribute to an instance attribute (I wasn't aware that worked until I tried). See the result of the final print()
in my example:
m1.baz = 2
MyClass.baz = 3
print(m1.baz, m2.baz, MyClass.baz)
Result:
2 1 3
The reason networkx
might be doing this is to allow you to supply a different Graph factory method, without affecting the class - only your instance would get a new one if you assign to its graph_attr_dict_factory
, but it is initialised with the class factory method.
However, note that even if networkx
didn't set it up like that in the constructor, a user could still assign a new factory to the instance later and see the same behaviour - the only real difference is that the instance wouldn't have an instance attribute up to that point and would have been accessing the class attribute instead. It's possible that other code relies on that distinction, so removing the assignment could have an impact elsewhere, but it does nothing for the immediate functionality of the class itself.
Creating property with abstract getter doesn't work as expected
The difference is that this code
BAR = property(_get_bar)
is executed only once when the body of your base class (Foo
) is executed -
the _getbar
in this expression refers to the method defined in this class (Foo), which is abstract and empty.
While the form
@property
def BAR(self) -> bool:
return self._get_bar()
encapsulates the reference to _get_bar
inside a method (BAR), which will receive the instance as a parameter, and then retrieve the method from whatever subclass from Foo
the instance (self
) happens to be.
The way to fix that is to wrap the property argument in the function call form as a lambda, so that the method is retrieved from the appropriate class on call time:
BAR = property(lambda self: self._get_bar())
property.setter and inheritance
You can use @BaseClass.foo.getter
to create a copy of the property with a different getter:
class Subclass(BaseClass):
@BaseClass.foo.getter
def foo(self):
print('Subclass')
See the property
documentation for details.
How does the @property decorator work in Python?
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 and a property
object with just a getter:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> 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__)
Related Topics
Customising Code of Qt Designer Widget
Is Tensorflow Compatible with a Windows Workflow
Any Reason Not to Use '+' to Concatenate Two Strings
Simple Animation Using Tkinter
Removing Entries from a Dictionary Based on Values
Cannot Use Geometry Manager Pack Inside
Is There a Python Module to Solve Linear Equations
Calling Functions from a Tkinter Frame to Another
How to Print a Percentage Value in Python
Type Annotations for *Args and **Kwargs
How to Integrate Flask & Scrapy
Numpy: Fix Array with Rows of Different Lengths by Filling the Empty Elements with Zeros
How to Replace (Or Strip) an Extension from a Filename in Python