views:

115

answers:

2

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.

+5  A: 

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.

unutbu
I suppose I could check each attribute of the instance to see if it's in the class, and if it's there and it's a property, grab the reference from the class instead of the instance. I was hoping there would be an easier way, though.
kindall
The matter is properties are created exactly to work as if they where values instead of function calls. And they do that in the most transparent way possible. Maybe you'd be better off using ordinar methods instead of properties on this case?
jsbueno
Your `get_dict_attr` is defined oddly. What you do in theory (except for the issues you mentioned, because you go through the mro yourself) is `try: return getattr(type(obj), attr); except AttributeError: return getattr(obj, attr)`
THC4k
@THC4k: My goal was to avoid using `getattr` entirely, since it invokes all the attribute lookup machinery that the OP is trying to avoid.
unutbu
@THC4k: Perhaps it's unlikely, but if `type(obj)` is a class whose metaclass has a data descriptor `attr`, then `getattr(type(obj),attr)` will call the property's `__get__` method, which I don't think the OP wants.
unutbu
A: 

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.

Nathan Davis
So basically this means I can't really do what I want. I guess that's all right then. :-)
kindall