В чем преимущество использования `exec` перед` type () `при создании классов во время выполнения? - PullRequest
10 голосов
/ 06 октября 2011

Я хочу динамически создавать классы во время выполнения в 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() класс Python реализован (см. эту строку ).Также очень важна защита этого использования exec здесь , создателем класса (Раймондом Хеттингером).В этой защите утверждается, что « Для именованных кортежей ключевым признаком является то, что они в точности эквивалентны рукописному классу », что может означать, что использование type()не так хорошо, как использование exec ...

Есть ли разница?Зачем использовать exec против type()?

Я ожидаю, что ответ может заключаться в том, что оба пути одинаковы, и просто в реализации namedtuple есть много переменных namedtuple, перенесенных через негои выполнение этого с динамически генерирующими замыканиями для всех методов сделало код громоздким, но я хочу знать, есть ли что-то еще в этом.

Что касается моего дискомфорта с exec, я признаю, что если естьдля ненадежных сторон нет способа внедрить в него гнусный код, это должно быть хорошо ... это только то, что заставляет меня нервничать.

Ответы [ 3 ]

7 голосов
/ 06 октября 2011

Я бы порекомендовал type более exec здесь.

Фактически, оператор class является просто синтаксическим сахаром для вызова type: тело класса выполняется в его собственном пространстве имен, которое затем передается метаклассу, который по умолчанию равен type, если нет указан пользовательский метакласс.

Этот подход менее подвержен ошибкам, поскольку нет необходимости анализировать код во время выполнения и может даже быть немного быстрее.

4 голосов
/ 06 октября 2011

Почему бы просто не создать класс в функции?

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

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

    Foo.__name__ = name
    return Foo
2 голосов
/ 06 октября 2011

Нет недостатка в использовании type () перед exec.Я думаю, что защита Раймонда немного оборонительна.Вы должны выбрать технику, которая вам наиболее понятна и понятна.Оба способа создают запутанный код.

Вы должны очень стараться избегать кода, который в первую очередь создает классы, что было бы лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...