Eu vejo padrões como
def __init__(self, x, y, z):
...
self.x = x
self.y = y
self.z = z
...
com bastante frequência, muitas vezes com muito mais parâmetros. Existe uma boa maneira de evitar esse tipo de repetitividade tediosa? Em namedtuple
vez disso, a classe deve herdar ?
__slots__
para o efeito ; é levemente atípico (mais detalhado para obter economia de memória), mas eu gosto muito de evitar o risco de auto-vivificar um atributo totalmente novo se eu digitar o nome.ini <shortcut> x, y, z): <shortcut>
e está pronto.Respostas:
Edit: Se você tem python 3.7+, apenas use classes de dados
Uma solução decoradora que mantém a assinatura:
fonte
signature
Isenção de responsabilidade: Parece que várias pessoas estão preocupadas em apresentar esta solução, portanto fornecerei uma isenção de responsabilidade muito clara. Você não deve usar esta solução. Eu o forneço apenas como informação, para que você saiba que o idioma é capaz disso. O restante da resposta está apenas mostrando os recursos do idioma, não endossando o uso dessa maneira.
Não há realmente nada de errado em copiar explicitamente parâmetros em atributos. Se você tiver muitos parâmetros no ctor, às vezes é considerado um cheiro de código e talvez você deva agrupar esses parâmetros em menos objetos. Outras vezes, é necessário e não há nada de errado com isso. Enfim, fazê-lo explicitamente é o caminho a percorrer.
No entanto, como você está perguntando como isso pode ser feito (e não se deve ou não), então uma solução é a seguinte:
fonte
self.__dict__.update(kwargs)
possa ser marginalmente mais PythonA.__init__
realmente esperam, nem verificação de erros para nomes de argumentos digitados incorretamente.kwargs
deixa você aberto para o equivalente a um ataque de injeção SQL. Se o seu objeto tiver um método nomeadomy_method
e você passar um argumento nomeadomy_method
para o construtor,update()
o dicionário, você apenas substituirá o método.Como outros já mencionaram, a repetição não é ruim, mas, em alguns casos, um duplo nomeado pode ser um ótimo ajuste para esse tipo de problema. Isso evita o uso de locals () ou kwargs, que geralmente são uma má idéia.
Encontrei uso limitado para isso, mas você pode herdar um nome nomeado como em qualquer outro objeto (exemplo: continuação):
fonte
properties
método pode ser escrito como justoreturn tuple(self)
, o que é mais sustentável se no futuro mais campos forem adicionados à definição de nome nomeado.XYZ = namedtuple("XYZ", "x y z")
funciona da mesma forma.namedtuple
s para esse propósito exato, especialmente em código matemático, em que uma função pode ser altamente parametrizada e ter vários coeficientes que só fazem sentido juntos.namedtuple
é que eles são somente leitura. Você não pode fazerabc.x += 1
ou algo assim.explícito é melhor do que implícito ... então você pode torná-lo mais conciso:
A melhor pergunta é você deveria?
... dito isso, se você quiser uma tupla nomeada, eu recomendaria o uso de uma tupla nomeada (lembre-se de que as tuplas têm certas condições associadas a elas) ... talvez você queira um decreto ordenado ou apenas um decreto ...
fonte
if k != "self":
pode ser alterado paraif v is not self:
teste de identidade barato, em vez de comparação de cadeias. Suponho que tecnicamente__init__
poderia ser chamado uma segunda vez após a construção e passadoself
como argumento subsequente, mas realmente não quero pensar que tipo de monstro faria isso. :-)locals
:set_fields_from_locals(locals())
. Então não são mais do que as soluções baseadas em decoradores mais mágicas.Para expandir a
gruszczy
resposta, usei um padrão como:Eu gosto desse método porque:
super().__init(...)
)X.__init__
Antes do Python 3.6, isso não dá controle sobre a ordem em que os atributos são definidos, o que pode ser um problema se alguns atributos forem propriedades com configuradores que acessam outros atributos.
Provavelmente poderia ser melhorado um pouco, mas sou o único usuário do meu próprio código, por isso não estou preocupado com nenhuma forma de limpeza de entrada. Talvez um
AttributeError
seria mais apropriado.fonte
Você também pode fazer:
Obviamente, você teria que importar o
inspect
módulo.fonte
Esta é uma solução sem importações adicionais.
Função auxiliar
Uma pequena função auxiliar torna mais conveniente e reutilizável:
Inscrição
Você precisa chamá-lo com
locals()
:Teste
Resultado:
Sem mudar
locals()
Se você não gosta de mudar,
locals()
use esta versão:fonte
locals()
não deve ser modificado (isso pode afetar o intérprete, no seu caso, removendoself
do escopo da função de chamada)self
ainda está disponível em__init__
.Uma biblioteca interessante que lida com isso (e evita muitas outras alterações) é attrs . Seu exemplo, por exemplo, pode ser reduzido a isso (suponha que a classe seja chamada
MyClass
):Você nem precisa mais de um
__init__
método, a menos que faça outras coisas também. Aqui está uma boa introdução de Glyph Lefkowitz .fonte
attr
redundância é feitadataclasses
?Meus 0,02 $. É muito próximo da resposta de Joran Beasley, mas mais elegante:
Além disso, a resposta de Mike Müller (a melhor para o meu gosto) pode ser reduzida com esta técnica:
E a justa ligação
auto_init(locals())
do seu__init__
fonte
locals()
não deve ser modificado (comportamento indefinido)É uma maneira natural de fazer coisas em Python. Não tente inventar algo mais inteligente, pois isso levará a códigos excessivamente inteligentes que ninguém na sua equipe entenderá. Se você quer ser um jogador da equipe e continue escrevendo dessa maneira.
fonte
Python 3.7 em diante
No Python 3.7, você pode (ab) usar o
dataclass
decorador, disponível nodataclasses
módulo. A partir da documentação:Se a sua turma for grande e complexa, pode ser inapropriado usar a
dataclass
. Estou escrevendo isso no dia do lançamento do Python 3.7.0, para que os padrões de uso ainda não estejam bem estabelecidos.fonte