2011-10-06 12 views
10

Chcę dynamicznie tworzyć klasy w czasie wykonywania w Pythonie.Jaka jest korzyść z używania `exec` ponad` type() `podczas tworzenia klas w środowisku wykonawczym?

Na przykład, chcę replikować poniższy kod:

>>> 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 
>>> 

... ale chcę klasy foo1, foo2, Foo być tworzone dynamicznie (tj: w trakcie realizacji, a nie na pierwszym-pass skompilować).

Jednym ze sposobów osiągnięcia tego celu jest z type(), tak:

>>> 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'>) 

Mogę również osiągnąć go exec, tak:

>>> 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'>) 

Zastosowanie exec nie współgra ze mną (jak się spodziewam, nie z wieloma ludźmi, którzy czytają to pytanie), ale exec jest dokładnie jak python's collections.namedtuple() klasa is implemented (patrz this line). Bardzo istotna jest również obrona tego użycia przez autora klasy (Raymonda Hettingera). W tej obronie jest powiedziane, że "Jest to kluczowa cecha dla nazwanych krotek, które są dokładnie równoważne z ręcznie pisaną klasą", co może oznaczać, że użycie type() nie jest tak dobre, jak użycie exec ...

Czy jest jakaś różnica? Dlaczego warto korzystać z exec kontra type()?

Spodziewam się, że odpowiedź może być tak, że obie strony są takie same i jest to po prostu, że realizacja namedtuple ma wiele zmiennych namedtuple pieprzem przez niego, i robi to z dynamicznego generowania zamknięć dla wszystkich metod wykonany kod dostać nieporęczny , ale chcę wiedzieć, czy jest w tym coś więcej.

Odnośnie mojego dyskomfortu z exec, zdaję sobie sprawę, że jeśli nie ma sposobu, aby niezaufane strony wprowadziły do ​​niego nikczemny kod, powinno być dobrze ... to tylko upewnia mnie, że denerwuję się.

+1

Istnieje również dobra dyskusja na ten temat na stronie http://blog.ccpgames.com/kristjan/2011/05/28/namedtuple-and-exec/ oraz pojawił się kolejny dobry wpis na blogu na ten temat. Nie widzę teraz. Nie przedstawiłeś żadnego powodu, dla którego 'typ' jest problematyczny w twojej sytuacji, więc nie wiem, dlaczego zawracałeś sobie głowę' exec', ponieważ wiesz, że 'type' działa. (Inne niż oczywiście ciekawość). – agf

+0

@agf - świetny link, dzięki! Początkowo nie miałem problemu z 'type', ponieważ oba podejścia działają. Byłem po prostu ciekawy różnic i próbowałem zrozumieć powód używania 'exec' w' namedtuple'. Przedstawione argumenty sygnatur klasy/funkcji są znakomite, chociaż ... Często mam problemy z dekoratorem, które wymagają użycia pakietu [dekorator] (http://pypi.python.org/pypi/decorator). – Russ

+0

Czy możesz podać powód potrzeby dynamicznego tworzenia klasy, czy jest to tylko ciekawość? – dhill

Odpowiedz

2

Nie ma wad polegających na użyciu type() over exec. Uważam, że obrona Raymonda jest nieco defensywna. Musisz wybrać technikę najbardziej czytelną i zrozumiałą. Oba sposoby tworzą mylący kod.

Powinieneś bardzo się starać, aby uniknąć kodu, który tworzy klasy w pierwszej kolejności, byłoby najlepiej.

+0

@agfL przepraszam, nie jestem pewien, co masz na myśli. –

+3

Jednym z argumentów dla 'exec' dla biblioteki' decorator' było łatwiejsze zachowanie podpisu dekorowanych funkcji/metod. Myślę, że był to również argument dla 'namedtuple', który z' exec' klasy były dokładnie takie same raz skonstruowane tak, jak gdyby były kodowane ręcznie, podczas gdy z 'type' były inne. – agf

+0

Zdecydowanie chciałbym uniknąć tworzenia klas w locie, ale mam przypadek, w którym muszę utworzyć atrybuty klasy na podstawie parametrów, których nie mogę zagwarantować, aby były w zasięgu przed utworzeniem klasy. Klasy dynamiczne wydają się obecnie najprostszym sposobem. – Russ

4

Dlaczego po prostu nie utworzyć klasy w funkcji?

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

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

    Foo.__name__ = name 
    return Foo 
+0

Kiedy faktycznie jest coś _dynamic_ na temat 'Foo' - nie chcesz go tworzyć za każdym razem tak samo. – agf

+0

Oczywiście to tylko szkielet. – yak

+0

Pokaż, w jaki sposób można w ten sposób zaimplementować dekoratory "nazwane" lub "zachowujące podpis". Powiedzenie, że jest "w żaden sposób mniej elastyczny", nie jest prawdą. – agf

7

Polecam type przez tutaj.

W rzeczywistości, oświadczenie class jest po prostu cukier syntaktyczny dla wywołania type: Ciało klasa jest wykonywany w ramach własnej przestrzeni nazw, które są następnie przekazywane do metaklasą, która domyślnie type jeśli nie zwyczaj metaklasa jest określony.

To podejście jest mniej błędów, ponieważ nie ma potrzeby analizowania kodu w czasie wykonywania, a nawet może być nieco szybciej.

+0

+1 ze względów praktycznych. Nie zasługuje na -1. – agf

Powiązane problemy