Estou tentando converter uma classe "data" oca longa em uma tupla nomeada. Atualmente, minha turma tem a seguinte aparência:
class Node(object):
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
Após a conversão, namedtuple
ele se parece com:
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Mas há um problema aqui. Minha classe original me permitiu passar apenas um valor e cuidou do padrão usando valores padrão para os argumentos nomeado / palavra-chave. Algo como:
class BinaryTree(object):
def __init__(self, val):
self.root = Node(val)
Mas isso não funciona no caso da minha tupla nomeada refatorada, pois espera que eu passe por todos os campos. É claro que posso substituir as ocorrências de Node(val)
to, Node(val, None, None)
mas não é do meu agrado.
Existe um bom truque que pode tornar minha reescrita bem-sucedida sem adicionar muita complexidade de código (metaprogramação) ou devo apenas engolir a pílula e prosseguir com a "pesquisa e substituição"? :)
Node
classe original do jeito que está. Por que converter em tupla nomeada?Node
classes atuais e outras são simples objetos de valor do detentor de dados com vários campos diferentes (Node
é apenas um deles). Essas declarações de classe nada mais são do que o ruído da linha IMHO, portanto, queria apará-las. Por que manter algo que não é necessário? :).debug_print()
método que anda na árvore e a imprime?BinaryTree
classe.Node
e outros titulares de dados não exigem métodos especiais, especialmente porque as tuplas nomeadas têm uma representação__str__
e um valor decente__repr__
. :)Respostas:
Python 3.7
Use o parâmetro padrão .
Ou, melhor ainda, use a nova biblioteca de classes de dados , que é muito melhor do que o nome de duplo.
Antes do Python 3.7
Defina
Node.__new__.__defaults__
com os valores padrão.Antes do Python 2.6
Defina
Node.__new__.func_defaults
com os valores padrão.Ordem
Em todas as versões do Python, se você definir menos valores padrão do que os existentes no parâmetro nomeado, os padrões serão aplicados aos parâmetros mais à direita. Isso permite que você mantenha alguns argumentos como argumentos necessários.
Wrapper para Python 2.6 a 3.6
Aqui está um invólucro para você, que ainda permite (opcionalmente) definir os valores padrão para algo diferente de
None
. Isso não suporta os argumentos necessários.Exemplo:
fonte
isinstance
... todos os profissionais, sem contras ... pena que você estava um pouco atrasado para a festa. Esta é a melhor resposta.(None)
não é uma tupla, éNone
. Se você usar(None,)
, ele deve funcionar bem.Node.__new__.__defaults__= (None,) * len(Node._fields)
Subclassifiquei nomeado duplo e substituí o
__new__
método:Isso preserva uma hierarquia de tipos intuitiva, que a criação de uma função de fábrica disfarçada de classe não.
fonte
__new__
não está sendo chamado quando_replace
é usado.Envolva-o em uma função.
fonte
isinstance(Node('val'), Node)
: agora isso gera uma exceção, em vez de retornar True. Embora um pouco mais detalhada, a resposta de @ justinfay (abaixo) preserva as informações da hierarquia de tipos corretamente, portanto, provavelmente é uma abordagem melhor se outras pessoas interagirem com instâncias do Node.def make_node(...):
vez de fingir que é uma definição de classe. Dessa forma, os usuários não são tentados a verificar o polimorfismo de tipo na função, mas usam a própria definição de tupla.isinstance
incorreto de pessoas enganosas .No
typing.NamedTuple
Python 3.6.1+, você pode fornecer um valor padrão e uma anotação de tipo para um campo NamedTuple. Usetyping.Any
se você precisar apenas do primeiro:Uso:
Além disso, caso você precise de valores padrão e mutabilidade opcional, o Python 3.7 terá classes de dados (PEP 557) que podem em alguns casos (muitos?) Substituir os nomes nomeados.
Nota: uma das peculiaridades da especificação atual de anotações (expressões depois
:
para parâmetros e variáveis e depois->
para funções) no Python é que elas são avaliadas no momento da definição * . Portanto, como "os nomes das classes são definidos após a execução de todo o corpo da classe", as anotações'Node'
nos campos da classe acima devem ser cadeias de caracteres para evitar NameError.Esse tipo de dica de tipo é chamado "referência direta" ( [1] , [2] ) e, com o PEP 563, o Python 3.7+ terá uma
__future__
importação (a ser ativada por padrão no 4.0) que permitirá o uso de referências avançadas sem aspas, adiando sua avaliação.* Somente anotações de variáveis locais AFAICT não são avaliadas em tempo de execução. (fonte: PEP 526 )
fonte
left
eright
(ou sejaNode
) é o mesmo tipo da classe que está sendo definida e, portanto, deve ser escrito como seqüências de caracteres.my_list: List[T] = None
self.my_list = my_list if my_list is not None else []
? Não podemos usar parâmetros padrão como este?typing.NamedTuple
. Mas com classes de dados você pode usarField
objetos com umdefault_factory
attr. para isso, substituindo seu idioma pormy_list: List[T] = field(default_factory=list)
.Este é um exemplo direto dos documentos :
Portanto, o exemplo do OP seria:
No entanto, gosto mais de outras respostas dadas aqui. Eu só queria adicionar isso para completar.
fonte
_
método (que basicamente significa um privado) para algo comoreplace
o que parece bastante útil ..*args
. Pode ser que tenha sido adicionado ao idioma antes de muitas dessas coisas serem padronizadas._
prefixo é evitar colidir com os nomes dos campos de tupla definidos pelo usuário (citação relevante do documento: "Qualquer identificador Python válido pode ser usado para um nome de campo, exceto para nomes que começam com um sublinhado."). Quanto à string separada por espaço, acho que é apenas para salvar algumas teclas (e você pode passar uma sequência de strings, se preferir)._
faz muito sentido.Não tenho certeza se existe uma maneira fácil apenas com o duplo nomeado embutido. Há um bom módulo chamado recordtype que possui esta funcionalidade:
fonte
recordtype
certamente pareça interessante para trabalhos futuros. 1recordtype
é mutável, enquanto quenamedtuple
não é. Isso pode importar se você deseja que o objeto seja lavável (o que eu acho que não, desde que começou como uma classe).Aqui está uma versão mais compacta inspirada na resposta de justinfay:
fonte
Node(1, 2)
não funciona com esta receita, mas funciona na resposta do @ justinfay. Caso contrário, é bastante bacana (+1).No python3.7 +, há um novo argumento padrão = palavra-chave.
Exemplo de uso:
fonte
Curto, simples e não leva as pessoas a usar
isinstance
indevidamente:fonte
Um exemplo levemente estendido para inicializar todos os argumentos ausentes com
None
:fonte
Python 3.7: introdução de
defaults
param na definição de nome nomeado.Exemplo, como mostrado na documentação:
Leia mais aqui .
fonte
Você também pode usar isso:
Isso basicamente oferece a possibilidade de construir qualquer tupla nomeada com um valor padrão e substituir apenas os parâmetros necessários, por exemplo:
fonte
Combinando abordagens de @Denis e @Mark:
Isso deve apoiar a criação da tupla com argumentos posicionais e também com casos mistos. Casos de teste:
mas também suporta TypeError:
fonte
Acho esta versão mais fácil de ler:
Isso não é tão eficiente quanto requer a criação do objeto duas vezes, mas você pode mudar isso definindo o duple padrão dentro do módulo e apenas fazendo com que a função faça a linha de substituição.
fonte
Como você está usando
namedtuple
como uma classe de dados, você deve estar ciente de que o python 3.7 apresentará um@dataclass
decorador para esse propósito - e, é claro, ele possui valores padrão.Um exemplo dos documentos :
Muito mais limpo, legível e utilizável que o hacking
namedtuple
. Não é difícil prever que o uso denamedtuple
s cairá com a adoção do 3.7.fonte
Inspirado por esta resposta a uma pergunta diferente, aqui está a minha solução proposta com base em uma metaclasse e usando
super
(para manipular corretamente a subescalonagem futura). É bastante semelhante à resposta de justinfay .Então:
fonte
A resposta do jterrace para usar o recordtype é ótima, mas o autor da biblioteca recomenda o uso do projeto namedlist , que fornece implementações mutáveis (
namedlist
) e imutáveis (namedtuple
).fonte
Aqui está uma resposta genérica curta e simples, com uma boa sintaxe para uma tupla nomeada com argumentos padrão:
Uso:
Minificado:
fonte
Usando a
NamedTuple
classe da minhaAdvanced Enum (aenum)
biblioteca e usando aclass
sintaxe, isso é bastante simples:A única desvantagem potencial é a exigência de uma
__doc__
sequência para qualquer atributo com um valor padrão (é opcional para atributos simples). Em uso, parece com:As vantagens que isso tem sobre
justinfay's answer
:é simplicidade, além de ser
metaclass
baseada em vez deexec
baseada.fonte
Outra solução:
Uso:
fonte
Aqui está uma versão menos flexível, porém mais concisa, do invólucro de Mark Lodato: usa os campos e os padrões como um dicionário.
Exemplo:
fonte
dict
não tem garantia de encomenda.