Estructuras tipo C en Python


447

¿Hay alguna manera de definir convenientemente una estructura tipo C en Python? Estoy cansado de escribir cosas como:

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

55
Semi-relacionados, los tipos de datos algebraicos serían absolutamente maravillosos, pero para usarlos bien, generalmente necesita una coincidencia de patrones.
Edward Z. Yang el

51
¿Hay algo malo con este método que no sea tedioso de escribir?
levesque

2
Puede encontrar útil dstruct: github.com/dorkitude/dstruct
Kyle Wild

10
@levesque más difícil de refactorizar sin errores tipográficos, más difícil de leer de un vistazo mientras se descrema el código, queMyStruct = namedtuple("MyStruct", "field1 field2 field3")
sam boosalis

1
pandas.Series(a=42).adebería hacerlo si eres un científico de datos ...
Mark Horvath

Respuestas:


341

Utilice una tupla con nombre , que se agregó a módulo de colecciones en la biblioteca estándar en Python 2.6. También es posible usar la tupla llamada Raymond Hettinger receta de si necesita admitir Python 2.4.

Es bueno para su ejemplo básico, pero también cubre un montón de casos extremos que podría encontrar más tarde también. Su fragmento anterior se escribiría como:

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

El tipo recién creado se puede usar así:

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

También puede usar argumentos con nombre:

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

164
... pero namedtuple es inmutable. El ejemplo en el OP es mutable.
mhowison

28
@mhowison - En mi caso, eso es solo una ventaja.
ArtOfWarfare

3
Buena solución ¿Cómo recorrerías una serie de estas tuplas? Supongo que los campos 1-3 tendrían que tener los mismos nombres en los objetos de tupla.
Michael Smith

2
namedtuple puede tener al menos cuatro argumentos, así que cómo podemos mapear la estructura con más miembros de datos con namedtuple correspondiente
Kapil

3
@Kapil: el segundo argumento para namedtuple debería ser una lista de los nombres de los miembros. Esa lista puede ser de cualquier longitud.
ArtOfWarfare

226

Actualización : clases de datos

Con la introducción de las clases de datos en Python 3.7 nos acercamos mucho.

El siguiente ejemplo es similar al ejemplo de NamedTuple a continuación, pero el objeto resultante es mutable y permite valores predeterminados.

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)

Esto funciona muy bien con el nuevo módulo de escritura en caso de que desee utilizar anotaciones de tipo más específicas.

¡He estado esperando desesperadamente esto! Si me preguntas, ¡las clases de datos y la nueva declaración NamedTuple , combinadas con el módulo de escritura , son una bendición!

Declaración mejorada de NamedTuple

Desde Python 3.6 se volvió bastante simple y hermoso (en mi humilde opinión), siempre que puedas vivir con inmutabilidad .

Se introdujo una nueva forma de declarar NamedTuples , que también permite anotaciones 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'))

66
Compañero, acabas de alegrarme el día - dictados inmutables - gracias: D
Dmitry Arkhipenko

10
El dataclassmódulo es nuevo en Python 3.7 pero puedes pip install dataclasses. Es el backport en Python 3.6. pypi.org/project/dataclasses/#description
Lavande

+1 para la declaración NamedTuple mejorada. La vieja manera era muy desagradable para leer si tuviera varias variables ...
gebbissimo

@Lavande ¿Puedo saber qué cambios importantes ocurrieron entre 3.6 y 3.7 que tiene que respaldar una versión menor ...?
Purple Ice

1
@PurpleIce Fue una implementación de PEP 557, clases de datos. @dataclassLos detalles están aquí: pypi.org/project/dataclasses/#description
Lavande

96

Puede usar una tupla para muchas cosas en las que usaría una estructura en C (algo así como coordenadas x, y o colores RGB, por ejemplo).

Para todo lo demás, puede usar el diccionario o una clase de utilidad como esta :

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

Creo que la discusión "definitiva" está aquí , en la versión publicada del Python Cookbook.


55
¿Una clase vacía haría lo mismo?
Kurt Liu

44
Tenga en cuenta si es nuevo en python: las tuplas son de solo lectura una vez creadas, a diferencia de las estructuras C
LeBleu el

2
@KurtLiu No, probablemente diríaTypeError: this constructor takes no arguments
Evgeni Sergeev

84

Quizás esté buscando estructuras sin constructores:

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

55
Lo que está haciendo aquí funciona, técnicamente, pero probablemente no sea inmediatamente evidente para muchos usuarios por qué funciona. Sus declaraciones bajo class Sample:no hacen nada de inmediato; establecen atributos de clase. Siempre se puede acceder a ellos como, por ejemplo Sample.name.
Channing Moore

22
Lo que realmente está haciendo es agregar propiedades de instancia a los objetos s1y s2en tiempo de ejecución. A menos que esté prohibido, puede agregar o modificar el nameatributo en cualquier instancia de cualquier clase en cualquier momento, ya sea que la clase tenga o no un nameatributo. Probablemente el mayor problema funcional al hacer esto es que diferentes instancias de la misma clase se comportarán de manera diferente dependiendo de si se ha configurado name. Si actualiza Sample.name, cualquier objeto sin una namepropiedad establecida explícitamente devolverá el nuevo name.
Channing Moore

2
Esto es lo más cercano a una estructura: 'clase' corta sin métodos, 'campos' (atributos de clase, lo sé) con valores predeterminados. Mientras no sea un tipo mutable (dict, list), estás bien. Por supuesto, puede golpear contra PEP-8 o verificaciones IDE "amigables" como "la clase no tiene método init " de PyCharm .
Tomasz Gandor

44
Experimenté el efecto secundario descrito por Channing Moore. No vale la economía de unas pocas selfpalabras clave y una línea de constructor si me preguntas. Agradecería que José pudiera editar su respuesta para agregar un mensaje de advertencia sobre el riesgo de compartir accidentalmente valores entre instancias.
Stéphane C.

@ChanningMoore: Traté de recrear el problema que estabas describiendo, pero fallé. ¿Podría presentar un ejemplo de trabajo mínimo donde aparece el problema?
gebbissimo

67

¿Qué tal un diccionario?

Algo como esto:

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

Entonces puede usar esto para manipular valores:

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

Y los valores no tienen que ser cadenas. Pueden ser prácticamente cualquier otro objeto.


34
Este también ha sido mi enfoque, pero siento que es peligroso precisamente porque un diccionario puede aceptar cualquier cosa por una clave. No habrá un error si configuré myStruct ["ffield"] cuando quise configurar myStruct ["campo"]. El problema puede (o no) hacerse evidente cuando estoy usando o reutilizando myStruct ["campo"] más adelante. Me gusta el enfoque de PabloG.
mobabo

El mismo problema existe con PabloG's. Intente agregar el siguiente código al suyo: pt3.w = 1 print pt3.w en un lenguaje con dictos, es mejor usarlos, especialmente para los objetos que se serializan, ya que puede usar automáticamente import json para guardarlos y otras bibliotecas de serialización siempre que no tenga extraños cosas dentro de tu dict. Los dictos son la solución para mantener separados los datos y la lógica y son mejores que las estructuras para las personas que no desean escribir funciones personalizadas de serialización y deserialización y no desean utilizar serializadores no portátiles como pickle.
Poikilos

27

dF: eso es genial ... No sabía que podía acceder a los campos en una clase usando dict.

Mark: las situaciones en las que desearía tener esto son precisamente cuando quiero una tupla pero nada tan "pesado" como un diccionario.

Puede acceder a los campos de una clase usando un diccionario porque los campos de una clase, sus métodos y todas sus propiedades se almacenan internamente usando dicts (al menos en CPython).

... Lo que nos lleva a su segundo comentario. Creer que los dictados de Python son "pesados" es un concepto extremadamente no pitonista. Y leer esos comentarios mata a mi Python Zen. Eso no es bueno.

Verá, cuando declara una clase, en realidad está creando una envoltura bastante compleja alrededor de un diccionario, por lo que, en todo caso, está agregando más sobrecarga que utilizando un diccionario simple. Una sobrecarga que, por cierto, no tiene sentido en ningún caso. Si está trabajando en aplicaciones críticas de rendimiento, use C o algo así.


55
# 1, Cython! = CPython. Creo que estabas hablando de CPython, la implementación de Python escrita en C, no Cython, un proyecto para compilar código Python en código C. Edité tu respuesta para arreglar eso. # 2, creo que cuando dijo que los dictos son pesados, se refería a la sintaxis. self['member']es 3 caracteres más largo que self.member, y esos personajes son relativamente poco amigables para la muñeca.
ArtOfWarfare

19

Puede subclasificar la estructura C que está disponible en la biblioteca estándar. El módulo ctypes proporciona una clase de estructura . El ejemplo de los 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

También me gustaría agregar una solución que use ranuras :

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

Definitivamente revise la documentación de las ranuras, pero una explicación rápida de las ranuras es que es la forma en que Python dice: "Si puede bloquear estos atributos y solo estos atributos en la clase de modo que se comprometa a no agregar ningún atributo nuevo una vez que la clase se crea una instancia (sí, puede agregar nuevos atributos a una instancia de clase, consulte el ejemplo a continuación), luego eliminaré la gran asignación de memoria que permite agregar nuevos atributos a una instancia de clase y usaré justo lo que necesito para estos atributos ranurados ".

Ejemplo de agregar atributos a la instancia de clase (por lo tanto, no usar ranuras):

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

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

Salida: 8

Ejemplo de intentar agregar atributos a la instancia de clase donde se usaron las ranuras:

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

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

Salida: AttributeError: el objeto 'Punto' no tiene atributo 'z'

Esto puede funcionar efectivamente como una estructura y usa menos memoria que una clase (como lo haría una estructura, aunque no he investigado exactamente cuánto). Se recomienda usar ranuras si va a crear una gran cantidad de instancias del objeto y no necesita agregar atributos. Un objeto de punto es un buen ejemplo de esto, ya que es probable que uno pueda instanciar muchos puntos para describir un conjunto de datos.


17

También puede pasar los parámetros de inicio a las variables de instancia por posición

# 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

77
La actualización por posición ignora el orden de declaración de los atributos y utiliza su clasificación alfabética. Entonces, si cambia el orden de las líneas en la Point3dStructdeclaración, Point3dStruct(5, 6)no funcionará como se esperaba. Es extraño que nadie haya escrito esto en los 6 años.
lapis

¿Podría agregar una versión de Python 3 a su increíble código? ¡Buen trabajo! Me gusta que tomes algo abstracto y lo hagas explícito con la segunda clase específica. Eso debería ser bueno para el manejo / captura de errores. Para Python 3, simplemente cambie print> print()y attrs[n]> next(attrs)(el filtro ahora es su propio objeto iterable y requiere next).
Jonathan Komar

10

Siempre que necesito un "objeto de datos instantáneos que también se comporta como un diccionario" (¡ no pienso en estructuras de C!), Pienso en este lindo truco:

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

Ahora solo puedes decir:

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

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

Perfectamente útil para aquellos momentos en que necesita una "bolsa de datos que NO es una clase", y para cuando las tuplas nombradas son incomprensibles ...


Yo uso pandas. Serie (a = 42) ;-)
Mark Horvath

8

Accede a la estructura C-Style en python de la siguiente manera.

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

si solo quieres 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)

si quieres crear una 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: en lugar del nombre 'cstruct', utilice su nombre de estructura en lugar de var_i, var_f, var_str, defina la variable miembro de su estructura.


3
¿Es esto diferente de lo que está en stackoverflow.com/a/3761729/1877426 ?
lagweezle

8

Algunas de las respuestas aquí son masivamente elaboradas. La opción más simple que he encontrado es (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

agregando más:

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

editar: Lo siento, no vi este ejemplo ya más abajo.


5

Esto puede ser un poco tarde, pero hice una solución usando Python Meta-Classes (versión de decorador a continuación también)

Cuando __init__se llama durante el tiempo de ejecución, toma cada uno de los argumentos y su valor y los asigna como variables de instancia a su clase. De esta manera, puede crear una clase de estructura sin tener que asignar cada valor manualmente.

Mi ejemplo no tiene verificación de errores, por lo que es más 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 

Aquí está en acción.

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


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

Lo publiqué en reddit y / u / matchu publicó una versión de decorador que es más limpia. Te animo a que lo uses a menos que quieras expandir la versión de la metaclase.

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

Maldición: hoy pasé dos horas escribiendo mi propio decorador para hacer esto y luego encontré esto. De todos modos, publicar el mío porque maneja los valores predeterminados mientras que el tuyo no. stackoverflow.com/a/32448434/901641
ArtOfWarfare

+1 por mencionar func_code. Comencé a cavar en esa dirección y encontré muchas cosas interesantes allí.
wombatonfire

5

Escribí un decorador que puede usar en cualquier método para que todos los argumentos pasados ​​o los valores predeterminados se asignen a la instancia.

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

Una rápida demostración. Tenga en cuenta que uso un argumento posicional a, uso el valor predeterminado para by un argumento con nombre c. Luego imprimo las 3 referencias self, para mostrar que se han asignado correctamente antes de ingresar el 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')

Tenga en cuenta que mi decorador debería funcionar con cualquier método, no solo __init__.


5

No veo esta respuesta aquí, así que creo que la agregaré ya que estoy inclinado Python en este momento y acabo de descubrirlo. El tutorial de Python (Python 2 en este caso) da el siguiente ejemplo simple y efectivo:

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

Es decir, se crea un objeto de clase vacío, luego se instancia y los campos se agregan dinámicamente.

La ventaja de esto es realmente simple. La desventaja es que no es particularmente autodocumentado (los miembros previstos no están listados en ninguna parte de la "definición" de clase), y los campos no definidos pueden causar problemas cuando se accede a ellos. Esos dos problemas pueden resolverse mediante:

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

Ahora, de un vistazo, al menos puede ver qué campos esperará el programa.

Ambos son propensos a errores tipográficos, john.slarly = 1000tendrán éxito. Aún así, funciona.


4

Aquí hay una solución que usa una clase (nunca instanciada) para almacenar datos. Me gusta que de esta manera se trate de escribir muy poco y no requiera ningún paquete adicional, etc.

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

Puede agregar más campos más tarde, según sea necesario:

myStruct.field3 = 3

Para obtener los valores, se accede a los campos como de costumbre:

>>> myStruct.field1
'one'

2

Personalmente, también me gusta esta variante. Extiende la respuesta de @ 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__)

Admite dos modos de inicialización (que se pueden combinar):

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

Además, se imprime mejor:

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

2

La siguiente solución a una estructura está inspirada en la implementación de namedtuple y algunas de las respuestas anteriores. Sin embargo, a diferencia del namedtuple, es mutable, en sus valores, pero al igual que la estructura de estilo c inmutable en los nombres / atributos, que no es una clase o dict 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

Hay un paquete de Python exactamente para este propósito. ver cstruct2py

cstruct2pyes una biblioteca de python pura para generar clases de python a partir de código C y usarlas para empacar y desempaquetar datos. La biblioteca puede analizar los encabezados C (estructuras, uniones, enumeraciones y declaraciones de matrices) y emularlos en Python. Las clases pitónicas generadas pueden analizar y empaquetar los datos.

Por ejemplo:

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"

Cómo utilizar

Primero necesitamos generar las estructuras pitónicas:

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

Ahora podemos importar todos los nombres del código C:

parser.update_globals(globals())

También podemos hacer eso directamente:

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

Usando tipos y definiciones del 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)

El resultado será:

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

Clon

Para cstruct2pyejecutar clon :

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

0

Creo que el diccionario de estructura Python es adecuado para este requisito.

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

0

https://stackoverflow.com/a/32448434/159695 no funciona en Python3.

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

Y lo extiendo para agregar valores predeterminados.

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

Si no tiene un 3.7 para @dataclass y necesita mutabilidad, el siguiente código podría funcionar para usted. Es bastante autodocumentado y compatible con IDE (autocompletado), evita escribir dos veces, es fácilmente extensible y es muy simple probar que todas las variables de instancia están 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

Aquí hay un truco rápido y sucio:

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

¿Cómo funciona? Simplemente reutilice la clase incorporada Warning(derivada de Exception) y úsela como si fuera su propia clase definida.

Lo bueno es que no necesita importar o definir nada primero, que "Advertencia" es un nombre corto, y que también deja en claro que está haciendo algo sucio que no debe usarse en otro lugar que no sea un pequeño script suyo.

Por cierto, intenté encontrar algo aún más simple, ms = object()pero no pude (este último ejemplo no funciona). Si tienes uno, estoy interesado.


0

La mejor manera de hacer esto fue utilizar una clase de diccionario personalizada como se explica en esta publicación: https://stackoverflow.com/a/14620633/8484485

Si se necesita el soporte de autocompletado de iPython, simplemente defina la función dir () de esta manera:

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

Luego define su pseudoestructura de la siguiente manera: (esta está anidada)

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

Luego puede acceder a los valores dentro de my_struct de esta manera:

print(my_struct.com1.inst)

=>[5]


0

NamedTuple es cómodo. pero allí nadie comparte el rendimiento y el almacenamiento.

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)

Si __dict__no está usando, elija entre __slots__(mayor rendimiento y almacenamiento) y NamedTuple(claro para leer y usar)

Puede revisar este enlace ( Uso de ranuras ) para obtener más __slots__información.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.