在运行时创建类时,使用`exec`而不是`type()`有什么好处?

Rus*_*uss 10 python namedtuple dynamic-class-creation

我想在python中在运行时动态创建类.

例如,我想复制下面的代码:

>>> 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
>>>
Run Code Online (Sandbox Code Playgroud)

...但我希望动态创建Foo1,Foo2,Foo类(即:在执行期间而不是在第一次通过编译时).

实现这一目标的一种方法是type(),像这样:

>>> 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'>)
Run Code Online (Sandbox Code Playgroud)

我也可以这样实现exec:

>>> 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'>)
Run Code Online (Sandbox Code Playgroud)

采用exec不跟我坐好(因为我希望它不会有很多人谁读了这个问题),但exec就是究竟如何Python的collections.namedtuple()中实现(见本线).同样非常相关的是由这个类的创造者(Raymond Hettinger)来保护exec 这里的使用.在这种辩护中,声明" 命名元组的一个关键特性是它们完全等同于手写类 ",人们可能会暗示使用type()它不如使用exec......

有区别吗?为何使用execvs type()

我希望答案可能是两种方式都是相同的,只是namedtuple实现中有很多名称变量通过它来实现,并且通过动态生成所有方法的闭包来实现这一点使得代码变得笨拙,但我想知道如果还有更多的东西.

关于我的不适exec,我确实认识到,如果没有任何方法让不信任的人注入邪恶的代码,它应该没问题......这只是确保这让我感到紧张.

Fer*_*yer 7

我会typeexec这里推荐.

实际上,该class语句只是对于调用的语法糖type:类主体在其自己的命名空间内执行,然后将其传递给元类,type如果未指定自定义元类,则默认为该类.

这种方法不那么错误,因为不需要在运行时解析代码,甚至可能更快一些.


Ned*_*der 2

使用 type() 相对于 exec 没有任何缺点。我认为雷蒙德的防守有点防守。您必须选择您认为最可读和最容易理解的技术。这两种方法都会产生令人困惑的代码。

您应该非常努力地避免首先创建类的代码,那是最好的。

  • “decorator”库的“exec”的论点之一是更容易保留装饰函数/方法的签名。我认为这也是“namedtuple”的一个论点,即使用“exec”,类一旦构建就与手工编码时完全相同,而使用“type”,它们就不同了。 (3认同)