Question about accessing class-attributes.
Bjorn Pettersen
BPettersen at NAREX.com
Tue Apr 29 13:21:58 EDT 2003
> From: Alex Martelli [mailto:aleax at aleax.it]
[...]
> > however I haven't been able to find a good description of attribute
> > lookup for new-style classes... (references?) E.g. why is
> > the metaclass' __getattr__ never called?
>
> Uh, what do you mean with this last question...?
>
> >>> class meta(type):
> ... def __getattr__(self, name):
> ... if len(name)==1: return name
> ... raise AttributeError, name
> ...
> >>> __metaclass__=meta
> >>> class X: pass
> ...
> >>> X.h
> 'h'
> >>>
Correct (although I must admit I didn't test regular attributes :-)
Here's the case I'm confused about:
>>> class meta(type):
... def __getattr__(cls, name):
... if name == '__len__':
... print "meta.__getattr__('__len__')"
... return lambda: 42
... else:
... print 'meta.__getattr__', name
... return name
...
>>> class S(object):
... __metaclass__ = meta
...
>>> S.__len__()
meta.__getattr__('__len__')
42
>>> len(S)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: len() of unsized object
>>>
I was told that special method "foo(x, arg)" was implemented as
"type(x).__foo__(x, arg)", which doesn't seem to be the case always...
Compare:
>>> class meta(type):
... def __len__(cls):
... return 42
...
>>> class S(object):
... __metaclass__ = meta
...
>>> S.__len__()
42
>>> len(S)
42
>>>
So, it looks like it's looking up __len__ in the metaclass, but not
falling back on __getattr__ when it isn't there? I've looked at the C
code and it seems like special methods each have their own way of
finding the function they're needing, e.g. for len, it looks like it
uses:
m = o->ob_type->tp_as_sequence;
if (m && m->sq_length)
return m->sq_length(o);
return PyMapping_Size(o);
which would never call the metaclass' __getattr__ (since tp_as_sequence
would presumably be NULL?).
The relevance this has to super, is that you couldn't do a (contrived)
example like:
for item in super(MyClass, self):
...
(in general, you can't pass super(MyClass, self) to a function expecting
a MyClass instance, even if MyClass defines no new behavior...)
Perhaps a more realistic exmaple?:
class Secure(object):
def __init__(self, name):
self.name = name
def __getattr__(self, attr):
return getSecureMethod(self.name, attr, getUserPrivileges())
class LogFoo(Secure):
def __init__(self):
Secure.__init__(self, 'SalaryDB')
def foo(self):
print 'calling foo'
return super(LogFoo, self).foo()
lf = LogFoo()
print lf.foo()
Any insight?
-- bjorn
More information about the Python-list
mailing list