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.

Asked By: kindall

||

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.

Answered By: unutbu

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.

Answered By: Nathan Davis

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.

Answered By: darkfeline

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.

Answered By: florisla

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.

Answered By: robru

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)
Answered By: ADR

Why not just lambdify it?

bar_getter = lambda: f.bar

Done.

Answered By: Ryan Johnson
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.