Questo bit di codice consente di creare nuove classi con dinamiche nomi e nomi di parametro. La verifica dei parametri in __init__
solo non consente parametri ignoti, se avete bisogno di altre verifiche, come tipo, o che sono obbligatorie, basta aggiungere la logica lì:
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
E questo funziona in questo modo, per esempio:
>>> 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
vedo che stai chiedendo per inserire i nomi dinamici nel campo di applicazione di denominazione - ora, che non è considerato un buon p ractice in Python - è possibile avere sia nomi delle variabili, noti al momento di codifica, o dati - ei nomi imparato in runtime sono più "dati" che "variabili" -
Quindi, si può solo aggiungere le classi per un dizionario e usarli da lì:
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
e se il vostro disegno ha bisogno assolutamente i nomi a venire portata, basta fare lo stesso, ma utilizzare il dizionario restituito dalla globals()
chiamata invece di un dizionario di arbitrario:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(Sarebbe infatti possibile che la funzione di classe class inserisse dinamicamente il nome nello scope globale del chiamante, ma questa è una pratica ancora peggiore e non è compatibile con le implementazioni Python.Il modo per farlo sarebbe ottenere il frame di esecuzione del chiamante, tramite sys._getframe(1) e impostare il nome della classe nel dizionario globale del frame nell'attributo f_globals
).
aggiornamento, tl; dr: Questa risposta era diventata popolare, ancora molto specifica per il corpo di domanda. La risposta generale su come "dinamicamente creare classi derivate da una classe base" in Python è una semplice chiamata al type
passando il nuovo nome di classe, una tupla con il baseclass (i) e il corpo __dict__
per la nuova classe -come questo:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
aggiornamento
Chi avesse bisogno di questo dovrebbe anche controllare il progetto dill - che afferma di essere in grado di fare la serializzazione e classi di deserializzare proprio come salamoia fa a oggetti ordinari, e aveva vissuto ad esso in alcuni dei miei test.
Giusto per sicurezza, vuoi cambiare il tipo di istanza in base agli argomenti forniti? Come se io dessi un 'a' sarà sempre' MyClass' e 'TestClass' non prenderà mai un' a'? Perché non dichiarare solo tutti e 3 gli argomenti in 'BaseClass .__ init __()' ma li imposti tutti come predefiniti come 'Nessuno '? 'def __init __ (self, a = None, b = None, C = None)'? – acattle
Non posso dichiarare nulla nella classe base, dato che non conosco tutti gli argomenti che potrei usare. Potrei avere 30 diverse clase con 5 argomenti diversi, quindi dichiarare 150 argomenti nel costrutto non è una soluzione. – Alex