'Can't set attribute' with new-style properties in Python

Python

Python Problem Overview


I'm trying to use new-style properties declaration:

class C(object):
    def __init__(self):
        self._x = 0

    @property
    def x(self):
        print 'getting'
        return self._x

    @x.setter
    def set_x(self, value):
        print 'setting'
        self._x = value

if __name__ == '__main__':
    c = C()
    print c.x
    c.x = 10
    print c.x

and see the following in console:

pydev debugger: starting
getting
0
File "\test.py", line 55, in <module>
c.x = 10
AttributeError: can't set attribute

what am I doing wrong? P.S.: Old-style declaration works fine.

Python Solutions


Solution 1 - Python

The documentation says the following about using decorator form of property:

> Be sure to give the additional functions the same name as the original property (x in this case.)

I have no idea why this is since if you use property as function to return an attribute the methods can be called whatever you like.

So you need to change your code to the following:

@x.setter
def x(self, value):
    'setting'
    self._x = value

Solution 2 - Python

The setter method has to have the same name as the getter. Don't worry, the decorator knows how to tell them apart.

@x.setter
def x(self, value):
 ...

Solution 3 - Python

When you call @x.setter, @x.getter, or @x.deleter, you're creating a new property object and giving it the name of the function you're decorating. So really, all that matters is that the last time you use a @x.*er decorator in the class definition, it has the name you actually want to use. But since this would leave your class namespace polluted with incomplete versions of the property you wish to use, it's best to use the same name to clean them up.

Solution 4 - Python

If you don't want the extra _x name slot, here's a complex little trick you can do:
(tested with Py34)

>>> class C(object):
	__slots__ = ['x'] # create a member_descriptor
	def __init__( self ):
		self.x = 0
        # or use this if you don't want to call x_setter:
        #set_x( self, 0 )

		
>>> get_x = C.x.__get__ # member_descriptor getter
>>> set_x = C.x.__set__ # member_descriptor setter
>>> # define custom wrappers:
>>> def x_getter( self ):
	print('getting')
	return get_x( self )

>>> def x_setter( self, value ):
	print('setting')
	set_x( self, value )

	
>>> C.x = property( x_getter, x_setter ) # replace the member_descriptor
>>> c = C()
setting
>>> print(c.x)
getting
0
>>> c.x = 10
setting
>>> 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMaxim PopravkoView Question on Stackoverflow
Solution 1 - PythonDave WebbView Answer on Stackoverflow
Solution 2 - PythonIgnacio Vazquez-AbramsView Answer on Stackoverflow
Solution 3 - PythonKyleView Answer on Stackoverflow
Solution 4 - PythonTcllView Answer on Stackoverflow