Generating modul classes with eval

Peter Otten __peter__ at web.de
Thu Feb 3 07:18:30 EST 2005


Axel Straschil wrote:

> class_dic = {}
> class_dic['Br'] = _Tag
> class_dic['Hr'] = _Tag
> class_dic['Html'] = _ContainerTag
> class_dic['Table'] = _ContainerTag
> 
> for class_name, class_base in class_dic.items():
>     class TmpClass(class_base):
>         def __init__(self, **props):
>             name = class_name.lower()
>             #super(TmpClass, self).__init__(name=name, **props)
>             class_base.__init__(self, name=name, **props)
>     setattr(_module_name, class_name, TmpClass)

While your workaround doesn't balk immediately, it doesn't do the right
thing either.

After the loop has finished, the global variable TmpClass will be bound to
whatever class was created last, and the variable class_base will be bound
to that the base class of that same TmpClass. Therefore only this last class
is guaranteed to work as expected.

A simplified example to demonstrate the binding problem:

>>> classes = []
>>> for text in ["alpha", "beta"]:
...     class T:
...             def __init__(self): print text
...     classes.append(T)
...
>>> classes[0] is classes[1]
False # two distinct classes, as expected
>>> classes[0]()
beta
<__main__.T instance at 0x402a9e2c>
>>> classes[1]()
beta
<__main__.T instance at 0x402a9f8c>

And now the proof that you are actually accessing the global variable: 

>>> text = "gamma"
>>> classes[0]()
gamma
<__main__.T instance at 0x402a9f8c>

One way to fix this is to introduce a factory function:

>>> def make_class(text):
...     class T:
...             def __init__(self): print text
...     return T
...
>>> classes = []
>>> for text in ["alpha", "beta"]:
...     classes.append(make_class(text))
...
>>> classes[0]()
alpha
<__main__.T instance at 0x402a9e4c>
>>> classes[1]()
beta
<__main__.T instance at 0x402a9f8c>
>>>


Peter




More information about the Python-list mailing list