Strutture simili a C in Python


447

C'è un modo per definire comodamente una struttura simile a C in Python? Sono stanco di scrivere cose come:

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

5
A livello semi-correlato, i tipi di dati algebrici sarebbero assolutamente meravigliosi, ma per usarli bene di solito è necessaria la corrispondenza dei modelli.
Edward Z. Yang,

51
C'è qualcosa di sbagliato in questo metodo se non quello che è noioso da scrivere?
levesque,

2
Puoi trovare utile dstruct: github.com/dorkitude/dstruct
Kyle Wild

10
@levesque più difficile ripensare senza errori di battitura, più difficile da leggere a colpo d'occhio durante lo skimming del codice, rispetto aMyStruct = namedtuple("MyStruct", "field1 field2 field3")
sam boosalis

1
pandas.Series(a=42).adovresti farlo se sei un esperto di dati ...
Mark Horvath il

Risposte:


341

Utilizzare una tupla con nome , che è stata aggiunta al modulo di raccolte nella libreria standard in Python 2.6. È anche possibile utilizzare la ricetta della tupla nominata di Raymond Hettinger se è necessario supportare Python 2.4.

È utile per il tuo esempio di base, ma copre anche un sacco di casi limite che potresti incontrare anche in un secondo momento. Il tuo frammento sopra sarebbe scritto come:

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

Il tipo appena creato può essere utilizzato in questo modo:

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

Puoi anche usare argomenti con nome:

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

164
... ma namedtuple è immutabile. L'esempio nell'OP è mutabile.
mhowison,

28
@mhowison - Nel mio caso, questo è solo un vantaggio.
ArtOfWarfare il

3
Bella soluzione. Come passeresti attraverso una serie di queste tuple? Suppongo che i campi 1-3 dovrebbero avere gli stessi nomi su tutti gli oggetti tupla.
Michael Smith,

2
namedtuple può avere almeno quattro argomenti, quindi come possiamo mappare la struttura con più membri di dati con corrispondenti nametuple
Kapil

3
@Kapil - Il secondo argomento di namedtuple dovrebbe essere un elenco dei nomi dei membri. Tale elenco può essere di qualsiasi lunghezza.
ArtOfWarfare il

228

Aggiornamento : classi di dati

Con l'introduzione delle Classi di dati in Python 3.7 ci avviciniamo molto.

Il seguente esempio è simile al namedtuple esempio sotto, ma l'oggetto risultante è mutabile e permette ai valori predefiniti.

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)

Funziona bene con il nuovo modulo di digitazione nel caso in cui desideri utilizzare annotazioni di tipo più specifiche.

Lo stavo aspettando disperatamente! Se me lo chiedi, le classi di dati e la nuova dichiarazione di NamedTuple , combinate con il modulo di digitazione, sono una manna dal cielo!

Dichiarazione NamedTuple migliorata

Da Python 3.6 è diventato abbastanza semplice e bello (IMHO), purché tu possa vivere con l' immutabilità .

È stato introdotto un nuovo modo di dichiarare NamedTuples , che consente anche le annotazioni dei tipi :

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
Amico, mi hai appena reso felice - immutabili dicts - grazie: D
Dmitry Arkhipenko l'

10
Il dataclassmodulo è nuovo in Python 3.7 ma puoi farlo pip install dataclasses. È il backport su Python 3.6. pypi.org/project/dataclasses/#description
Lavande

+1 per una migliore dichiarazione di NamedTuple. Il vecchio modo era davvero spiacevole da leggere se avessi diverse variabili ...
gebbissimo,

@Lavande Posso sapere quali sono state le ultime modifiche tra 3.6 e 3.7 che è necessario eseguire il backport di una versione minore ...?
Purple Ice,

1
@PurpleIce Era un'implementazione di PEP 557, Classi di dati @dataclassI dettagli sono qui: pypi.org/project/dataclasses/#description
Lavande

96

Puoi usare una tupla per molte cose in cui useresti uno struct in C (qualcosa come x, y coordinate o colori RGB per esempio).

Per tutto il resto puoi usare il dizionario o una classe di utilità come questa :

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

Penso che la discussione "definitiva" sia qui , nella versione pubblicata del Cookbook di Python.


5
Una classe vuota farebbe lo stesso?
Kurt Liu,

44
Nota se non conosci Python: le tuple sono di sola lettura una volta create, a differenza delle strutture C
LeBleu,

2
@KurtLiu No, probabilmente direbbeTypeError: this constructor takes no arguments
Evgeni Sergeev,

84

Forse stai cercando Strotti senza costruttori:

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
Ciò che stai facendo qui funziona, tecnicamente, ma probabilmente non è immediatamente chiaro a molti utenti perché funzioni. Le tue dichiarazioni sotto class Sample:non fanno immediatamente nulla; impostano gli attributi di classe. Quelli sono sempre accessibili come ad es Sample.name.
Channing Moore,

22
Quello che stai effettivamente facendo è aggiungere proprietà dell'istanza agli oggetti s1e s2in fase di esecuzione. Se non diversamente proibito, è possibile aggiungere o modificare l' nameattributo su qualsiasi istanza di qualsiasi classe in qualsiasi momento, indipendentemente dal fatto che la classe abbia o meno un nameattributo. Probabilmente il più grande problema funzionale nel fare questo è che diverse istanze della stessa classe si comporteranno in modo diverso a seconda che tu abbia impostato name. Se si aggiorna Sample.name, tutti gli oggetti senza una nameproprietà impostata esplicitamente restituiranno il nuovo name.
Channing Moore,

2
Questo è il più vicino possibile a una struttura - breve "classe" senza metodi, "campi" (attributi di classe, lo so) con valori predefiniti. Finché non è un tipo mutabile (dict, list), stai bene. Ovviamente, puoi colpire PEP-8 o controlli IDE "amichevoli" come la "classe di PyCharm non ha metodo init ".
Tomasz Gandor,

4
Ho sperimentato l'effetto collaterale descritto da Channing Moore. Non vale l'economia di poche selfparole chiave e una linea di costruzione se me lo chiedi. Gradirei se Jose potesse modificare la sua risposta per aggiungere un messaggio di avvertimento sul rischio di condivisione accidentale di valori tra le istanze.
Stéphane C.,

@ChanningMoore: ho provato a ricreare il problema che stavi descrivendo, ma non ci sono riuscito. Potresti presentare un esempio minimo di lavoro in cui si presenta il problema?
gebbissimo,

67

Che ne dici di un dizionario?

Qualcosa come questo:

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

Quindi puoi usarlo per manipolare i valori:

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

E i valori non devono essere stringhe. Possono essere praticamente qualsiasi altro oggetto.


34
Anche questo è stato il mio approccio, ma mi sembra pericoloso proprio perché un dizionario può accettare qualsiasi cosa per una chiave. Non ci sarà un errore se imposto myStruct ["ffield"] quando intendevo impostare myStruct ["field"]. Il problema potrebbe (o non potrebbe) manifestarsi quando sto usando o riutilizzando myStruct ["field"] in seguito. Mi piace l'approccio di PabloG.
mobabo,

Lo stesso problema esiste con PabloG. Prova ad aggiungere il seguente codice al suo: pt3.w = 1 print pt3.w In una lingua con dicts, è meglio usarli, specialmente per gli oggetti in fase di serializzazione, poiché puoi usare automaticamente import json per salvarli e altre librerie di serializzazione purché tu non abbia strani roba dentro il tuo dict. Dicts sono la soluzione per mantenere separati dati e logica e sono migliori delle strutture per le persone che non vogliono scrivere funzioni di serializzazione personalizzate e di non serializzazione e non vogliono usare serializzatori non portatili come pickle.
Poikilos,

27

dF: è piuttosto bello ... Non sapevo che avrei potuto accedere ai campi in una classe usando dict.

Mark: le situazioni che vorrei avere questo sono proprio quando voglio una tupla ma niente di "pesante" come un dizionario.

Puoi accedere ai campi di una classe usando un dizionario perché i campi di una classe, i suoi metodi e tutte le sue proprietà sono memorizzati internamente usando dicts (almeno in CPython).

... Il che ci porta al tuo secondo commento. Credere che i cubetti di Python siano "pesanti" è un concetto estremamente non pitonistico. E leggere tali commenti uccide il mio Python Zen. Questo non è buono.

Vedi, quando dichiari una classe stai effettivamente creando un wrapper piuttosto complesso attorno a un dizionario - quindi, semmai, stai aggiungendo più sovraccarico che usando un semplice dizionario. Un sovraccarico che, a proposito, è insignificante in ogni caso. Se stai lavorando su applicazioni critiche per le prestazioni, usa C o qualcosa del genere.


5
# 1, Cython! = CPython. Penso che stavi parlando di CPython, l'implementazione di Python scritta in C, non Cython, un progetto per incrociare la compilazione del codice Python in codice C. Ho modificato la tua risposta per risolverlo. # 2, penso che quando ha detto che i dadi sono pesanti, si riferiva alla sintassi. self['member']è più lungo di 3 caratteri self.membere quei personaggi sono relativamente poco amichevoli per il polso.
ArtOfWarfare

19

È possibile sottoclassare la struttura C disponibile nella libreria standard. Il modulo ctypes fornisce una classe Structure . L'esempio dai documenti:

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

Vorrei anche aggiungere una soluzione che utilizza gli slot :

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

Controlla sicuramente la documentazione per gli slot, ma una rapida spiegazione degli slot è che è il modo di Python di dire: "Se riesci a bloccare questi attributi e solo questi attributi nella classe in modo da impegnarti a non aggiungere nuovi attributi una volta che la classe è istanziato (sì, è possibile aggiungere nuovi attributi a un'istanza di classe, vedere l'esempio di seguito), quindi eliminerò l'ampia allocazione di memoria che consente di aggiungere nuovi attributi a un'istanza di classe e utilizzerò esattamente ciò di cui ho bisogno per questi attributi con slot ".

Esempio di aggiunta di attributi all'istanza di classe (quindi non utilizzo di slot):

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

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

Uscita: 8

Esempio di tentativo di aggiungere attributi all'istanza di classe in cui sono stati utilizzati gli slot:

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

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

Output: AttributeError: l'oggetto 'Point' non ha attributo 'z'

Questo può effettivamente funzionare come una struttura e usa meno memoria di una classe (come farebbe una struttura, anche se non ho studiato esattamente quanto). Si consiglia di utilizzare gli slot se si sta creando una grande quantità di istanze dell'oggetto e non è necessario aggiungere attributi. Un oggetto punto ne è un buon esempio in quanto è probabile che si possano istanziare molti punti per descrivere un set di dati.


17

Puoi anche passare i parametri init alle variabili di istanza per posizione

# 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
L'aggiornamento per posizione ignora l'ordine delle dichiarazioni degli attributi e utilizza invece il loro ordinamento alfabetico. Quindi, se si modifica l'ordine delle righe nella Point3dStructdichiarazione, Point3dStruct(5, 6)non funzionerà come previsto. È strano che nessuno l'abbia scritto in tutti e 6 gli anni.
lapis,

Potresti aggiungere una versione di Python 3 al tuo fantastico codice? Ottimo lavoro! Mi piace che tu prenda qualcosa di astratto e lo renda esplicito con la seconda classe specifica. Ciò dovrebbe essere utile per la gestione / rilevazione degli errori. Per Python 3, basta cambiare print> print(), e attrs[n]> next(attrs)(il filtro ora è il suo oggetto iterabile e richiede next).
Jonathan Komar

10

Ogni volta che ho bisogno di un "oggetto dati istantaneo che si comporti anche come un dizionario" ( non penso alle strutture C!), Penso a questo hack carino:

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

Ora puoi semplicemente dire:

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

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

Perfettamente utile per quelle volte in cui è necessario un "data bag NON di classe" e per quando le coppie con nome sono incomprensibili ...


Uso panda.Series (a = 42) ;-)
Mark Horvath il

8

Si accede alla struttura C-Style in Python nel modo seguente.

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

se vuoi solo usare l'oggetto di 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 si desidera creare una matrice di oggetti di 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: anziché il nome 'cstruct', si prega di utilizzare il nome della struttura anziché var_i, var_f, var_str, definire la variabile membro della struttura.


3
È diverso da ciò che è in stackoverflow.com/a/3761729/1877426 ?
Lagweezle,

8

Alcune risposte qui sono estremamente elaborate. L'opzione più semplice che ho trovato è (da: http://norvig.com/python-iaq.html ):

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

Inizializzazione:

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

aggiungendo altro:

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

modifica: mi dispiace non ho visto questo esempio già più in basso.


5

Potrebbe essere un po 'tardi, ma ho creato una soluzione usando le Meta-Classi di Python (anche la versione decoratore di seguito).

Quando __init__viene chiamato durante il runtime, prende ciascuno degli argomenti e il loro valore e li assegna come variabili di istanza alla tua classe. In questo modo è possibile creare una classe simile alla struttura senza dover assegnare manualmente tutti i valori.

Il mio esempio non ha errori nel controllo, quindi è più facile da seguire.

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 

Qui è in azione.

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


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

L'ho pubblicato su reddit e / u / matchu ha pubblicato una versione decoratore che è più pulita. Ti incoraggio a usarlo a meno che tu non voglia espandere la versione della 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
>>> 

Dannazione - Ho passato due ore oggi a scrivere il mio decoratore per farlo e poi l'ho trovato. Ad ogni modo, pubblicando il mio perché gestisce i valori predefiniti mentre il tuo no. stackoverflow.com/a/32448434/901641
ArtOfWarfare

+1 per menzionare func_code. Iniziò a scavare in quella direzione e lì trovò molte cose interessanti.
Wombatonfire,

5

Ho scritto un decoratore che puoi usare su qualsiasi metodo per farlo in modo che tutti gli argomenti passati, o eventuali valori predefiniti, siano assegnati all'istanza.

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 rapida dimostrazione. Si noti che utilizzo un argomento posizionale a, uso il valore predefinito per be un argomento denominato c. Quindi stampo tutti e 3 i riferimenti self, per mostrare che sono stati assegnati correttamente prima di inserire il metodo.

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

Nota che il mio decoratore dovrebbe funzionare con qualsiasi metodo, non solo __init__.


5

Non vedo questa risposta qui, quindi immagino che la aggiungerò poiché sto appoggiando Python in questo momento e l'ho appena scoperto. Il tutorial di Python (Python 2 in questo caso) fornisce il seguente esempio semplice ed efficace:

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

Ossia, viene creato un oggetto di classe vuoto, quindi istanziato e i campi vengono aggiunti in modo dinamico.

L'aspetto positivo di questo è davvero semplice. Il rovescio della medaglia è che non è particolarmente auto-documentato (i membri previsti non sono elencati da nessuna parte nella classe "definizione") e i campi non impostati possono causare problemi quando si accede. Questi due problemi possono essere risolti:

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

Ora a colpo d'occhio puoi almeno vedere quali campi si aspetta il programma.

Entrambi sono inclini a errori di battitura, john.slarly = 1000avrà successo. Funziona ancora.


4

Ecco una soluzione che utilizza una classe (mai istanziata) per conservare i dati. Mi piace che in questo modo implichi pochissima digitazione e non richieda pacchetti aggiuntivi ecc.

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

Puoi aggiungere altri campi in un secondo momento, se necessario:

myStruct.field3 = 3

Per ottenere i valori, si accede ai campi come al solito:

>>> myStruct.field1
'one'

2

Personalmente, mi piace anche questa variante. Estende la risposta di @ 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__)

Supporta due modalità di inizializzazione (che possono essere miscelate):

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

Inoltre, stampa meglio:

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

2

La seguente soluzione a una struttura si ispira all'implementazione di namedtuple e ad alcune delle risposte precedenti. Tuttavia, a differenza della namedtuple, è mutabile, nei suoi valori, ma come la struttura in stile c immutabile nei nomi / attributi, che non è una classe normale o un dict.

_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

Esiste un pacchetto python esattamente per questo scopo. vedi cstruct2py

cstruct2pyè una libreria Python pura per generare classi Python dal codice C e usarle per comprimere e decomprimere i dati. La libreria può analizzare C headre (dichiarazioni di strutture, sindacati, enum e array) ed emularle in Python. Le classi pythonic generate possono analizzare e comprimere i dati.

Per esempio:

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"

Come usare

Per prima cosa dobbiamo generare le strutture pitoniche:

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

Ora possiamo importare tutti i nomi dal codice C:

parser.update_globals(globals())

Possiamo anche farlo direttamente:

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

Utilizzo di tipi e definizioni dal codice 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)

L'output sarà:

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

Clone

Per clone cstruct2pyrun:

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

0

Penso che il dizionario della struttura di Python sia adatto a questo requisito.

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

0

https://stackoverflow.com/a/32448434/159695 non funziona in Python3.

https://stackoverflow.com/a/35993/159695 funziona in Python3.

E lo estendo per aggiungere valori predefiniti.

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 non hai un 3.7 per @dataclass e hai bisogno di mutabilità, il seguente codice potrebbe funzionare per te. È abbastanza auto-documentante e IDE-friendly (completamento automatico), impedisce di scrivere due volte le cose, è facilmente estensibile ed è molto semplice verificare che tutte le variabili di istanza siano completamente inizializzate:

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

Ecco un trucco rapido e sporco:

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

Come funziona? Riutilizza semplicemente la classe builtin Warning(derivata da Exception) e la usa come se fosse la tua classe definita.

I punti positivi sono che non è necessario importare o definire prima nulla, che "Avvertenza" è un nome breve e che è anche chiaro che stai facendo qualcosa di sporco che non dovrebbe essere usato altrove che un tuo piccolo script.

A proposito, ho provato a trovare qualcosa di ancora più semplice ms = object()ma non ci sono riuscito (quest'ultimo esempio non funziona). Se ne hai uno, sono interessato.


0

Il modo migliore che ho trovato per fare questo era usare una classe di dizionario personalizzata come spiegato in questo post: https://stackoverflow.com/a/14620633/8484485

Se è necessario il supporto per il completamento automatico di iPython, è sufficiente definire la funzione dir () in questo modo:

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

Quindi definisci la tua pseudo strutt in questo modo: (questa è nidificata)

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

È quindi possibile accedere ai valori all'interno di my_struct in questo modo:

print(my_struct.com1.inst)

=>[5]


0

NamedTuple è comodo. ma nessuno condivide le prestazioni e l'archiviazione.

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 __dict__non lo si utilizza, scegliere tra __slots__(prestazioni e archiviazione più elevate) eNamedTuple (deselezionare per la lettura e l'uso)

È possibile rivedere questo collegamento ( Utilizzo di slot ) per ottenere ulteriori __slots__informazioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.