Using metaclasses to inherit class variables
Steven Bethard
steven.bethard at gmail.com
Mon May 22 17:56:11 EDT 2006
telesphore4 at gmail.com wrote:
> Fresh copies of class vars so the first one is the correct: ('foo',
> 'bar', [], False)
Ahh, yeah, then you definitely need the copy.copy call.
>>>> import copy
>>>>
>>>> class ClassVars(type):
> ... def __init__(cls, name, bases, dict):
> ... for name, value in type(cls).classVars.iteritems():
> ... if name not in dict:
> ... setattr(cls, name, copy.copy(value))
> ...
>>>> def are(**kwargs):
> ... return type('', (ClassVars,), {'classVars':kwargs})
> ...
>>>> class C(object):
> ... __metaclass__ = are(name='foo', desc='bar', list=[])
> ... name = 'not foo' #<<< Changing a copy only owned by this class
> ...
>>>> class D(C):
> ... pass
> ...
>>>> C.name, C.desc, C.list
> ('not foo', 'bar', []) <<<<<<<<<<<<<<<<<<<<<< Separate copy we changed
>>>> D.name, D.desc, D.list, D.list is C.list
> ('foo', 'bar', [], False) <<<<<<<<<<<<<<<<<<<< Defaults are not changed
Hmm... I don't think I can get away with just a function for
__metaclass__ in this situation since D doesn't invoke it:
>>> class C(object):
... def __metaclass__(*args):
... print '__metaclass__%r' % (args,)
... return type(*args)
...
__metaclass__('C', (<type 'object'>,), {'__module__': '__main__',
'__metaclass__': <function __metaclass__ at 0x00FA89F0>})
>>> class D(C):
... pass
...
>>>
I'm not sure why this is. Anyone else out there know? D *does* invoke
__metaclass__ if it's a subclass of type:
>>> class C(object):
... class __metaclass__(type):
... def __init__(*args):
... print '__init__%r' % (args,)
...
__init__(<class '__main__.C'>, 'C', (<type 'object'>,), {'__module__':
'__main__', '__metaclass__': <class '__main__.__metaclass__'>})
>>> class D(C):
... pass
...
__init__(<class '__main__.D'>, 'D', (<class '__main__.C'>,),
{'__module__': '__main__'})
>>>
So it seems you pretty much have to go with the approach you're
currently using. It doesn't make any real difference, but I'd tend to
do it with a nested class statement instead of a call to type:
>>> def set_classvars(**kwargs):
... class __metaclass__(type):
... def __init__(cls, name, bases, classdict):
... for name, value in kwargs.iteritems():
... if name not in classdict:
... setattr(cls, name, copy.copy(value))
... return __metaclass__
...
>>> class C(object):
... __metaclass__ = set_classvars(name='foo', desc='bar', list=[])
... name = 'not foo'
...
>>> class D(C):
... pass
...
>>> C.name, C.desc, C.list
('not foo', 'bar', [])
>>> D.name, D.desc, D.list, D.list is C.list
('foo', 'bar', [], False)
> Thanks for your help btw.
No problem. This is much more fun than doing real work. ;-)
STeVe
More information about the Python-list
mailing list