Why property works only for objects?

Michal Kwiatkowski ruby at no.spam
Fri Mar 10 20:54:40 EST 2006


Bruno Desthuilliers napisał(a):
>> Let me understand it clearly. If I change __class__ of an object,
>> existing attributes (so methods as well) of an object are still
>> accessible the same way and don't change its values. Only resolution of
>> attributes/methods not found in object is changed, as it uses new
>> version of __class__ to lookup names. Is this right?
> 
> Attributes, yes. Not methods. Methods are looked up in the class.

My experience shows exactly the opposite. Any attribute/method you try
to access is first looked up in object dictionary, then inside class
definition.

    import types

    class C(object):
        def f(self):
            print "old method f()"

    obj = C()

    def f(self):
        print "new method f()"

    obj.f = types.MethodType(f, C)

    obj.f() # => "new method f()"

Since that works, intuitively for me would be to assign object's
descriptors like that:

    obj.x = property(types.MethodType(lambda self: 42, C))

But I just get a property object. So, it seems descriptors have little
bit of magic, as they don't work identically for classes and objects.

The same goes for special methods and attributes (written as __*__). So
I cannot change __getattr__/__setattr__/__metaclass__ or any other
attribute that starts with __ for a single object. It's not so bad
except for situations were class of an object defines its own
__getattribute__ method, which takes control of an object from us. To
get/set any attribute of an object we must use object type methods:

    class C(object):
        def __getattribute__(self, name):
            return 42

    obj = C()

    obj.a = 5

    print obj.a # => 42
    print object.__getattribute__(obj, 'a') # => 5

I gets even more strange when you try to modify, say __len__ or
__repr__. Docstring for object.__repr__ says:
"x.__repr__() <==> repr(x)" which doesn't seem to be always true:

    class C(object):
        def __repr__(self):
            return "class repr"

    obj = C()
    obj.__repr__ = types.MethodType(lambda self: "instance repr", C)

    print repr(obj) # => class repr
    print obj.__repr__() # => instance repr

Maybe the manual should say "x.__class__.__repr__() <==> repr(x)" instead?

I'm trying to understand attributes lookups made by Python, having
properties and special methods in mind. So far I've come up with kind of
 reasoning I've coded below. I would appreciate any comments and
suggestions.

def lookup_name(obj, name):
    get = lambda obj, name: object.__getattribute__(obj, name)
    has = lambda obj, name: name in get(obj, '__dict__')

    # assume C is a new style class
    C = get(obj, '__class__')

    # 1) use class' __getattribute__ method
    try:
        if has(C, '__getattribute__'):
            return get(C, '__getattribute__')(obj, name)
    except AttributeError: pass

    # 2) lookup in object's dictionary
    try:
        if has(obj, name):
            return get(obj, name)
    except AttributeError: pass

    # 3) lookup in classes
    for c in obj.__class__.mro():
        try:
            if has(c, name):
                desc = get(c, name)
                # 3a) handle descriptors
                try:
                    return get(desc, '__get__')(obj)
                except: pass
                # 3b) no descriptors -> use value
                return desc
        except AttributeError: pass

    raise AttributeError, "Not found!"

mk
-- 
 . o .       >>  http://joker.linuxstuff.pl  <<
 . . o   It's easier to get forgiveness for being wrong
 o o o   than forgiveness for being right.



More information about the Python-list mailing list