Alguém pode alterar namedtuple ou fornecer uma classe alternativa para que funcione para objetos mutáveis?
Principalmente para facilitar a leitura, gostaria de algo semelhante a namedtuple que faça isso:
from Camelot import namedgroup
Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
Point(x=100, y=0)
Deve ser possível conservar o objeto resultante. E de acordo com as características da tupla nomeada, a ordem da saída quando representada deve corresponder à ordem da lista de parâmetros ao construir o objeto.
python
mutable
namedtuple
Alexandre
fonte
fonte
namedtuple
s, parece que você não precisa ser capaz de referenciar os atributos por índice, ou seja,p[0]
ep[1]
seriam formas alternativas de referenciarx
ey
, respectivamente, corrigir?Respostas:
Existe uma alternativa mutável para
collections.namedtuple
- recordclass .Ele tem a mesma API e pegada de memória
namedtuple
e oferece suporte a atribuições (deve ser mais rápido também). Por exemplo:Para python 3.6 e superior
recordclass
(desde 0.5), suporta dicas de tipo:Há um exemplo mais completo (também inclui comparações de desempenho).
Desde a
recordclass
biblioteca 0.9 fornece outra variante -recordclass.structclass
função de fábrica. Ele pode produzir classes, cujas instâncias ocupam menos memória do que as__slots__
instâncias baseadas em. Isso pode ser importante para as instâncias com valores de atributo, que não pretendem ter ciclos de referência. Isso pode ajudar a reduzir o uso de memória se você precisar criar milhões de instâncias. Aqui está um exemplo ilustrativo .fonte
recordclass
é mais lento, ocupa mais memória e requer extensões C em comparação com a receita de Antti Haapala enamedlist
.recordclass
é uma versão mutável docollection.namedtuple
que herda sua api, pegada de memória, mas oferece suporte a atribuições.namedlist
é na verdade uma instância da classe Python com slots. É mais útil se você não precisa de acesso rápido a seus campos por índice.recordclass
exemplo (python 3.5.2) é cerca de 2-3% mais lento do que paranamedlist
namedtuple
uma criação de classe simplesPoint = namedtuple('Point', 'x y')
, o Jedi pode preencher automaticamente os atributos, embora este não seja o casorecordclass
. Se eu usar o código de criação mais longo (baseado emRecordClass
), então Jedi entende aPoint
classe, mas não seu construtor ou atributos ... Existe uma maneira de começarrecordclass
a trabalhar bem com Jedi?types.SimpleNamespace foi introduzido no Python 3.3 e oferece suporte aos requisitos solicitados.
fonte
SimpleNamespace
falhou nos testes 6-10 (acesso por índice, desempacotamento iterativo, iteração, dicionário ordenado, substituição in-loco) e 12, 13 (campos, slots). Observe que a documentação (que você vinculou na resposta) diz especificamente "SimpleNamespace
pode ser útil como um substituto paraclass NS: pass
. No entanto, para um tipo de registro estruturado, usenamedtuple()
".SimpleNamespace
cria um objeto, não um construtor de classe, e não pode substituir o namedtuple. A comparação de tipos não funcionará e a área de cobertura da memória será muito maior.Como uma alternativa muito Pythônica para esta tarefa, desde o Python-3.7, você pode usar um
dataclasses
módulo que não apenas se comporta como um mutável,NamedTuple
porque usa definições de classe normais, mas também suporta outros recursos de classe.Do PEP-0557:
Este recurso é apresentado no PEP-0557 e você pode ler sobre ele em mais detalhes no link de documentação fornecido.
Exemplo:
Demo:
fonte
dataclass
testes de falha 6-10 (acesso por índice, desempacotamento iterativo, iteração, dicionário ordenado, substituição in-loco) e 12, 13 (campos, slots) no Python 3.7 .1.A lista de nomes 1.7 mais recente passou em todos os seus testes com Python 2.7 e Python 3.5 em 11 de janeiro de 2016. É uma implementação de Python pura, enquanto
recordclass
é uma extensão C. Claro, depende de seus requisitos se uma extensão C é preferida ou não.Seus testes (mas também veja a nota abaixo):
Saída em Python 2.7
A única diferença com o Python 3.5 é que o
namedlist
tornou-se menor, o tamanho é 56 (relatórios do Python 2.7 64).Observe que eu mudei seu teste 10 para substituição no local. O
namedlist
tem um_replace()
método que faz uma cópia superficial e que faz todo o sentido para mim porque onamedtuple
na biblioteca padrão se comporta da mesma maneira. Mudar a semântica do_replace()
método seria confuso. Na minha opinião, o_update()
método deve ser usado para atualizações no local. Ou talvez eu não tenha entendido a intenção do seu teste 10?fonte
namedlist
valores da loja na instância da lista. A coisa é quecpython
'slist
é realmente uma matriz dinâmica. Por design, ele aloca mais memória do que o necessário para tornar a mutação da lista mais barata.list
e, por padrão, usa a__slots__
otimização. Quando fiz a medição, o uso de memória foi inferior arecordclass
: 96 bytes vs 104 bytes para seis campos no Python 2.7recorclass
usa mais memória porque é umtuple
objeto semelhante com tamanho de memória variável.types.SimpleNamespace
. Infelizmente, o pylint não gosta disso :-(Parece que a resposta a essa pergunta é não.
Abaixo está bem parecido, mas não é tecnicamente mutável. Isso é criar uma nova
namedtuple()
instância com um valor x atualizado:Por outro lado, você pode criar uma classe simples usando
__slots__
que deve funcionar bem para atualizar atributos de instância de classe com frequência:Para adicionar a essa resposta, acho que
__slots__
é um bom uso aqui porque é eficiente em termos de memória quando você cria muitas instâncias de classe. A única desvantagem é que você não pode criar novos atributos de classe.Aqui está um tópico relevante que ilustra a eficiência da memória - Dicionário vs Objeto - que é mais eficiente e por quê?
O conteúdo citado na resposta deste tópico é uma explicação muito sucinta porque
__slots__
é mais eficiente em termos de memória - slots Pythonfonte
O que se segue é uma boa solução para o Python 3: Uma classe mínima, utilizando
__slots__
eSequence
classe base resumo; não faz detecção de erros extravagante ou algo semelhante, mas funciona e se comporta principalmente como uma tupla mutável (exceto para typecheck).Exemplo:
Se quiser, você pode ter um método para criar a classe também (embora usar uma classe explícita seja mais transparente):
Exemplo:
No Python 2, você precisa ajustá-lo ligeiramente - se você herdar de
Sequence
, a classe terá um__dict__
e o__slots__
deixará de funcionar.A solução em Python 2 é não herdar de
Sequence
, masobject
. Seisinstance(Point, Sequence) == True
desejar, você precisa registrar oNamedMutableSequence
como uma classe base paraSequence
:fonte
Vamos implementar isso com a criação de tipo dinâmico:
Isso verifica os atributos para ver se eles são válidos antes de permitir que a operação continue.
Então, isso é pickleable? Sim se (e somente se) você fizer o seguinte:
A definição deve estar em seu namespace e deve existir por tempo suficiente para que pickle a encontre. Portanto, se você definir que está em seu pacote, deve funcionar.
Pickle irá falhar se você fizer o seguinte ou tornar a definição temporária (sai do escopo quando a função termina, digamos):
E sim, ele preserva a ordem dos campos listados na criação do tipo.
fonte
__iter__
método comfor k in self._attrs_: yield getattr(self, k)
, isso suportará descompactação como uma tupla.__len__
,__getitem__
e__setiem__
métodos para apoiar ficando valus pelo índice, comop[0]
. Com essas últimas partes, esta parece ser a resposta mais completa e correta (para mim, pelo menos).__len__
e__iter__
são bons.__getitem__
e__setitem__
pode realmente ser mapeado paraself.__dict__.__setitem__
eself.__dict__.__getitem__
As tuplas são, por definição, imutáveis.
No entanto, você pode criar uma subclasse de dicionário onde pode acessar os atributos com notação de ponto;
fonte
Se você deseja um comportamento semelhante ao namedtuples, mas mutável, tente namedlist
Observe que, para ser mutável, não pode ser uma tupla.
fonte
Desde que o desempenho seja de pouca importância, pode-se usar um hack bobo como:
fonte
z
, você tem que chamarmutable_z.z.pop(0)
entãomutable_z.z.append(new_value)
. Se errar, você acabará com mais de 1 elemento e seu programa se comportará de maneira inesperada.mutable_z.z[0] = newValue
. Na verdade, é um hack, como afirmado.