What is the advantage in using `exec` over `type()` when creating classes at runtime?

不羁的心 提交于 2021-02-17 19:31:12

问题


I want to dynamically create classes at runtime in python.

For example, I want to replicate the code below:

>>> class RefObj(object):
...     def __init__(self, ParentClassName):
...         print "Created RefObj with ties to %s" % ParentClassName
... class Foo1(object):
...     ref_obj = RefObj("Foo1")
... class Foo2(object):
...     ref_obj = RefObj("Foo2")
... 
Created RefObj with ties to Foo1
Created RefObj with ties to Foo2
>>>

... but I want the Foo1, Foo2, Foo classes to be created dynamically (ie: during execution instead of on first-pass compile).

One way to achieve this is with type(), like so:

>>> class RefObj(object):
...     def __init__(self, ParentClassName):
...         print "Created RefObj with ties to %s" % ParentClassName
... def make_foo_class(index):
...     name = "Foo%s" % index
...     return type(name, (object, ), dict(ref_obj = RefObj(name)))
... 
>>> Foo1 = make_foo_class(1)
Created RefObj with ties to Foo1
>>> Foo2 = make_foo_class(2)
Created RefObj with ties to Foo2
>>> type(Foo1()), type(Foo2())
(<class 'Foo1'>, <class 'Foo2'>)

I can also achieve it with exec, like so:

>>> class RefObj(object):
...     def __init__(self, ParentClassName):
...         print "Created RefObj with ties to %s" % ParentClassName
... def make_foo_object(index):
...     class_template = """class Foo%(index)d(object):
...         ref_obj = RefObj("Foo%(index)d")
...         """ % dict(index = index)
...     global RefObj
...     namespace = dict(RefObj = RefObj)
...     exec class_template in namespace
...     return namespace["Foo%d" % index]
... 
>>> Foo1 = make_foo_object(1)
Created RefObj with ties to Foo1
>>> Foo2 = make_foo_object(2)
Created RefObj with ties to Foo2
>>> type(Foo1()), type(Foo2())
(<class 'Foo1'>, <class 'Foo2'>)

The use of exec doesn't sit well with me (as I expect it doesn't with a lot of people who read this question) but exec is exactly how python's collections.namedtuple() class is implemented (see this line). Also very relevant is the defense of this use of exec here, by the creator of the class (Raymond Hettinger). In this defense, it is stated that "It is a key feature for named tuples that they are exactly equivalent to a hand-written class", which one might take to imply that the use of type() is not as good as using exec...

Is there a difference? Why use exec vs type()?

I expect the answer may be that both ways are the same and it is simply that the namedtuple implementation has a lot of namedtuple variables peppered through it, and doing this with dynamically generate closures for all methods made the code get unwieldy, but I want to know if there is something more to this.

Regarding my discomfort with exec, I do recognize that if there is no way whatsoever for untrusted parties to inject nefarious code into it, it should be fine... it is just ensuring that that makes me nervous.


回答1:


There is no disadvantage to using type() over exec. I think Raymond's defense is a bit defensive. You have to choose the technique that you find most readable and understandable. Both ways create confusing code.

You should try really hard to avoid code that creates classes in the first place, that would be best.




回答2:


I would recommend type over exec here.

In fact, the class statement is just syntactic sugar for a call to type: The class body is executed within its own namespace, which is then passed on to the metaclass, which defaults to type if no custom metaclass is specified.

This approach is less errorprone since there is no need to parse code at runtime, and might even be a bit faster.




回答3:


Why not just create a class in a function?

def foo_factory(index):
    name = 'Foo%d' % index

    class Foo(object):
        ref_obj = RefObj(name)

    Foo.__name__ = name
    return Foo


来源:https://stackoverflow.com/questions/7676947/what-is-the-advantage-in-using-exec-over-type-when-creating-classes-at-run

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!