name lookup failure using metaclasses with unittests
Ulrich Eckhardt
ulrich.eckhardt at dominolaser.com
Fri Apr 12 03:17:39 EDT 2013
Am 11.04.2013 10:19, schrieb Steven D'Aprano:
> if sys.version >= '3':
Use sys.version_info >= (3,), otherwise your code breaks when upgrading
to Python 10 and greater. ;^)
>> The second question that came up was if there is a way to keep a
>> metaclass defined inside the class or if the only way is to provide it
>> externally. [...]
>
> Not in general, since the metaclass has to exist independently of the
> class.
Thanks for your explanations, they are appreciated.
> The class is an instance of your metaclass. That means that the
> metaclass must exist first, so it can be instantiated when you
> define the class.
I don't like the approach to define the code to post-process a class
before defining the class. It's a bit like top-posting, it messes up the
reading order. Since I really intend to post-process the class, it seems
that metaclasses are simply not the right tool.
At the moment, this leaves me with two options:
1. post-process the class
class X:
pass
# attach constants to clas X
for i in (1, 2, 3):
setattr(X, 'f{}' % i, i)
2. generate code inline
class Y: pass
# generate constants in local (class-)namespace
for i in (1, 2, 3):
locals()['f{}' % i] = i
In both cases, variables (loop variable 'i') are leaked into the
surrounding namespace, which is kind-of ugly. The second approach also
seems a bit hackish and I can't use the class-in-definition there, which
is limiting when you want to attach e.g. constants of type X to X.
>> Also PEP 3115 "Metaclasses in Python 3000"[2] seems to
>> consider postprocessing of a class definition as better handled by a
>> class decorator, which is something I haven't looked at yet.
>
> Generally, class decorators are less brain-melting than metaclasses.
Alas, they also need to be defined before the class, messing with the
mentioned order of declaration. They can be used to call a class
function though which then does the necessary postprocessing...
3. post-process the class triggered with decorator
def postprocess_class(cls):
"""invoke postprocess() on the decorated object"""
cls.postprocess()
del cls.postprocess
return cls
@postprocess_class
class Z:
@classfunction
def postprocess(cls):
# attach constants to class
for i in (1, 2, 3):
setattr(cls, 'f{}' % i, i)
I guess I'll stay with variant 1 for now, since it requires the least
amount of code and the least amount of questions from other developers here.
Thanks everybody!
Uli
More information about the Python-list
mailing list