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