estruturas C-like em Python

votos
304

Existe uma maneira de definir convenientemente uma estrutura C-like 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
Publicado 30/08/2008 em 15:33
fonte usuário
Em outras línguas...                            


22 respostas

votos
289

Use uma tupla chamado , que foi adicionado ao módulo de coleções na biblioteca padrão em Python 2.6. Também é possível usar de Raymond Hettinger tupla chamado receita, se você precisa para suportar Python 2.4.

É bom para o seu exemplo básico, mas também abrange um monte de casos de ponta que você pode executar em 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 como este:

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

Você também pode usar argumentos nomeados:

m = MyStruct(field1="foo", field2="bar", field3="baz")
Respondeu 30/08/2008 em 16:18
fonte usuário

votos
91

Você pode usar uma tupla para um monte de coisas em que você usaria um struct em C (algo como coordenadas X, Y ou cores RGB, por exemplo).

Para tudo o resto você pode usar dicionário, ou uma classe utilitário como este :

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

Eu acho que a discussão "definitiva" é aqui , na versão publicada do Python Cookbook.

Respondeu 30/08/2008 em 15:38
fonte usuário

votos
75

Talvez você está 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
Respondeu 21/09/2010 em 16:15
fonte usuário

votos
52

Como cerca de 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 têm de ser strings. Eles podem ser praticamente qualquer outro objeto.

Respondeu 30/08/2008 em 15:35
fonte usuário

votos
35

Atualização : Classes de dados

Com a introdução de classes de dados em Python 3.7 que chegar muito perto.

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

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

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

Isso funciona muito bem com a nova digitação módulo no caso de você querer usar anotações de tipo mais específicas.

Eu estive esperando desesperadamente por isso! Se você me perguntar, Classes de dados ea nova NamedTuple declaração, combinados com a digitação módulo são uma dádiva de Deus!

Melhorou declartaion NamedTuple

Desde Python 3.6 tornou-se bastante simples e bonito (IMHO), contanto que você pode viver com a imutabilidade.

Uma nova forma de declarar NamedTuples foi introduzido, o que permite anotações de tipo assim:

from typing import NamedTuple

class MyStruct(NamedTuple):
    my_string: str
    my_int: int
    my_list: list
    my_dict: dict
    my_foo: Foo

Usá-lo como este:

my_item = MyStruct(
    my_string='foo',
    my_int=0,
    my_list=['bar'],
    my_dict={'baz': 'qux'},
    my_foo=Foo('bar')
)

Ou como isso se você realmente quer:

my_item = MyStruct('foo', 0, ['bar'], {'baz': 'qux'}, Foo('bar'))
Respondeu 31/07/2017 em 22:54
fonte usuário

votos
23

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

Mark: as situações que eu gostaria de ter esta são precisamente quando eu quero uma tupla mas nada tão "pesado", como 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 armazenadas internamente usando dicts (pelo menos em CPython).

... O que nos leva para o seu segundo comentário. Acreditando que dicts Python são "pesado" é um conceito extremamente não-pythonistic. E ler tais comentários mata meu Python Zen. Isso não é bom.

Você vê, quando você declarar uma classe que você está criando realmente um invólucro bastante complexo em torno de um dicionário - por isso, se alguma coisa, você está adicionando mais sobrecarga do que usando um dicionário simples. Uma sobrecarga que, pela maneira, não tem significado em qualquer caso. Se você está trabalhando em aplicações críticas de desempenho, utilize C ou algo assim.

Respondeu 30/08/2008 em 16:20
fonte usuário

votos
14

Você também pode passar os parâmetros de inicialização para as variáveis ​​de instância por 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
Respondeu 30/08/2008 em 16:53
fonte usuário

votos
12

Você pode subclasse a estrutura C que está disponível na biblioteca padrão. O ctypes módulo fornece uma classe Estrutura . O exemplo a partir 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
>>>
Respondeu 25/06/2015 em 23:50
fonte usuário

votos
9

Sempre que eu preciso um "objeto instantâneo de dados que também se comporta como um dicionário" (I não ! Pensar em estruturas C), penso este hack bonito:

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

Agora você pode simplesmente 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 namedtuples são incompreensíveis ...

Respondeu 13/09/2013 em 18:40
fonte usuário

votos
6

Você acessa C-Style struct em python no seguinte caminho.

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

se você só quer usar objeto de 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', por favor use o seu nome struct em vez de var_i, var_f, var_str, defina variável de membro da sua estrutura.

Respondeu 09/11/2014 em 07:37
fonte usuário

votos
5

Algumas das respostas aqui são maciçamente elaborado. A opção mais simples que eu encontrei é (a partir 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)

Inicialização:

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

adicionando mais:

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

edit: Desculpe não vi este exemplo já mais abaixo.

Respondeu 05/08/2017 em 00:39
fonte usuário

votos
4

Eu escrevi um decorador que você pode usar em qualquer método para fazer com que todos os argumentos passados, ou quaisquer padrões, são atribuídos para a 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. Note que eu uso um argumento posicional a, utilize o valor padrão para b, e um argumento nomeado c. Eu, então, imprimir todos os 3 referência self, para mostrar que eles foram devidamente atribuídas antes que o método é inserido.

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')

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

Respondeu 08/09/2015 em 02:38
fonte usuário

votos
3

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

Quando __init__é chamado durante tempo de execução, que agarra cada um dos argumentos e seu valor e atribui-los como exemplo variáveis para sua classe. Desta forma, você pode fazer uma classe struct-como sem ter que atribuir a cada valor manualmente.

Meu exemplo não tem verificação de erros, por isso é mais fácil de seguir.

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. Eu encorajá-lo a usá-lo a menos que você deseja expandir a versão metaclass.

>>> 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
>>> 
Respondeu 23/03/2015 em 14:32
fonte usuário

votos
1

Há um pacote de python exatamente para esta finalidade. veja cstruct2py

cstruct2pyé uma biblioteca python puro para gerar classes de python a partir do código C e usá-los para embalar e desembalar dados. A biblioteca pode analisar headres C (estruturas, sindicatos, enums, e arrays declarações) e imitá-los em python. As classes Pythonic gerados podem analisar e embalar 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 Pythonic:

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())

Nós também pode fazer isso diretamente:

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

Utilizando tipos e define a partir 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 clone cstruct2pyprazo:

git clone https://github.com/st0ky/cstruct2py.git --recursive
Respondeu 11/12/2018 em 09:34
fonte usuário

votos
1

Eu também gostaria de adicionar uma solução que utiliza ranhuras :

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

Definitivamente verificar a documentação para ranhuras, mas uma explicação rápida de fendas é que é a maneira de python de dizer: "Se você pode bloquear esses atributos e apenas estes atributos para a classe de tal forma que se comprometer que não irá adicionar novos atributos uma vez que a classe é instanciado (sim, você pode adicionar novos atributos para uma instância de classe, veja o exemplo abaixo), então vou acabar com a grande alocação de memória que permite a adição de novos atributos para uma instância de classe e usar apenas o que eu preciso para estes fendidos atributos".

Exemplo de adição de atributos a instância de classe (assim não usando ranhuras):

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 tentar adicionar atributos a instância de classe, onde foi usado ranhuras:

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: objeto 'Ponto' tem nenhum atributo 'z'

Isso pode efetivamente funciona como um struct e usa menos memória do que uma classe (como um struct faria, embora eu não tenha pesquisado exatamente quanto). Recomenda-se usar ranhuras se você estará criando uma grande quantidade de instâncias do objeto e não precisa adicionar atributos. Um objeto de ponto é um bom exemplo disso, pois é provável que se pode instanciar muitos pontos para descrever um conjunto de dados.

Respondeu 12/04/2018 em 06:17
fonte usuário

votos
1

A solução que se segue para um struct é inspirado pela implementação namedtuple e algumas das respostas anteriores. No entanto, ao contrário do namedtuple é mutável, em um de seus valores, mas como o struct c-style imutável nos nomes / atributos que uma classe normal ou dict não é.

_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'
Respondeu 29/03/2018 em 16:25
fonte usuário

votos
1

Pessoalmente, eu gosto desta variante também. Estende-se resposta da @ 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 pode ser misturado):

# 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 mais agradável:

print(mystruct2)
# Prints: {'field3': 3, 'field1': 1, 'field2': 2}
Respondeu 06/11/2017 em 15:38
fonte usuário

votos
1

Eu não vejo esta resposta aqui, então eu acho que eu vou adicioná-lo desde que eu estou inclinado Python agora e só descobri-lo. A tutorial Python (Python 2 neste caso) dá 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 vazia é criada, então instanciado, e os campos são adicionados dinamicamente.

O up-side para isso é a sua realmente simples. A desvantagem é que não é particularmente auto-documentando (os membros pretendidos não estão listados em qualquer lugar na classe "definição"), e os campos unset pode causar problemas quando acessado. Esses dois problemas podem ser resolvidos por:

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

Agora à primeira vista você pode pelo menos ver quais campos o programa estará esperando.

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

Respondeu 08/11/2016 em 14:06
fonte usuário

votos
0

Aqui está uma solução que utiliza uma classe (nunca instanciado) para armazenar dados. Eu gosto que esta forma envolve muito pouco de digitação e não necessita de qualquer pacote adicional etc.

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

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

myStruct.field3 = 3

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

>>> myStruct.field1
'one'
Respondeu 09/04/2019 em 11:10
fonte usuário

votos
0

Se você não tem um 3,7 para @dataclass e precisa de mutabilidade, o seguinte código pode funcionar para você. É completamente auto-documentado e IDE-friendly (auto-complete), impede a escrever as coisas duas vezes, é facilmente extensível e é muito simples para testar que todas as variáveis ​​de instância são completamente inicializado:

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)
Respondeu 28/01/2019 em 11:27
fonte usuário

votos
0

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

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

E eu estende-lo 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')
Respondeu 30/10/2017 em 13:40
fonte usuário

votos
0

Acho dicionário estrutura Python é adequado para esse requisito.

d = dict{}
d[field1] = field1
d[field2] = field2
d[field2] = field3
Respondeu 08/07/2017 em 19:17
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more