Ten fragment kodu umożliwia tworzenie nowych klas z dynamicznymi nazwami i nazwami parametrów. Weryfikacja parametrów po __init__
prostu nie zezwala na nieznane parametry, jeśli potrzebujesz innych weryfikacji, takich jak typ, lub że są one obowiązkowe, po prostu dodaj tam logikę:
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():
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
A to działa tak, 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 prosi o wstawienie nazw dynamicznych w zakresie nazewnictwa - teraz, że nie jest uważane za dobre praktyki w Pythonie - albo masz nazwy zmiennych, znane w momencie pisania kodu lub danych - i nazwiska nabyte w czasie pracy są bardziej " dane "niż" zmienne "-
Możesz więc po prostu dodać swoje klasy do słownika i używać ich z tego miejsca:
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
A jeśli twój projekt absolutnie potrzebuje nazw, które wchodzą w zakres, po prostu zrób to samo, ale użyj słownika zwróconego przez globals()
wywołanie zamiast dowolnego słownika:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(Rzeczywiście byłoby możliwe, aby funkcja fabryki klasy wstawiała nazwę dynamicznie do globalnego zakresu wywołującego - ale jest to jeszcze gorsza praktyka i nie jest kompatybilna z implementacjami Pythona. Sposobem na to byłoby uzyskanie wywołującego wykonanie ramki, poprzez sys._getframe (1) i ustawienie nazwy klasy w globalnym słowniku ramki w jej f_globals
atrybucie).
update, tl; dr: Ta odpowiedź stała się popularna, ale nadal jest bardzo specyficzna dla treści pytania. Ogólna odpowiedź na temat
„dynamicznego tworzenia klas pochodnych z klasy bazowej”
w Pythonie jest prostym wywołaniem type
przekazania nowej nazwy klasy, krotki z __dict__
klasami podstawowymi i treścią nowej klasy - na przykład:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
aktualizacja
Każdy, kto tego potrzebuje, powinien również sprawdzić projekt koperkowy - twierdzi, że jest w stanie marynować i unpickle klasy, tak jak w przypadku zwykłych przedmiotów, i przeżył to w niektórych moich testach.
a
, zawsze będzieMyClass
iTestClass
nigdy nie weźmiea
? Dlaczego po prostu nie zadeklarować wszystkich 3 argumentów w programie,BaseClass.__init__()
ale wszystkie domyślnie są ustawione naNone
?def __init__(self, a=None, b=None, C=None)
?