В чем преимущество использования `exec` перед`type()`при создании классов во время выполнения?
Я хочу динамически создавать классы во время выполнения в 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
>>>
... но я хочу, чтобы классы 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'>)
Я также могу достичь этого с 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'>)
Использование exec
не устраивает меня (как я ожидаю, это не устраивает многих людей, которые читают этот вопрос), но exec
именно так питон collections.namedtuple()
класс реализован (см. эту строку). Также очень важна защита этого использования exec
здесь, создатель класса (Раймонд Хеттингер). В этой защите утверждается, что " для именованных кортежей ключевым моментом является то, что они в точности эквивалентны рукописному классу ", что может означать, что использование type()
не так хорошо, как использовать exec
...
Есть ли разница? Зачем использовать exec
против type()
?
Я ожидаю, что ответ может быть, что оба пути одинаковы, и это просто, что namedtuple
Реализация содержит множество переменных именованных кортежей, и благодаря этому динамически генерируются замыкания для всех методов, код становится громоздким, но я хочу знать, есть ли что-то еще для этого.
Что касается моего дискомфорта с exec
Я признаю, что, если нет никакой возможности для ненадежных сторон внедрить в нее гнусный код, это должно быть хорошо... это просто гарантирует, что это заставляет меня нервничать.
3 ответа
Нет недостатка в использовании type() перед exec. Я думаю, что защита Раймонда немного оборонительна. Вы должны выбрать технику, которая вам наиболее удобна и понятна. Оба способа создают запутанный код.
Вы должны очень стараться избегать кода, который в первую очередь создает классы, это было бы лучше всего.
Я бы посоветовал type
над exec
Вот.
На самом деле, class
заявление является просто синтаксическим сахаром для вызова type
: Тело класса выполняется в его собственном пространстве имен, которое затем передается метаклассу, который по умолчанию равен type
если пользовательский метакласс не указан.
Этот подход менее подвержен ошибкам, поскольку нет необходимости анализировать код во время выполнения и может даже быть немного быстрее.
Почему бы просто не создать класс в функции?
def foo_factory(index):
name = 'Foo%d' % index
class Foo(object):
ref_obj = RefObj(name)
Foo.__name__ = name
return Foo