Este trecho de código permite criar novas classes com nomes dinâmicos e nomes de parâmetros. A verificação de parâmetros em __init__
apenas não permite parâmetros desconhecidos, se você precisar de outras verificações, como tipo, ou que sejam obrigatórias, basta adicionar a lógica aí:
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 isso funciona assim, por exemplo:
>>> 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
Vejo que você está pedindo para inserir os nomes dinâmicos no escopo de nomenclatura - agora, isso não é considerado uma boa prática em Python - você tem nomes de variáveis, conhecidos no tempo de codificação ou dados - e os nomes aprendidos em tempo de execução são mais " dados "do que" variáveis "-
Então, você pode simplesmente adicionar suas classes a um dicionário e usá-las a partir daí:
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
E se o seu projeto precisa absolutamente que os nomes entrem no escopo, apenas faça o mesmo, mas use o dicionário retornado pelo globals()
chamada em vez de um dicionário arbitrário:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(Na verdade, seria possível para a função de fábrica de classe inserir o nome dinamicamente no escopo global do chamador - mas isso é uma prática ainda pior e não é compatível com as implementações Python. A maneira de fazer isso seria obter o autor da chamada frame de execução, através de sys._getframe (1) e definindo o nome da classe no dicionário global do frame em seu f_globals
atributo).
update, tl; dr: Esta resposta se tornou popular, ainda é muito específica para o corpo da pergunta. A resposta geral sobre como
"criar dinamicamente classes derivadas de uma classe base"
em Python é uma simples chamada para type
passar o nome da nova classe, uma tupla com as classes básicas e o __dict__
corpo da nova classe - assim:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
atualizar
Qualquer pessoa que precise disso também deve verificar o projeto de endro - ele afirma ser capaz de conservar e desempacotar classes exatamente como o pickle faz com objetos comuns, e sobreviveu a isso em alguns de meus testes.
a
, sempre seráMyClass
eTestClass
nunca receberá uma
? Por que não apenas declarar todos os 3 argumentos em,BaseClass.__init__()
mas padronizá-los todosNone
?def __init__(self, a=None, b=None, C=None)
?