Jak mogę dynamicznie tworzyć klasy pochodne z klasy bazowej
Na przykład mam klasę bazową w następujący sposób:
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
Z tej klasy wywodzę kilka innych klas, np.
class TestClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Test')
class SpecialClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Special')
Czy istnieje ładny, pythoniczny sposób tworzenia tych klas dynamicznie przez wywołanie funkcji, które umieszcza nową klasę w moim bieżącym zakresie, jak:
foo(BaseClass, "My")
a = MyClass()
...
Ponieważ będą komentarze i pytania, po co mi to: wszystkie klasy pochodne mają dokładnie tę samą strukturę wewnętrzną z tą różnicą, że konstruktor pobiera liczbę wcześniej nieokreślone argumenty. Na przykład, MyClass
przyjmuje słowa kluczowe a
, podczas gdy konstruktor klasy TestClass
przyjmuje b
i c
.
inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")
Ale nigdy nie powinny używać typu klasy jako argumentu wejściowego, takiego jak
inst1 = BaseClass(classtype = "My", a=4)
Udało mi się to zrobić, ale wolałabym inny sposób, tj. dynamicznie tworzone obiekty klasowe.
2 answers
Ten bit kodu pozwala na tworzenie nowych klas z dynamicznym
nazwy i nazwy parametrów.
Weryfikacja parametru w __init__
po prostu nie pozwala
nieznane parametry, jeśli potrzebujesz innych weryfikacji, takich jak
wpisz, lub że są 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
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 prosisz o wstawienie nazw dynamicznych w zakresie nazewnictwa -- teraz, to nie jest uważane za dobrą praktykę w Pythonie - albo masz nazwy zmiennych, znane w czasie kodowania, lub Dane - i nazwy poznane w trybie runtime są bardziej "dane" niż "zmienne" -
Więc możesz po prostu dodać swoje klasy do słownika i używać ich stamtąd:
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
I jeśli twój projekt absolutnie potrzebuje nazw w zakresie,
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 to możliwe dla fabryki klas funkcja dynamicznego wstawiania nazwy w globalnym zasięgu wywołującego-ale to jeszcze gorsza praktyka i nie jest kompatybilna we wszystkich implementacjach Pythona. Aby to zrobić, należy uzyskać ramkę wykonawczą wywołującego, poprzez sys._getframe (1) i ustawienie nazwy klasy w globalnym słowniku ramki w atrybutie f_globals
).
Update, TL; dr: ta odpowiedź stała się popularna, nadal jest bardzo specyficzna dla ciała pytającego. Ogólna odpowiedź na to, jak
"dynamiczne tworzenie klas pochodnych z klasy bazowej"
w Pythonie jest proste wywołanie type
przekazujące nową nazwę klasy, krotkę z baseclass (es) i ciało __dict__
dla nowej klasy -jak to:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
Update
Każdy, kto tego potrzebuje, powinien również sprawdzić projekt dill - twierdzi, że potrafi marynować i rozpakowywać klasy, tak jak pickle robi to ze zwykłymi obiektami, i przeżył to w niektórych moich testach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-10 23:38:59
type()
jest funkcją, która tworzy klasy (a w szczególności podklasy):
def set_x(self, value):
self.x = value
SubClass = type('SubClass', (BaseClass,), {'set_x': set_x})
# (More methods can be put in SubClass, including __init__().)
obj = SubClass()
obj.set_x(42)
print obj.x # Prints 42
print isinstance(obj, BaseClass) # True
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-28 02:17:29