Bruno Desthuilliers napisa³(a):
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