Access property object (for replacing setter method) - python

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 ....>

Related

Python class attribute validation on __init__

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.

python - Extract attributes having setter properties in an object

I have an object. Using __dict__, I can list down all the attributes of the object. Using __dir__, I can list down all the methods and attributes of the object. Is there a way to list down the attributes having setter properties declared in the class whose instance the object is.
Use:
isinstance(getattr(type(object), 'attribute'), property)
Thanks to #a_guest and OP Rahul Nahata's own prior edit to the question.
To check if an object is a property, use isinstance(value, property).
To list all the attributes and methods of a class, use: vars(MyClass).
Combine everything and get:
all_property_names = [name for name, value in vars(MyClass).items() if isinstance(value, property)]
With the following class,
class MyClass():
def __init__(self):
self._x = 1
#property
def x(self):
return self._x
def notproperty(self):
return self.x
you would get:
# all_property_names
['x']

Python Getters, Setters

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.

Can you change a python descriptor's __get__ method at runtime?

All,
As the title asks, is it possible to change the __get__ method of a Descriptor at run time. I'm in a situation where I've got a function that is being decorated and undecorated on the the fly at run time. I'd like the result of this function to be available as a attribute, similar to what the #property does. I researched that and found it's a descriptor, but it seems descriptors' __get__ method is read only.
class Test( object ):
def __init__( self ):
self._x = 10
def get_x( self ):
return self._x
#property
def x( self ):
return self.get_x()
The above code does what I want, roughly, in that
The value is set in the constructor
I can decorate the get_x method to my heart's content
instance.x returns the correct value
My issue is that I'd rather not have to create the get_x method since it's basically unnecessary. I just haven't been able to decorate the __get__ method of x as it is read-only.
Background
I'm writing a turn based strategy game, and I'm using decorators to implement persistent conditions. I'm able to implement these decorators effectively when I use test cases, but the issue is that to get the computed value then, you must use a function call, not an attribute access. This seems like an bad idea because getting values describing a unit would inconsistently use functions or attributes. I'd like to standardize on attributes if I can.
You can override default "read-only" characteristic of property's __get__ attribute using simple inheritance :
class MyProperty( property ): pass
class Test( object ):
def __init__( self ):
self._x = 10
def get_x( self ):
return self._x
#MyProperty
def x( self ):
return self.get_x()
test = Test()
The problem now that even if you redefine __get__ attribure of your Text.x property, on test.x request python runtime will call MyProperty.__get__(Test.x, test, Test)
So you could rewrite it only there like
MyProperty.__get__ = lambda self,instance,owner: ""the x attribute"
So good option here is to delegate call to some redifineable attribute like
MyProperty.__get__ = lambda self,instance,owner: self.get(instance,owner)
From now on get attribute of your property in your full control.
Also there is bad option to generate separate type for each property-like object.
So in good case you could do something like:
class MyProperty( property ):
def __get__(self,instance,owner) :
if not instance: return self
else: return self.get(instance,owner)
class Test( object ):
def __init__( self ):
self._x = 10
def get_x( self ):
return self._x
#MyProperty
def x( self ): pass
#MyProperty
def y(self): pass
x.get = lambda self,clazz: self.get_x()
y.get = lambda self,clazz: "the y property of " + clazz.__name__ + " object"
>>> test = Test()
>>> test.x
10
>>> test.y
'the y property of Test object'

How do Python properties work?

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

Categories

Resources