Por exemplo, tenho uma classe base da seguinte forma:
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
Desta classe eu obtenho várias outras classes, por exemplo
class TestClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Test')
class SpecialClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Special')
Existe uma maneira legal e pítônica de criar essas classes dinamicamente por uma chamada de função que coloca a nova classe em meu escopo atual, como:
foo(BaseClass, "My")
a = MyClass()
...
Como haverá comentários e perguntas sobre por que preciso disso: Todas as classes derivadas têm exatamente a mesma estrutura interna, com a diferença de que o construtor leva uma série de argumentos previamente indefinidos. Então, por exemplo, MyClass
pega as palavras-chave a
enquanto o construtor da classe TestClass
pega b
e c
.
inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")
Mas eles NUNCA devem usar o tipo da classe como argumento de entrada como
inst1 = BaseClass(classtype = "My", a=4)
Eu fiz isso funcionar, mas preferiria o outro caminho, ou seja, objetos de classe criados dinamicamente.
fonte
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)
?Respostas:
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í:E isso funciona assim, por exemplo:
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í:
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:(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: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.
fonte
BaseClass.__init__()
seria melhor como a mais geralsuper(self.__class__).__init__()
, que funciona mais bem quando as novas classes são subclasses. (Referência: rhettinger.wordpress.com/2011/05/26/super-considered-super )super
acima e crie uma subclasse de uma classe criada dinamicamente para entendê-lo; E, por outro lado, neste caso você pode ter a classe básica como um objeto geral a partir do qual chamar__init__
.__init__
deBaseClass
é chamado com um argumento, mas na verdadeBaseClass.__init__
sempre leva uma lista arbitrária de argumentos de palavras-chave. Em segundo lugar, a solução acima define todos os nomes de parâmetros permitidos como atributos, o que não é o que desejo. QUALQUER argumento TEM que irBaseClass
, mas qual eu conheço ao criar a classe derivada. Provavelmente atualizarei a pergunta ou farei outra mais precisa para torná-la mais clara.super()
eu estava mencionando dáTypeError: must be type, not SubSubClass
. Se bem entendi, isso vem do primeiro argumentoself
de__init__()
, que éSubSubClass
onde umtype
objeto é esperado: isso parece relacionado ao fato desuper(self.__class__)
ser um superobjeto não ligado. Qual é o seu__init__()
método? Não tenho certeza de qual método pode exigir um primeiro argumento do tipotype
. Você poderia explicar? (Nota lateral: minhasuper()
abordagem de fato não faz sentido, aqui, porque__init__()
tem uma assinatura variável.)type()
é a função que cria classes (e em subclasses particulares):fonte
TypeError
que disse__init__() takes exactly 2 arguments (1 given)
. Descobri que adicionar algo (qualquer coisa?) Para preencher a lacuna seria suficiente. Por exemplo,obj = SubClass('foo')
é executado sem erros.SubClass
é uma subclasse deBaseClass
na questão eBaseClass
leva um parâmetro (classtype
, que está'foo'
no seu exemplo).Para criar uma classe com um valor de atributo dinâmico, verifique o código abaixo. NB. Estes são trechos de código na linguagem de programação python
fonte