Ten fragment kodu pozwala na tworzenie nowych klas z dynamicznymi nazwisk i nazw parametrów. Weryfikacja parametrów w __init__
prostu nie pozwala nieznanych parametrów, jeśli potrzebujesz inne weryfikacje, jak typu, lub że są one obowiązkowe, wystarczy dodać logikę tam:
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
def ClassFactory(name, argnames, BaseClass=BaseClass):
def __init__(self, **kwargs):
for key, value in kwargs.items():
# here, the argnames variable is the one passed to the
# ClassFactory call
if key not in argnames:
raise TypeError("Argument %s not valid for %s"
% (key, self.__class__.__name__))
setattr(self, key, value)
BaseClass.__init__(self, name[:-len("Class")])
newclass = type(name, (BaseClass,),{"__init__": __init__})
return newclass
I to działa w ten sposób, na przykład:
>>> SpecialClass = ClassFactory("SpecialClass", "a b c".split())
>>> s = SpecialClass(a=2)
>>> s.a
2
>>> s2 = SpecialClass(d=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in __init__
TypeError: Argument d not valid for SpecialClass
widzę, że proszą o włożenie dynamicznych nazw w zakresie nazewnictwa - teraz że nie jest uważane za dobry p ractice w Pythonie - albo masz nazw zmiennych, znane w momencie pisania kodu lub danych - a nazwy nauczył się w czasie wykonywania więcej „dane” niż „zmiennych” -
Tak, można po prostu dodać swoje klasy do słownika i wykorzystywać je stamtąd:
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
a jeśli projekt absolutnie potrzebuje nazwy przyjść w zakresie, prostu zrobić to samo, ale korzystać ze słownika zwrócony przez globals()
rozmowy zamiast dowolnego słownika:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(W rzeczywistości funkcja fabryczna klasy może wstawiać nazwę dynamicznie na globalnym zasięgu dzwoniącego - ale to jest jeszcze gorsza praktyka i nie jest kompatybilna z innymi implementacjami Pythona.Aby to zrobić, należy uzyskać ramkę wykonania dzwoniącego, poprzez sys._getframe(1) i ustawić nazwę klasy w globalnym słowniku ramki w jej atrybucie f_globals
).
aktualizacja, tl; dr: Ta odpowiedź stała się popularna, wciąż bardzo specyficzna dla ciała pytania. Ogólna odpowiedź na jak „dynamicznego tworzenia klas pochodnych z klasy bazowej” w Pythonie jest proste wywołanie type
przekazując nową nazwę klasy, krotką z klasy bazowej (y) i ciała __dict__
dla nowej klasy -Jak to:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
aktualizacja
Każdy potrzebuje ten powinien również sprawdzić projekt dill - twierdzi, że jest w stanie marynowane i unpickle klasy tak jak robi zalewie zwykłych przedmiotów, i żył z nim w niektóre z moich testów.
Czy chcesz mieć pewność, że typ instancji zmieni się w zależności od dostarczonych argumentów? Jakbym dał "a", zawsze będzie to "MyClass", a "TestClass" nigdy nie przyjmie "a"? Dlaczego po prostu nie zadeklarować wszystkich 3 argumentów w 'BaseClass .__ init __()', ale domyślnie wszystkie do 'None'? 'def __init __ (self, a = None, b = None, C = None)'? – acattle
Nie mogę zadeklarować niczego w klasie bazowej, ponieważ nie znam wszystkich argumentów, których mógłbym użyć. Mogę mieć 30 różnych zdań z 5 różnymi argumentami, więc deklarowanie 150 argumentów w konstruktorze nie jest rozwiązaniem. – Alex