I am looking at this Python Doc page:
http://docs.python.org/2/library/functions.html#property
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Right below it says:
If then c is an instance of C, c.x will invoke the getter, c.x = value will invoke the setter and del c.x the deleter.
To me, c.x = value looks like assignment of a value to a function, since c.x is a function, unless the "=" operator is overloaded. Is it what is happening here?
Same thing with del c.x
Thanks.
property is a descriptor, which changes the way Python handles attribute access. The Python docs have an article introducing descriptors.
When Python accesses an attribute that points to an object with a __get__ method, it will return what that method returns instead of the object itself. Similarly, = will delegate to __set__ and del to __delete__. The special methods are described in the docs.
Related
Consider the following code taken from the official documentation
class test:
_x = 10
def getx(self): return self._x
def setx(self, value): self._x = value
x = property(getx, setx)
as already explained in many other questions, this is 100% equivalent to
class test:
_x = 10
#property
def x(self):
return self._x
#x.setter
def x(self, val):
self._x = val
I would like to access the property x (and not the int in _x) in order to change the value of x.setter.
However doing type(test().x) returns int rather than property indicating that what test().x returns is _x and not the property x. Indeed, trying to do access test().x.setter returns a AttributeError: 'int' object has no attribute 'setter'.
I understand that 99.9% of the time, when someone does test().x he wants to access the value associated with the property x. This is exactly what properties are meant for.
However, how can I do in that 0.01% of the times when I want to access the property object rather than the value returned by the getter?
x is a class attribute, whose __get__ method receives a reference to the object when invoked on an instance of the class. You need to get a reference to the class first, then you can get the actual property object without invoking the getter.
>>> t = test()
>>> t.x
10
>>> type(t).x
<property object at ....>
I'm trying to validate one attribute of my class using setter in the code below. The attribute I want to validate is called '__x' and is set to parameter passed on 'init' method. When I change 'self__x' to 'self.x', it's working as I expect. What I want to is how it's working with 'self.x', while I don't return 'x' attribute anywhere in getter and setter methods and why it's not working with 'self.__x'?
class P:
def __init__(self, x):
self.__x = x # not working
# self.x = x # working
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
else:
self.__x = x
p = P(-5)
print(p.x) # prints -5
It's like this. Imagine there's a school bully, let's call him Dan, who targets you. There's also Beth, who you like very much. Normally, you want to avoid Dan and meet Beth, but Dan doesn't care and will bop you on the head if he sees you.
Now you also make friends with Joe. He's a gentle giant. Very nice guy. He says to come to his place and he'll make sure he doesn't let Dan in. It all works great: when Dan comes to Joe's door, he's turned away; when Beth comes, Joe lets her in.
The key point is this: it only works as long as Dan is opening the door. If you hear the doorbell and you go out yourself, it doesn't work any more.
So here, if you do self.x = -5, Joe checks the number, sees it's Dan, and sends him packing with a zero. But if you do self.__x = -5, Joe never sees Dan. You get a bop on the head.
self.__x is just a variable, it can't do any checking on its own. self.x is a function though (two of them really, one for reading and one for writing), and it can do whatever it wants - set self.__x or refuse to.
Let's begin with the "#decorator" syntax. It's actually only syntactic sugar, so
#decorate
def myfunc():
pass
is just a shorthand for
def myfunc():
pass
myfunc = decorate(myfunc)
Note that python functions are objects too (as well as classes and modules FWIW) so you can pass functions as arguments to other functions, return functions from functions, store functions as variables or attributes etc.
Now with the property class (yes, it's a class): it's only a generic implementation of the descriptor protocol, which is the python mechanism to support computed attributes.
A naive python implementation of property would mostly look something like (I ignore the fdel and __del__ parts):
class propertytype(type):
# this is what will allow you
# to use `property` as decorator,
# it will return a new `property` instance
# with `func` as setter
def __call__(cls, func):
return cls(func)
class property(metaclass=propertytype):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
# this is the getter
def __get__(self, instance, cls=None):
if instance is None:
return self
return self.fget(instance)
# this is the setter (if there's one)
def __set__(self, instance, value):
if not self.fset:
raise AttributeError("Attribute is read-only")
self.fset(instance, value)
# and this allows you to use`#myprop.setter`
# in your class definition
def setter(self, func):
self.fset = func
return self
And finally: while it's good practice to create all instance attributes of an object in the initializer (the __init__ method), you can actually set existing or new attributes just wherever and whenever you want. Except for a few types that (mainly for implementation reasons) use a totally different way to store attributes (you can look for slots if you want to learn more about this), ordinary Python objects are, mainly, dicts in disguise, so myobj.foo = 'bar' will usually just store 'bar' in self.__dict__['foo']. Well, if you don't use computed attributes, of course ;)
Ok, now we have the building blocks, let analyze what's going on with your class:
class P:
# let's ignore the initializer for now
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
else:
self.__x = x
This could be rewritten as
class P:
# let's ignore the initializer for now
def _getx(self):
return self.__x
def _setx(self):
if x < 0:
self.__x = 0
else:
self.__x = x
x = property(_getx, setx)
So now with
p = P()
when we do:
p.x = 5
the attribute resolution rules (implemented in object.__setattr__(self, name, value)) will actually lookup "x" on "P", find our "x" property, and since it's a binding descriptor (it has a __set__ method), call x.__set__(p, 5), which in turn will call self.fset(p, 5) (cf property.__set__() definition), which will call p._setx(5).
And if we had back the initializer:
class P:
def __init__(self, x):
self.x = x
# getter / setter / property definition here
then the very exact thing happens (except the P instance is named self instead of p ) - it actually ends up calling P._setx(self, x).
The only difference with your original implementation is that using the property has a decorator, the getter and setter functions do not become methods of the class, they only live as the fget and fset attributes of the x property object.
class P:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
I am trying to learn 'getters' and 'setters' methods.
When I create an instance with these inputs:
p1 = P(1001)
print(p1.x)
p1.x = -12
print(p1.x)
I get as outputs:
1001
-12
I don't know why do I get 1001 and -12 instead of 1000 and 0.
Your real problem is that you're using Python 2.x with old-style classes. Your code as shown will work as you expect on Python 3.x (except for the broken indentation, of course). It will also work on Python 2.x if you make P a new-style class by explicitly inheriting from object (e.g. declare it as class P(object):).
In an old-style class, the problem is that your setter and getter methods are both named x, and both are competing for entries in the same namespace.
When you have code like:
#x.setter
def x(self, x):
...
What happens is that a new function object (confusingly, named x) is created with the code you specify, then x.setter(x_function_object) is called, and whatever x.setter returns (which will be a descriptor object with __get__ and __set__ defined) is assigned to x in the namespace dictionary that is passed to the metaclass's __new__ method and used to build the class's type.
However, in an old-style class, when you write self.x = ... then it will invoke __setattr__ directly rather than using the descriptor protocol (self.__dict__['x'].__set__(x, ...)). Thus the assignment to self.x in your __init__ method will overwrite the setter method instead of calling it.
In a process of coding I have been faced with the need to change behavior of object's property (but NOT a class property). And I found that already initialized object cannot be patched with descriptor. So why?
code examples
class A(object):
pass
class D(object):
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
pass
def __get__(self, obj, objtype=None):
return 5
A.x = D()
A.x
Out[12]: 5 # work!
a = A()
a.y = D()
a.y
Out[14]: <__main__.D at 0x13a8d90> # not work!
From the documentation.
For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b)).
That is, the attribute lookup on an instance (b) is converted into a descriptor call on the class (type(b)). Descriptors operate at the class level.
As for why this is true, it's because descriptors are basically a way to do method-like work (i.e., call a method) on attribute lookup. And methods are essentially class-level behavior: you generally define the methods you want on a class, and you don't add extra methods to individual instances. Doing descriptor lookup on an instance would be like defining a method on an instance.
Now, it is possible to assign new methods to instances, and it's also possible to get descriptors to work on instances of a particular class. You just have to do extra work. As the documentation quote above says, the machinery is in object.__getattribute__, so you can override it by defining a custom __getattribute__ on your class:
class Foo(object):
def __getattribute__(self, attr):
myDict = object.__getattribute__(self, '__dict__')
if attr in myDict and hasattr(myDict[attr], '__get__'):
return myDict[attr].__get__(self, type(self))
else:
return super(Foo, self).__getattribute__(attr)
class D(object):
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
pass
def __get__(self, obj, objtype=None):
return 5
And then:
>>> f = Foo()
>>> f.x = D()
>>> f.x
5
So if you feel the need to do this, you can make it happen. It's just not the default behavior, simply because it's not what descriptors were designed to do.
I've been successfully using Python properties, but I don't see how they could work. If I dereference a property outside of a class, I just get an object of type property:
#property
def hello(): return "Hello, world!"
hello # <property object at 0x9870a8>
But if I put a property in a class, the behavior is very different:
class Foo(object):
#property
def hello(self): return "Hello, world!"
Foo().hello # 'Hello, world!'
I've noticed that unbound Foo.hello is still the property object, so class instantiation must be doing the magic, but what magic is that?
As others have noted, they use a language feature called descriptors.
The reason that the actual property object is returned when you access it via a class Foo.hello lies in how the property implements the __get__(self, instance, owner) special method:
If a descriptor is accessed on an instance, then that instance is passed as the appropriate argument, and owner is the class of that instance.
When it is accessed through the class, then instance is None and only owner is passed. The property object recognizes this and returns self.
Besides the Descriptors howto, see also the documentation on Implementing Descriptors and Invoking Descriptors in the Language Guide.
In order for #properties to work properly the class needs to be a subclass of object.
when the class is not a subclass of object then the first time you try access the setter it actually makes a new attribute with the shorter name instead of accessing through the setter.
The following does not work correctly.
class C(): # <-- Notice that object is missing
def __init__(self):
self._x = None
#property
def x(self):
print 'getting value of x'
return self._x
#x.setter
def x(self, x):
print 'setting value of x'
self._x = x
>>> c = C()
>>> c.x = 1
>>> print c.x, c._x
1 0
The following will work correctly
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
print 'getting value of x'
return self._x
#x.setter
def x(self, x):
print 'setting value of x'
self._x = x
>>> c = C()
>>> c.x = 1
setting value of x
>>> print c.x, c._x
getting value of x
1 1
Properties are descriptors, and descriptors behave specially when member of a class instance. In short, if a is an instance of type A, and A.foo is a descriptor, then a.foo is equivalent to A.foo.__get__(a).
The property object just implements the descriptor protocol: http://docs.python.org/howto/descriptor.html