Can I get a reference to a Python property?
Question:
If I have this:
class foo(object):
@property
def bar(self):
return 0
f = foo()
How do I get a reference to f.bar without actually invoking the method, if this is even possible?
Edited to add: What I want to do is write a function that iterates over the members of f and does something with them (what is not important). Properties are tripping me up because merely naming them in getattr() invokes their __get__() method.
Answers:
get_dict_attr
(below) looks up attr
in a given object’s __dict__
, and returns the associated value if its there. If attr
is not a key in that __dict__
, the object’s MRO’s __dict__
s are searched. If the key is not found, an AttributeError
is raised.
def get_dict_attr(obj, attr):
for obj in [obj] + obj.__class__.mro():
if attr in obj.__dict__:
return obj.__dict__[attr]
raise AttributeError
For example,
class Foo(object):
x=1
def bar(self):
pass
@property
def baz(self):
return 0
foo=Foo()
print(get_dict_attr(foo,'x'))
# 1
print(get_dict_attr(foo,'bar'))
# <unbound method Foo.bar>
print(get_dict_attr(foo,'baz'))
# <property object at 0xb77c0dc4>
print(get_dict_attr(foo,'y'))
# AttributeError
Note that this is very different than the normal rules of attribute lookup.
For one thing, data-descriptors in obj.__class__.__dict__
(descriptors with both __get__
and __set__
methods) normally have precedence over values in obj.__dict__
. In get_dict_attr
, obj.__dict__
has precedence.
get_dict_attr
does not try calling __getattr__
.
Finally, get_dict_attr
will only work with objects obj
which are instances of new-style classes.
Nevertheless, I hope it is of some help.
class Foo(object):
@property
def bar(self):
return 0
f = Foo()
This references the property bar
:
print(Foo.bar)
# <property object at 0xb76d1d9c>
You see bar
is a key in Foo.__dict__
:
print(Foo.__dict__['bar'])
# <property object at 0xb775dbbc>
All properties are descriptors, which implies it has a __get__
method:
print(Foo.bar.__get__)
# <method-wrapper '__get__' of property object at 0xb76d7d74>
You can call the method by passing the object f
, and the class of f
as arguments:
print(Foo.bar.__get__(f,Foo))
# 0
I am fond of the following diagram. Vertical lines show the relationship between an object and the object’s class.
When you have this situation:
Foo B
| Foo.__dict__={'bar':b} | B.__dict__={'__get__':...}
| |
f `--------> b
f.bar
causes b.__get__(f,Foo)
to be called.
This is explained in detail here.
One thing you should know is that data descriptors (i.e., properties) only work when they are applied to (new-style) classes (see http://docs.python.org/reference/datamodel.html#implementing-descriptors). Copying them to an object will not create the property on that object. You need to copy them to a (new-style) class to take effect.
I’m going to say ignore the other answers because they are bad.
You can get a reference to the property simply by foo.bar
As for what you’re trying to do iterating over the members of f
, see below for specifics.
The long answer: what you have to understand are that methods in Python do not belong to instances, but are attributes of the class object. For example,
class Foo:
def bar(self):
pass
foo = Foo()
if you call foo.bar()
, Python first checks to see if foo
has bar
(it doesn’t), then it checks if Foo
has bar
, which it does. Python then “binds” foo.bar
to Foo.bar(foo)
, and calls that.
The same is true for properties. I don’t know the exact process (and I imagine it differs between implementations), but essentially Python looks for foo.bar
, then Foo.bar
, sees that it is a property, so evaluates its getter
and returns that.
In your case,
class Foo(object):
@property
def bar(self):
return 0
f = Foo()
You can access the property through the class Foo. These are all equivalent:
Foo.bar
Foo.__dict__['bar']
f.__class__.__dict__['bar']
=> <property at 0x4920c28>
As you can see, this is a property
instance. On a property you have a method called fget
which returns the ‘getter’ value.
isinstance(Foo.bar, property)
=> True
Foo.bar.fget
=> <function Foo.bar at 0x0000014DC0CF21F0>
You can call this function (an instance method) directly.
Foo.bar.fget(f)
=> 0
Note: you have to supply the object f
(the self
argument) to fget
yourself, because when accessed through the class, the class does not know the instance f
yet. In other words, instance method Foo.bar.fget
is not yet ‘bound’ to f
.
I ran into a similar situation to this and none of the other answers helped me, so here’s an alternate approach.
My situation was slightly different, as instead of just having a @property
, I was mixing in a custom decorator that adds attributes to the wrapped method. So eg normally I’d have something like this:
class Foo:
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
My @my_decorator
would then give me access to self.bar.my_attribute
that had some data I need to access sometimes. This works great for functions and methods, but I hit a snag when trying to mix it with @property
because the property hides the reference to the method (self.bar.my_attribute
fails because self.bar
returns the return value of the method, which doesn’t have the my_attribute
that I want).
I’d use @property
as the outer decorator like so:
class Foo:
@property
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
And then the solution is to access my_attribute
as Foo.bar.fget.my_attribute
(the important detail here is that accessing the property from the class returns the property object, whereas accessing the property from an instance just calls the method and returns the value, without access to the reference to the original method).
TL;DR: If you need a reference to the method that provides your @property
, you need to use YourClassName.your_property.fget
.
Short answer:
Properties return its self when they called from class: MyClass.my_prop
Also, they have fields that contain a link to the actual methods: fget
, fset
and fdel
.
Description:
So, my_class.my_prop
(where my_class = MyClass()
) returns the value, but MyClass.my_prop
returns the property object and MyClass.my_prop.fget
returns the getter method of this property. The self
is not linked to it, so it should be populated during the call: MyClass.my_prop.fget(my_class)
Example:
class MyClass:
my_prop = property(lambda self: 'get', lambda self, x: print('set', x))
setter = MyClass.my_prop.fset
getter = MyClass.my_prop.fget
my_class = MyClass()
setter(my_class, 5) # equals my_class.my_prop = 5
print(getter(my_class)) # equals print(my_class.my_prop)
Why not just lambdify it?
bar_getter = lambda: f.bar
Done.
If I have this:
class foo(object):
@property
def bar(self):
return 0
f = foo()
How do I get a reference to f.bar without actually invoking the method, if this is even possible?
Edited to add: What I want to do is write a function that iterates over the members of f and does something with them (what is not important). Properties are tripping me up because merely naming them in getattr() invokes their __get__() method.
get_dict_attr
(below) looks up attr
in a given object’s __dict__
, and returns the associated value if its there. If attr
is not a key in that __dict__
, the object’s MRO’s __dict__
s are searched. If the key is not found, an AttributeError
is raised.
def get_dict_attr(obj, attr):
for obj in [obj] + obj.__class__.mro():
if attr in obj.__dict__:
return obj.__dict__[attr]
raise AttributeError
For example,
class Foo(object):
x=1
def bar(self):
pass
@property
def baz(self):
return 0
foo=Foo()
print(get_dict_attr(foo,'x'))
# 1
print(get_dict_attr(foo,'bar'))
# <unbound method Foo.bar>
print(get_dict_attr(foo,'baz'))
# <property object at 0xb77c0dc4>
print(get_dict_attr(foo,'y'))
# AttributeError
Note that this is very different than the normal rules of attribute lookup.
For one thing, data-descriptors in obj.__class__.__dict__
(descriptors with both __get__
and __set__
methods) normally have precedence over values in obj.__dict__
. In get_dict_attr
, obj.__dict__
has precedence.
get_dict_attr
does not try calling __getattr__
.
Finally, get_dict_attr
will only work with objects obj
which are instances of new-style classes.
Nevertheless, I hope it is of some help.
class Foo(object):
@property
def bar(self):
return 0
f = Foo()
This references the property bar
:
print(Foo.bar)
# <property object at 0xb76d1d9c>
You see bar
is a key in Foo.__dict__
:
print(Foo.__dict__['bar'])
# <property object at 0xb775dbbc>
All properties are descriptors, which implies it has a __get__
method:
print(Foo.bar.__get__)
# <method-wrapper '__get__' of property object at 0xb76d7d74>
You can call the method by passing the object f
, and the class of f
as arguments:
print(Foo.bar.__get__(f,Foo))
# 0
I am fond of the following diagram. Vertical lines show the relationship between an object and the object’s class.
When you have this situation:
Foo B
| Foo.__dict__={'bar':b} | B.__dict__={'__get__':...}
| |
f `--------> b
f.bar
causes b.__get__(f,Foo)
to be called.
This is explained in detail here.
One thing you should know is that data descriptors (i.e., properties) only work when they are applied to (new-style) classes (see http://docs.python.org/reference/datamodel.html#implementing-descriptors). Copying them to an object will not create the property on that object. You need to copy them to a (new-style) class to take effect.
I’m going to say ignore the other answers because they are bad.
You can get a reference to the property simply by foo.bar
As for what you’re trying to do iterating over the members of f
, see below for specifics.
The long answer: what you have to understand are that methods in Python do not belong to instances, but are attributes of the class object. For example,
class Foo:
def bar(self):
pass
foo = Foo()
if you call foo.bar()
, Python first checks to see if foo
has bar
(it doesn’t), then it checks if Foo
has bar
, which it does. Python then “binds” foo.bar
to Foo.bar(foo)
, and calls that.
The same is true for properties. I don’t know the exact process (and I imagine it differs between implementations), but essentially Python looks for foo.bar
, then Foo.bar
, sees that it is a property, so evaluates its getter
and returns that.
In your case,
class Foo(object):
@property
def bar(self):
return 0
f = Foo()
You can access the property through the class Foo. These are all equivalent:
Foo.bar
Foo.__dict__['bar']
f.__class__.__dict__['bar']
=> <property at 0x4920c28>
As you can see, this is a property
instance. On a property you have a method called fget
which returns the ‘getter’ value.
isinstance(Foo.bar, property)
=> True
Foo.bar.fget
=> <function Foo.bar at 0x0000014DC0CF21F0>
You can call this function (an instance method) directly.
Foo.bar.fget(f)
=> 0
Note: you have to supply the object f
(the self
argument) to fget
yourself, because when accessed through the class, the class does not know the instance f
yet. In other words, instance method Foo.bar.fget
is not yet ‘bound’ to f
.
I ran into a similar situation to this and none of the other answers helped me, so here’s an alternate approach.
My situation was slightly different, as instead of just having a @property
, I was mixing in a custom decorator that adds attributes to the wrapped method. So eg normally I’d have something like this:
class Foo:
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
My @my_decorator
would then give me access to self.bar.my_attribute
that had some data I need to access sometimes. This works great for functions and methods, but I hit a snag when trying to mix it with @property
because the property hides the reference to the method (self.bar.my_attribute
fails because self.bar
returns the return value of the method, which doesn’t have the my_attribute
that I want).
I’d use @property
as the outer decorator like so:
class Foo:
@property
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
And then the solution is to access my_attribute
as Foo.bar.fget.my_attribute
(the important detail here is that accessing the property from the class returns the property object, whereas accessing the property from an instance just calls the method and returns the value, without access to the reference to the original method).
TL;DR: If you need a reference to the method that provides your @property
, you need to use YourClassName.your_property.fget
.
Short answer:
Properties return its self when they called from class: MyClass.my_prop
Also, they have fields that contain a link to the actual methods: fget
, fset
and fdel
.
Description:
So, my_class.my_prop
(where my_class = MyClass()
) returns the value, but MyClass.my_prop
returns the property object and MyClass.my_prop.fget
returns the getter method of this property. The self
is not linked to it, so it should be populated during the call: MyClass.my_prop.fget(my_class)
Example:
class MyClass:
my_prop = property(lambda self: 'get', lambda self, x: print('set', x))
setter = MyClass.my_prop.fset
getter = MyClass.my_prop.fget
my_class = MyClass()
setter(my_class, 5) # equals my_class.my_prop = 5
print(getter(my_class)) # equals print(my_class.my_prop)
Why not just lambdify it?
bar_getter = lambda: f.bar
Done.