C-achtige structuren in Python

Is er een manier om handig een C-achtige structuur in Python te definiëren? Ik ben het beu om dingen te schrijven zoals:

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

1, Autoriteit 100%

Gebruik een genaamd Tuple , die is toegevoegd aan de Collections-module in de standaardbibliotheek in Python 2.6. Het is ook mogelijk om Raymond Hettingter’s genaamd Tuple recept te gebruiken als u Python 2.4 moet ondersteunen.

Het is leuk voor uw basisvoorbeeld, maar dekt ook een stel randgevallen die u ook later kunt rennen. Uw fragment hierboven zou worden geschreven als:

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

Het nieuw gemaakte type kan als volgt worden gebruikt:

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

U kunt ook genoemde argumenten gebruiken:

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

2, Autoriteit 95%

Update : Data Classes

Met de introductie van Data-klassen in Python 3.7 We komen heel dichtbij.

Het volgende voorbeeld is vergelijkbaar met het voorbeeld NamedTuplehieronder, maar het resulterende object is veranderbaaren staat standaardwaarden toe.

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)

Dit werkt goed samen met de nieuwe module typenvoor het geval u deze wilt gebruiken meer specifieke typeannotaties.

Ik heb hier wanhopig op gewacht! Als je het mij vraagt, zijn Data Classesen de nieuwe NamedTupledeclaratie, gecombineerd met de typingmodule een uitkomst!

Verbeterde NamedTuple-declaratie

Sinds Python 3.6is het vrij eenvoudig en mooi geworden (IMHO), zolang je kunt leven met onveranderlijkheid.

Een nieuwe manier om NamedTuples te declarerenwerd geïntroduceerd, die staat ook type annotatiestoe:

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

Antwoord 3, autoriteit 26%

Je kunt een tuple voor veel dingen gebruiken waar je een struct in C zou gebruiken (zoiets als x,y-coördinaten of RGB-kleuren bijvoorbeeld).

Voor al het andere kunt u woordenboek gebruiken of een hulpprogramma zoals deze :

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

Ik denk dat de “definitieve” discussie Hier , in de gepubliceerde versie van het Cookbook Python.


4, Autoriteit 23%

Misschien ben je op zoek naar structuren zonder constructeurs:

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, Autoriteit 19%

Hoe zit het met een woordenboek?

zoiets:

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

Dan kunt u dit gebruiken om waarden te manipuleren:

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

En de waarden hoeven geen snaren te zijn. Ze kunnen vrijwel elk ander object zijn.


6, Autoriteit 7%

dF: dat is best cool… ik niet
weet dat ik toegang heb tot de velden in
een klas die dict gebruikt.

Mark: de situaties die ik graag had gehad
dit is precies wanneer ik een tuple wil
maar niets zo “zwaar” als een
woordenboek.

Je hebt toegang tot de velden van een klasse met behulp van een woordenboek, omdat de velden van een klasse, zijn methoden en alle eigenschappen intern worden opgeslagen met behulp van dictaten (tenminste in CPython).

…Dat brengt ons bij uw tweede opmerking. Geloven dat Python-dicts “zwaar” zijn, is een extreem niet-pythonistisch concept. En het lezen van dergelijke opmerkingen doodt mijn Python Zen. Dat is niet goed.

Zie je, wanneer je een klasse declareert, creëer je eigenlijk een behoorlijk complexe wrapper rond een woordenboek – dus je voegt meer overhead toe dan door een eenvoudig woordenboek te gebruiken. Een overhead die overigens sowieso zinloos is. Als je aan prestatiekritieke applicaties werkt, gebruik dan C of zoiets.


Antwoord 7, autoriteit 6%

Ik zou ook graag een oplossing willen toevoegen die gebruikmaakt van slots:

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

Controleer zeker de documentatie voor slots, maar een snelle uitleg van slots is dat het Python’s manier is om te zeggen: “Als je deze attributen en alleen deze attributen in de klasse kunt vergrendelen, zodat je vastlegt dat je geen nieuwe attributen zult toevoegen zodra de klasse is geïnstantieerd (ja, je kunt nieuwe attributen toevoegen aan een klasse-instantie, zie het onderstaande voorbeeld), dan zal ik de grote geheugentoewijzing afschaffen die het mogelijk maakt om nieuwe attributen aan een klasse-instantie toe te voegen en precies gebruiken wat ik hiervoor nodig heb slottedattributen”.

Voorbeeld van het toevoegen van attributen aan klasse-instantie (dus geen slots gebruiken):

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
p1 = Point(3,5)
p1.z = 8
print(p1.z)

Uitvoer: 8

Voorbeeld van proberen attributen toe te voegen aan klasse-instantie waar slots werden gebruikt:

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y
p1 = Point(3,5)
p1.z = 8

Uitvoer: AttributeError: ‘Point’-object heeft geen attribuut ‘z’

Dit kan effectief werken als een struct en gebruikt minder geheugen dan een klasse (zoals een struct zou doen, hoewel ik niet precies heb onderzocht hoeveel). Het wordt aanbevolen om slots te gebruiken als u een groot aantal instanties van het object gaat maken en geen attributen hoeft toe te voegen. Een puntobject is hier een goed voorbeeld van, omdat het waarschijnlijk is dat men veel punten kan instantiëren om een dataset te beschrijven.


Antwoord 8, autoriteit 5%

U kunt de C-structuur die beschikbaar is in de standaardbibliotheek subclasseren. De module ctypesbiedt een Structuurklasse. Het voorbeeld uit de documenten:

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

Antwoord 9, autoriteit 5%

U kunt de init-parameters ook per positie doorgeven aan de instantievariabelen

# 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

Antwoord 10, autoriteit 3%

Als ik een “instant data-object dat zich ook als een woordenboek gedraagt” nodig heb (ik nietdenk aan C-structs!), denk ik aan deze leuke hack:

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

Nu kun je gewoon zeggen:

struct = Map(field1='foo', field2='bar', field3=42)
self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])

Perfect handig voor die momenten waarop je een “databag die GEEN klasse is” nodig hebt, en voor wanneer benoemdetuples onbegrijpelijk zijn…


Antwoord 11, autoriteit 3%

Sommige antwoorden hier zijn enorm uitgebreid. De eenvoudigste optie die ik heb gevonden is (van: http://norvig.com/python-iaq.html):

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

Initialiseren:

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

meer toevoegen:

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

bewerken:Sorry, ik zag dit voorbeeld niet al verderop.


Antwoord 12, autoriteit 2%

Je krijgt op de volgende manier toegang tot C-Style struct in python.

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

als je alleen het object van cstruct wilt gebruiken

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)

als je een array van objecten van cstruct wilt maken

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)

Opmerking:
in plaats van de ‘cstruct’-naam, gebruik a.u.b. uw struct-naam
in plaats van var_i, var_f, var_str, definieert u alstublieft de lidvariabele van uw structuur.


Antwoord 13

Dit is misschien een beetje laat, maar ik heb een oplossing gemaakt met Python Meta-Classes (decoratorversie hieronder ook).

Wanneer __init__wordt aangeroepen tijdens runtime, pakt het elk van de argumenten en hun waarde en wijst ze toe als instantievariabelen aan uw klasse. Op deze manier kun je een struct-achtige klasse maken zonder elke waarde handmatig toe te wijzen.

Mijn voorbeeld bevat geen foutcontrole, dus het is gemakkelijker te volgen.

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 

Hier is het in actie.

>>> class MyClass(object):
    __metaclass__ = MyStruct
    def __init__(self, a, b, c):
        pass
>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>> 

Ik plaatste het op redditen /u/matchuplaatste een versie van de decorateur die schoner is. Ik raad je aan het te gebruiken, tenzij je de metaklasseversie wilt uitbreiden.

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

14

Ik zie dit antwoord hier niet, dus ik denk dat ik het voeg ervoor toe sinds ik nu python leun en net ontdekt. python tutorial (python 2 in dit geval) geeft het volgende eenvoudige en effectieve voorbeeld :

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

Dat wil zeggen, er wordt een leeg klasseobject gemaakt, vervolgens geïnstantieerd en de velden worden dynamisch toegevoegd.

Het voordeel hiervan is dat het heel eenvoudig is. Het nadeel is dat het niet bijzonder zelfdocumenterend is (de beoogde leden worden nergens in de klasse “definitie” vermeld), en niet-ingestelde velden kunnen problemen veroorzaken bij toegang. Deze twee problemen kunnen worden opgelost door:

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

Nu kun je in één oogopslag zien welke velden het programma verwacht.

Beide zijn gevoelig voor typefouten, john.slarly = 1000zal slagen. Toch werkt het.


Antwoord 15

Hier is een oplossing die een klasse (nooit geïnstantieerd) gebruikt om gegevens vast te houden. Ik vind het prettig dat er op deze manier weinig getypt hoeft te worden en dat er geen extra pakketten etc.

nodig zijn

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

U kunt indien nodig later meer velden toevoegen:

myStruct.field3 = 3

Om de waarden te krijgen, worden de velden zoals gewoonlijk geopend:

>>> myStruct.field1
'one'

Antwoord 16

Persoonlijk vind ik deze variant ook leuk. Het breidt @dF’s antwoorduit.

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

Het ondersteunt twee initialisatiemodi (die kunnen worden gemengd):

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

Het drukt ook mooier af:

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

Antwoord 17

Er is precies een python-pakket voor dit doel. zie cstruct2py

cstruct2pyis een pure python-bibliotheek voor het genereren van python-klassen van C-code en deze gebruiken om gegevens in en uit te pakken. De bibliotheek kan C-headers (structs, unions, enums en arrays-declaraties) ontleden en ze emuleren in python. De gegenereerde pythonic-klassen kunnen de gegevens ontleden en inpakken.

Bijvoorbeeld:

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"

Hoe te gebruiken

Eerst moeten we de pythonische structuren genereren:

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

Nu kunnen we alle namen uit de C-code importeren:

parser.update_globals(globals())

We kunnen dat ook rechtstreeks doen:

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

Typen en definities uit de C-code gebruiken

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

De uitvoer zal zijn:

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

Klonen

Voor kloon cstruct2pyvoer je uit:

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

18

De volgende oplossing voor een struct is geïnspireerd op de genoemde tuple-implementatie en enkele van de eerdere antwoorden. In tegenstelling tot de namedtuple is het echter veranderlijk, in zijn waarden, maar net als de c-style struct onveranderlijk in de namen/attributen, wat een normale klasse of dict niet is.

_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

Gebruik:

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'

Antwoord 19

Hier is een snelle en vuile truc:

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

Hoe werkt het? Het hergebruikt gewoon de ingebouwde klasse Warning(afgeleid van Exception) en gebruikt het zoals het je eigen gedefinieerde klasse was.

De goede punten zijn dat je niets eerst hoeft te importeren of definiëren, dat “Waarschuwing” een korte naam is, en dat het ook duidelijk maakt dat je iets vies doet dat niet ergens anders gebruikt mag worden dan een klein script van de jouwe.

Trouwens, ik heb geprobeerd iets nog eenvoudiger te vinden, zoals ms = object(), maar dat lukte niet (dit laatste voorbeeld werkt niet). Als je er een hebt, ben ik geïnteresseerd.


Antwoord 20

https://stackoverflow.com/a/32448434/159695werkt niet in Python3.

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

En ik breid het uit om standaardwaarden toe te voegen.

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

Antwoord 21

Als je geen 3.7 voor @dataclass hebt en mutability nodig hebt, kan de volgende code voor jou werken. Het is behoorlijk zelfdocumenterend en IDE-vriendelijk (auto-complete), voorkomt dubbel schrijven, is eenvoudig uit te breiden en het is heel eenvoudig om te testen of alle instantievariabelen volledig zijn geïnitialiseerd:

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)

Antwoord 22

De beste manier die ik vond om dit te doen, was door een aangepaste woordenboekklasse te gebruiken, zoals uitgelegd in dit bericht: https://stackoverflow. com/a/14620633/8484485

Als ondersteuning voor automatisch aanvullen van iPython nodig is, definieert u eenvoudig de functie dir() als volgt:

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

Je definieert dan je pseudo-structuur als volgt: (deze is genest)

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

Je hebt dan als volgt toegang tot de waarden in my_struct:

print(my_struct.com1.inst)

=>[5]


Antwoord 23

Ik denk dat het Python-structuurwoordenboek geschikt is voor deze vereiste.

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

Other episodes