Estruturas do tipo C em Python


446

Existe uma maneira de definir convenientemente uma estrutura semelhante a C em Python? Estou cansado de escrever coisas como:

class MyStruct():
    def __init__(self, field1, field2, field3):
        self.field1 = field1
        self.field2 = field2
        self.field3 = field3

5
Semi-relacionados, os tipos de dados algébricos seriam absolutamente maravilhosos, mas para usá-los bem, geralmente você precisa de correspondência de padrões.
Edward Z. Yang

50
Existe algo errado com esse método que não seja tedioso para escrever?
levesque

2
Você pode achar útil o dstruct: github.com/dorkitude/dstruct
Kyle Wild

10
@levesque mais difícil de re-fator sem erros, mais difíceis de ler em resumo enquanto desnatação código, queMyStruct = namedtuple("MyStruct", "field1 field2 field3")
sam boosalis

1
pandas.Series(a=42).adeve fazê-lo se o seu data-cientista ...
Mark Horvath

Respostas:


341

Use uma tupla nomeada , que foi adicionada ao módulo de coleções na biblioteca padrão no Python 2.6. Também é possível usar a receita de tupla nomeada de Raymond Hettinger se você precisar oferecer suporte ao Python 2.4.

É bom para o seu exemplo básico, mas também abrange vários casos extremos que você pode encontrar mais tarde também. Seu fragmento acima seria escrito como:

from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")

O tipo recém-criado pode ser usado assim:

m = MyStruct("foo", "bar", "baz")

Você também pode usar argumentos nomeados:

m = MyStruct(field1="foo", field2="bar", field3="baz")

164
... mas o nome nomeado é imutável. O exemplo no OP é mutável.
precisa saber é o seguinte

28
@howhow - No meu caso, isso é apenas uma vantagem.
ArtOfWarfare 12/08

3
Ótima solução. Como você percorreria uma matriz dessas tuplas? Eu assumiria que os campos 1 a 3 teriam que ter o mesmo nome nos objetos da tupla.
Michael Smith

2
namedtuple pode ter atmost quatro argumentos então como podemos mapear a estrutura com mais membros de dados com correspondente namedtuple
Kapil

3
@ Kapil - O segundo argumento para nomear duplo deve ser uma lista dos nomes dos membros. Essa lista pode ter qualquer tamanho.
ArtOfWarfare 14/10

225

Atualização : Classes de Dados

Com a introdução das classes de dados no Python 3.7 , chegamos muito perto.

O exemplo a seguir é semelhante ao exemplo NamedTuple abaixo, mas o objeto resultante é mutável e permite valores padrão.

from dataclasses import dataclass


@dataclass
class Point:
    x: float
    y: float
    z: float = 0.0


p = Point(1.5, 2.5)

print(p)  # Point(x=1.5, y=2.5, z=0.0)

Isso funciona bem com o novo módulo de digitação , caso você queira usar anotações de tipo mais específicas.

Eu tenho esperado desesperadamente por isso! Se você me perguntar, Data Classes e a nova declaração NamedTuple , combinadas com o módulo de digitação , são uma dádiva de Deus!

Declaração NamedTuple aprimorada

Desde o Python 3.6 , tornou-se bastante simples e bonito (IMHO), desde que você possa viver com imutabilidade .

Foi introduzida uma nova maneira de declarar NamedTuples , que também permite anotações de tipo :

from typing import NamedTuple


class User(NamedTuple):
    name: str


class MyStruct(NamedTuple):
    foo: str
    bar: int
    baz: list
    qux: User


my_item = MyStruct('foo', 0, ['baz'], User('peter'))

print(my_item) # MyStruct(foo='foo', bar=0, baz=['baz'], qux=User(name='peter'))

6
Cara, você acabou de fazer o meu dia - ditados imutáveis ​​- obrigado: D
Dmitry Arkhipenko

10
O dataclassmódulo é novo no Python 3.7, mas você pode pip install dataclasses. É o backport no Python 3.6. pypi.org/project/dataclasses/#description
Lavande

+1 para a declaração NamedTuple aprimorada. A maneira antiga era muito desagradável para ler se você tivesse várias variáveis ...
gebbissimo

@Lavande Posso saber quais mudanças ocorreram entre 3,6 e 3,7 e que você precisa fazer o backport de uma versão menor ...?
Purple Ice

1
@PurpleIce Foi uma implementação do PEP 557, Classes de dados @dataclassOs detalhes estão aqui: pypi.org/project/dataclasses/#description
Lavande

96

Você pode usar uma tupla para muitas coisas em que você usaria uma estrutura em C (algo como coordenadas x, y ou cores RGB, por exemplo).

Para todo o resto, você pode usar o dicionário ou uma classe de utilitário como esta :

>>> class Bunch:
...     def __init__(self, **kwds):
...         self.__dict__.update(kwds)
...
>>> mystruct = Bunch(field1=value1, field2=value2)

Acho que a discussão "definitiva" está aqui , na versão publicada do Python Cookbook.


5
Uma classe vazia faria o mesmo?
Kurt Liu

44
Nota Se você é novo para python: tuplas são somente leitura, uma vez criado, ao contrário de estruturas C
LeBleu

2
@KurtLiu Não, provavelmente diriaTypeError: this constructor takes no arguments
Evgeni Sergeev

84

Talvez você esteja procurando Structs sem construtores:

class Sample:
  name = ''
  average = 0.0
  values = None # list cannot be initialized here!


s1 = Sample()
s1.name = "sample 1"
s1.values = []
s1.values.append(1)
s1.values.append(2)
s1.values.append(3)

s2 = Sample()
s2.name = "sample 2"
s2.values = []
s2.values.append(4)

for v in s1.values:   # prints 1,2,3 --> OK.
  print v
print "***"
for v in s2.values:   # prints 4 --> OK.
  print v

5
O que você está fazendo aqui funciona tecnicamente, mas provavelmente não é imediatamente aparente para muitos usuários o motivo pelo qual ele funciona. Suas declarações abaixo class Sample:não fazem nada imediatamente; eles definem atributos de classe. Esses sempre podem ser acessados ​​como por exemplo Sample.name.
Channing Moore

22
O que você está realmente fazendo é adicionar propriedades da instância aos objetos s1e s2no tempo de execução. A menos que seja proibido de outra forma, você pode adicionar ou modificar o nameatributo em qualquer instância de qualquer classe a qualquer momento, independentemente de a classe ter ou não um nameatributo. Provavelmente, o maior problema funcional ao fazer isso é que diferentes instâncias da mesma classe se comportarão de maneira diferente dependendo se você definiu name. Se você atualizar Sample.name, qualquer objeto sem uma namepropriedade definida explicitamente retornará o novo name.
Channing Moore

2
Isso é o mais próximo possível de uma estrutura - curta 'classe' sem métodos, 'campos' (atributos de classe, eu sei) com valores padrão. Contanto que não seja um tipo mutável (dict, list), você estará bem. Obviamente, você pode acessar o PEP-8 ou verificações IDE "amigáveis", como a classe "classe não tem método init do PyCharm ".
Tomasz Gandor 04/04

4
Eu experimentei o efeito colateral descrito por Channing Moore. Não vale a economia de algumas selfpalavras-chave e uma linha de construtor, se você me perguntar. Gostaria que Jose pudesse editar sua resposta para adicionar uma mensagem de aviso sobre o risco de compartilhar valores acidentalmente entre instâncias.
Stéphane C.

@ ChanningMoore: tentei recriar o problema que você estava descrevendo, mas falhou. Você poderia apresentar um exemplo de trabalho mínimo em que o problema aparece?
Gebbissimo 28/01/19

67

Que tal um dicionário?

Algo assim:

myStruct = {'field1': 'some val', 'field2': 'some val'}

Então você pode usar isso para manipular valores:

print myStruct['field1']
myStruct['field2'] = 'some other values'

E os valores não precisam ser seqüências de caracteres. Eles podem ser praticamente qualquer outro objeto.


34
Essa também foi minha abordagem, mas sinto que é perigosa precisamente porque um dicionário pode aceitar qualquer coisa como chave. Não haverá erro se eu definir myStruct ["ffield"] quando pretender definir myStruct ["field"]. O problema pode (ou não) se tornar aparente quando eu estiver usando ou reutilizando o myStruct ["campo"] posteriormente. Eu gosto da abordagem de PabloG.
mobabo

O mesmo problema existe com PabloG. Tente adicionar o seguinte código ao dele: pt3.w = 1 print pt3.w Em uma linguagem com dicts, é melhor usá-los, especialmente para objetos que estão sendo serializados, pois você pode usar automaticamente o import json para salvá-los e outras bibliotecas de serialização, desde que você não tenha estranhos coisas dentro do seu ditado. Os dicts são a solução para manter dados e lógica separados e são melhores do que estruturas para pessoas que não desejam gravar funções serializadas e desserializadas personalizadas e não desejam usar serializadores não portáteis como pickle.
Poikilos

27

dF: isso é muito legal ... eu não sabia que poderia acessar os campos em uma classe usando dict.

Mark: as situações que eu gostaria de ter são exatamente quando eu quero uma tupla, mas nada tão "pesado" quanto um dicionário.

Você pode acessar os campos de uma classe usando um dicionário, porque os campos de uma classe, seus métodos e todas as suas propriedades são armazenados internamente usando dictos (pelo menos no CPython).

... O que nos leva ao seu segundo comentário. Acreditar que os dicionários de Python são "pesados" é um conceito extremamente não-pythonístico. E ler esses comentários mata meu Python Zen. Isso não é bom.

Veja bem, quando você declara uma classe, na verdade está criando um wrapper bastante complexo em torno de um dicionário - portanto, se houver, você está adicionando mais sobrecarga do que usando um dicionário simples. Uma sobrecarga que, a propósito, não tem sentido em nenhum caso. Se você estiver trabalhando em aplicativos críticos para o desempenho, use C ou algo assim.


5
# 1, Cython! = CPython. Eu acho que você estava falando do CPython, a implementação do Python escrita em C, não do Cython, um projeto para cruzar o código Python compilado no código C. Eu editei sua resposta para corrigir isso. # 2, acho que quando ele disse que os ditados são pesados, ele estava se referindo à sintaxe. self['member']tem 3 caracteres a mais que self.membere esses caracteres são todos relativamente fáceis de usar.
ArtOfWarfare 8/09/15

19

Você pode subclassificar a estrutura C que está disponível na biblioteca padrão. O módulo ctypes fornece uma classe Structure . O exemplo dos documentos:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = [("x", c_int),
...                 ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print point.x, point.y
10 20
>>> point = POINT(y=5)
>>> print point.x, point.y
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: too many initializers
>>>
>>> class RECT(Structure):
...     _fields_ = [("upperleft", POINT),
...                 ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print rc.upperleft.x, rc.upperleft.y
0 5
>>> print rc.lowerright.x, rc.lowerright.y
0 0
>>>

18

Eu também gostaria de adicionar uma solução que usa slots :

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

Definitivamente, verifique a documentação quanto a slots, mas uma explicação rápida dos slots é que é a maneira de o python dizer: "Se você pode bloquear esses atributos e somente esses atributos na classe, de forma que você confirme que não adicionará novos atributos uma vez que a classe é instanciado (sim, você pode adicionar novos atributos a uma instância de classe, veja o exemplo abaixo), então eliminarei a grande alocação de memória que permite adicionar novos atributos a uma instância de classe e usarei exatamente o que preciso para esses atributos com fenda ".

Exemplo de adição de atributos à instância da classe (portanto, não usando slots):

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8
print(p1.z)

Saída: 8

Exemplo de tentativa de adicionar atributos à instância da classe em que os slots foram usados:

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8

Saída: AttributeError: o objeto 'Point' não tem atributo 'z'

Isso pode funcionar efetivamente como uma estrutura e usa menos memória que uma classe (como uma estrutura faria, embora eu não tenha pesquisado exatamente quanto). É recomendável usar slots se você estiver criando uma grande quantidade de instâncias do objeto e não precisar adicionar atributos. Um objeto de ponto é um bom exemplo disso, pois é possível que seja possível instanciar muitos pontos para descrever um conjunto de dados.


17

Você também pode passar os parâmetros init para as variáveis ​​da instância pela posição

# Abstract struct class       
class Struct:
    def __init__ (self, *argv, **argd):
        if len(argd):
            # Update by dictionary
            self.__dict__.update (argd)
        else:
            # Update by position
            attrs = filter (lambda x: x[0:2] != "__", dir(self))
            for n in range(len(argv)):
                setattr(self, attrs[n], argv[n])

# Specific class
class Point3dStruct (Struct):
    x = 0
    y = 0
    z = 0

pt1 = Point3dStruct()
pt1.x = 10

print pt1.x
print "-"*10

pt2 = Point3dStruct(5, 6)

print pt2.x, pt2.y
print "-"*10

pt3 = Point3dStruct (x=1, y=2, z=3)
print pt3.x, pt3.y, pt3.z
print "-"*10

7
A atualização por posição ignora a ordem de declaração dos atributos e usa sua classificação alfabética. Portanto, se você alterar a ordem das linhas na Point3dStructdeclaração, Point3dStruct(5, 6)não funcionará conforme o esperado. É estranho que ninguém tenha escrito isso nos 6 anos.
lapis

Poderia adicionar uma versão Python 3 ao seu código incrível? Ótimo trabalho! Eu gosto que você pegue algo abstrato e explique com a segunda classe específica. Isso deve ser bom para manipulação / captura de erros. Para o Python 3, basta alterar print> print()e attrs[n]> next(attrs)(o filtro agora é seu próprio objeto iterável e requer next).
Jonathan Komar

10

Sempre que preciso de um "objeto de dados instantâneos que também se comporte como um dicionário" ( não penso em estruturas em C!), Penso neste truque fofo:

class Map(dict):
    def __init__(self, **kwargs):
        super(Map, self).__init__(**kwargs)
        self.__dict__ = self

Agora você pode apenas dizer:

struct = Map(field1='foo', field2='bar', field3=42)

self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])

Perfeitamente útil para aqueles momentos em que você precisa de um "saco de dados que NÃO é uma classe" e para quando os nomeados são incompreensíveis ...


Eu uso pandas.Series (A = 42) ;-)
Mark Horvath

8

Você acessa a estrutura C-Style em python da seguinte maneira.

class cstruct:
    var_i = 0
    var_f = 0.0
    var_str = ""

se você quiser apenas usar o objeto cstruct

obj = cstruct()
obj.var_i = 50
obj.var_f = 50.00
obj.var_str = "fifty"
print "cstruct: obj i=%d f=%f s=%s" %(obj.var_i, obj.var_f, obj.var_str)

se você deseja criar uma matriz de objetos de cstruct

obj_array = [cstruct() for i in range(10)]
obj_array[0].var_i = 10
obj_array[0].var_f = 10.00
obj_array[0].var_str = "ten"

#go ahead and fill rest of array instaces of struct

#print all the value
for i in range(10):
    print "cstruct: obj_array i=%d f=%f s=%s" %(obj_array[i].var_i, obj_array[i].var_f, obj_array[i].var_str)

Nota: em vez do nome 'cstruct', use o nome da sua estrutura em vez de var_i, var_f, var_str, defina a variável de membro da sua estrutura.


3
Isso é diferente do que está no stackoverflow.com/a/3761729/1877426 ?
lagweezle

8

Algumas respostas aqui são massivamente elaboradas. A opção mais simples que encontrei é (de: http://norvig.com/python-iaq.html ):

class Struct:
    "A structure that can have any fields defined."
    def __init__(self, **entries): self.__dict__.update(entries)

Inicializando:

>>> options = Struct(answer=42, linelen=80, font='courier')
>>> options.answer
42

adicionando mais:

>>> options.cat = "dog"
>>> options.cat
dog

edit: Desculpe, ainda não vi esse exemplo mais abaixo.


5

Isso pode ser um pouco tarde, mas eu fiz uma solução usando Python Meta-Classes (versão decoradora abaixo também).

Quando __init__é chamado durante o tempo de execução, ele pega cada um dos argumentos e seus valores e os atribui como variáveis ​​de instância à sua classe. Dessa forma, você pode criar uma classe do tipo struct sem precisar atribuir todos os valores manualmente.

Meu exemplo não possui verificação de erro, portanto é mais fácil segui-lo.

class MyStruct(type):
    def __call__(cls, *args, **kwargs):
        names = cls.__init__.func_code.co_varnames[1:]

        self = type.__call__(cls, *args, **kwargs)

        for name, value in zip(names, args):
            setattr(self , name, value)

        for name, value in kwargs.iteritems():
            setattr(self , name, value)
        return self 

Aqui está em ação.

>>> class MyClass(object):
    __metaclass__ = MyStruct
    def __init__(self, a, b, c):
        pass


>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>> 

Eu postou no reddit e / u / Matchu postou uma versão decorador que é mais limpo. Aconselho você a usá-lo, a menos que queira expandir a versão da metaclasse.

>>> def init_all_args(fn):
    @wraps(fn)
    def wrapped_init(self, *args, **kwargs):
        names = fn.func_code.co_varnames[1:]

        for name, value in zip(names, args):
            setattr(self, name, value)

        for name, value in kwargs.iteritems():
            setattr(self, name, value)

    return wrapped_init

>>> class Test(object):
    @init_all_args
    def __init__(self, a, b):
        pass


>>> a = Test(1, 2)
>>> a.a
1
>>> 

Droga - eu passei duas horas hoje escrevendo meu próprio decorador para fazer isso e então eu encontrei isso. Enfim, postar o meu porque ele lida com valores padrão, enquanto o seu não. stackoverflow.com/a/32448434/901641
ArtOfWarfare

+1 por mencionar func_code. Começou a cavar nessa direção e encontrou muitas coisas interessantes por lá.
wombatonfire

5

Eu escrevi um decorador que você pode usar em qualquer método para torná-lo de modo que todos os argumentos passados ​​ou quaisquer padrões sejam atribuídos à instância.

def argumentsToAttributes(method):
    argumentNames = method.func_code.co_varnames[1:]

    # Generate a dictionary of default values:
    defaultsDict = {}
    defaults = method.func_defaults if method.func_defaults else ()
    for i, default in enumerate(defaults, start = len(argumentNames) - len(defaults)):
        defaultsDict[argumentNames[i]] = default

    def newMethod(self, *args, **kwargs):
        # Use the positional arguments.
        for name, value in zip(argumentNames, args):
            setattr(self, name, value)

        # Add the key word arguments. If anything is missing, use the default.
        for name in argumentNames[len(args):]:
            setattr(self, name, kwargs.get(name, defaultsDict[name]))

        # Run whatever else the method needs to do.
        method(self, *args, **kwargs)

    return newMethod

Uma demonstração rápida. Observe que eu uso um argumento posicional a, use o valor padrão para be um argumento nomeado c. Em seguida, imprimo todas as 3 referências self, para mostrar que elas foram atribuídas corretamente antes da inserção do método.

class A(object):
    @argumentsToAttributes
    def __init__(self, a, b = 'Invisible', c = 'Hello'):
        print(self.a)
        print(self.b)
        print(self.c)

A('Why', c = 'Nothing')

Observe que meu decorador deve funcionar com qualquer método, não apenas __init__.


5

Não vejo essa resposta aqui, então acho que vou adicioná-la, já que estou inclinando o Python agora e a descobri. O tutorial do Python (neste caso, o Python 2) fornece o seguinte exemplo simples e eficaz:

class Employee:
    pass

john = Employee()  # Create an empty employee record

# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

Ou seja, um objeto de classe vazio é criado, instanciado e os campos são adicionados dinamicamente.

O lado positivo disso é realmente simples. A desvantagem é que não é particularmente auto-documentada (os membros pretendidos não estão listados em nenhum lugar da classe "definição"), e os campos não definidos podem causar problemas quando acessados. Esses dois problemas podem ser resolvidos por:

class Employee:
    def __init__ (self):
        self.name = None # or whatever
        self.dept = None
        self.salary = None

Agora, de relance, você pode pelo menos ver quais campos o programa estará esperando.

Ambos são propensos a erros de digitação, john.slarly = 1000terão sucesso. Ainda assim, funciona.


4

Aqui está uma solução que usa uma classe (nunca instanciada) para armazenar dados. Gosto que dessa maneira envolva muito pouca digitação e não exija nenhum pacote adicional, etc.

class myStruct:
    field1 = "one"
    field2 = "2"

Você pode adicionar mais campos posteriormente, conforme necessário:

myStruct.field3 = 3

Para obter os valores, os campos são acessados ​​como de costume:

>>> myStruct.field1
'one'

2

Pessoalmente, eu também gosto dessa variante. Estende a resposta do @ dF .

class struct:
    def __init__(self, *sequential, **named):
        fields = dict(zip(sequential, [None]*len(sequential)), **named)
        self.__dict__.update(fields)
    def __repr__(self):
        return str(self.__dict__)

Ele suporta dois modos de inicialização (que podem ser combinados):

# Struct with field1, field2, field3 that are initialized to None.
mystruct1 = struct("field1", "field2", "field3") 
# Struct with field1, field2, field3 that are initialized according to arguments.
mystruct2 = struct(field1=1, field2=2, field3=3)

Além disso, ele imprime melhor:

print(mystruct2)
# Prints: {'field3': 3, 'field1': 1, 'field2': 2}

2

A solução a seguir para uma estrutura é inspirada na implementação do nomeado duplo e em algumas das respostas anteriores. No entanto, ao contrário do duplo nomeado, ele é mutável, em seus valores, mas como a estrutura do estilo c imutável nos nomes / atributos, que não são uma classe ou ditado normal.

_class_template = """\
class {typename}:
def __init__(self, *args, **kwargs):
    fields = {field_names!r}

    for x in fields:
        setattr(self, x, None)            

    for name, value in zip(fields, args):
        setattr(self, name, value)

    for name, value in kwargs.items():
        setattr(self, name, value)            

def __repr__(self):
    return str(vars(self))

def __setattr__(self, name, value):
    if name not in {field_names!r}:
        raise KeyError("invalid name: %s" % name)
    object.__setattr__(self, name, value)            
"""

def struct(typename, field_names):

    class_definition = _class_template.format(
        typename = typename,
        field_names = field_names)

    namespace = dict(__name__='struct_%s' % typename)
    exec(class_definition, namespace)
    result = namespace[typename]
    result._source = class_definition

    return result

Uso:

Person = struct('Person', ['firstname','lastname'])
generic = Person()
michael = Person('Michael')
jones = Person(lastname = 'Jones')


In [168]: michael.middlename = 'ben'
Traceback (most recent call last):

  File "<ipython-input-168-b31c393c0d67>", line 1, in <module>
michael.middlename = 'ben'

  File "<string>", line 19, in __setattr__

KeyError: 'invalid name: middlename'

2

Existe um pacote python exatamente para esse fim. veja cstruct2py

cstruct2pyé uma biblioteca python pura para gerar classes python a partir do código C e usá-las para compactar e descompactar dados. A biblioteca pode analisar os cabeçalhos C (declarações de estruturas, uniões, enumerações e matrizes) e emulá-los em python. As classes pitônicas geradas podem analisar e compactar os dados.

Por exemplo:

typedef struct {
  int x;
  int y;
} Point;

after generating pythonic class...
p = Point(x=0x1234, y=0x5678)
p.packed == "\x34\x12\x00\x00\x78\x56\x00\x00"

Como usar

Primeiro, precisamos gerar as estruturas pitônicas:

import cstruct2py
parser = cstruct2py.c2py.Parser()
parser.parse_file('examples/example.h')

Agora podemos importar todos os nomes do código C:

parser.update_globals(globals())

Também podemos fazer isso diretamente:

A = parser.parse_string('struct A { int x; int y;};')

Usando tipos e definições do código C

a = A()
a.x = 45
print a
buf = a.packed
b = A(buf)
print b
c = A('aaaa11112222', 2)
print c
print repr(c)

A saída será:

{'x':0x2d, 'y':0x0}
{'x':0x2d, 'y':0x0}
{'x':0x31316161, 'y':0x32323131}
A('aa111122', x=0x31316161, y=0x32323131)

Clone

Para cstruct2pyexecução de clone :

git clone https://github.com/st0ky/cstruct2py.git --recursive

0

Eu acho que o dicionário de estrutura Python é adequado para esse requisito.

d = dict{}
d[field1] = field1
d[field2] = field2
d[field2] = field3

0

https://stackoverflow.com/a/32448434/159695 não funciona no Python3.

https://stackoverflow.com/a/35993/159695 funciona em Python3.

E eu o estendo para adicionar valores padrão.

class myStruct:
    def __init__(self, **kwds):
        self.x=0
        self.__dict__.update(kwds) # Must be last to accept assigned member variable.
    def __repr__(self):
        args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
        return '%s(%s)' % ( self.__class__.__qualname__, ', '.join(args) )

a=myStruct()
b=myStruct(x=3,y='test')
c=myStruct(x='str')

>>> a
myStruct(x=0)
>>> b
myStruct(x=3, y='test')
>>> c
myStruct(x='str')

0

Se você não possui um 3.7 para @dataclass e precisa de mutabilidade, o código a seguir pode funcionar para você. É bastante auto-documentado e amigável ao IDE (preenchimento automático), evita escrever as coisas duas vezes, é facilmente extensível e é muito simples testar se todas as variáveis ​​de instância são completamente inicializadas:

class Params():
    def __init__(self):
        self.var1 : int = None
        self.var2 : str = None

    def are_all_defined(self):
        for key, value in self.__dict__.items():
            assert (value is not None), "instance variable {} is still None".format(key)
        return True


params = Params()
params.var1 = 2
params.var2 = 'hello'
assert(params.are_all_defined)

0

Aqui está um truque rápido e sujo:

>>> ms = Warning()
>>> ms.foo = 123
>>> ms.bar = 'akafrit'

Como isso funciona? Apenas reutiliza a classe builtin Warning(derivada deException ) e use-a como se fosse sua própria classe definida.

Os pontos positivos são que você não precisa importar ou definir nada primeiro, que "Aviso" é um nome abreviado e também deixa claro que você está fazendo algo sujo que não deve ser usado em outro lugar que não seja um pequeno script seu.

A propósito, tentei encontrar algo ainda mais simples, ms = object()mas não consegui (este último exemplo não está funcionando). Se você tiver um, estou interessado.


0

A melhor maneira de fazer isso foi usar uma classe de dicionário personalizada, conforme explicado neste post: https://stackoverflow.com/a/14620633/8484485

Se o suporte ao preenchimento automático do iPython for necessário, basta definir a função dir () assim:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self
    def __dir__(self):
        return self.keys()

Você define sua pseudo estrutura da seguinte forma: (esta é aninhada)

my_struct=AttrDict ({
    'com1':AttrDict ({
        'inst':[0x05],
        'numbytes':2,
        'canpayload':False,
        'payload':None
    })
})

Você pode acessar os valores dentro de my_struct assim:

print(my_struct.com1.inst)

=>[5]


0

NamedTuple é confortável. mas ninguém compartilha o desempenho e o armazenamento.

from typing import NamedTuple
import guppy  # pip install guppy
import timeit


class User:
    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserSlot:
    __slots__ = ('name', 'uid')

    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserTuple(NamedTuple):
    # __slots__ = ()  # AttributeError: Cannot overwrite NamedTuple attribute __slots__
    name: str
    uid: int


def get_fn(obj, attr_name: str):
    def get():
        getattr(obj, attr_name)
    return get
if 'memory test':
    obj = [User('Carson', 1) for _ in range(1000000)]      # Cumulative: 189138883
    obj_slot = [UserSlot('Carson', 1) for _ in range(1000000)]          # 77718299  <-- winner
    obj_namedtuple = [UserTuple('Carson', 1) for _ in range(1000000)]   # 85718297
    print(guppy.hpy().heap())  # Run this function individually. 
    """
    Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 1000000    24 112000000 34 112000000  34 dict of __main__.User
     1 1000000    24 64000000  19 176000000  53 __main__.UserTuple
     2 1000000    24 56000000  17 232000000  70 __main__.User
     3 1000000    24 56000000  17 288000000  87 __main__.UserSlot
     ...
    """

if 'performance test':
    obj = User('Carson', 1)
    obj_slot = UserSlot('Carson', 1)
    obj_tuple = UserTuple('Carson', 1)

    time_normal = min(timeit.repeat(get_fn(obj, 'name'), repeat=20))
    print(time_normal)  # 0.12550550000000005

    time_slot = min(timeit.repeat(get_fn(obj_slot, 'name'), repeat=20))
    print(time_slot)  # 0.1368690000000008

    time_tuple = min(timeit.repeat(get_fn(obj_tuple, 'name'), repeat=20))
    print(time_tuple)  # 0.16006120000000124

    print(time_tuple/time_slot)  # 1.1694481584580898  # The slot is almost 17% faster than NamedTuple on Windows. (Python 3.7.7)

Se você __dict__não estiver usando, escolha entre __slots__(maior desempenho e armazenamento) e NamedTuple(limpe para leitura e uso)

Você pode revisar este link ( Uso de slots ) para obter mais __slots__informações.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.