C-lignende strukturer i Python

stemmer
318

Er det en måte å enkelt definere en C-lignende struktur i Python? Jeg er lei av å skrive ting som:

class MyStruct():
    def __init__(self, field1, field2, field3):
        self.field1 = field1
        self.field2 = field2
        self.field3 = field3
Publisert på 30/08/2008 klokken 14:33
kilden bruker
På andre språk...                            


23 svar

stemmer
296

Bruk en navngitt tuppel , som ble lagt til samlingene modulen i standardbiblioteket i Python 2.6. Det er også mulig å bruke Raymond Hettinger er oppkalt tuppel oppskrift hvis du trenger for å støtte Python 2.4.

Det er fint for grunnleggende eksempel, men omfatter også en haug med edge tilfeller du kan kjøre inn senere også. Din fragment ovenfor ville bli skrevet som:

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

Den nyopprettede typen kan brukes slik:

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

Du kan også bruke navngitte argumenter:

m = MyStruct(field1="foo", field2="bar", field3="baz")
Svarte 30/08/2008 kl. 15:18
kilden bruker

stemmer
93

Du kan bruke en tuppel for mange ting der du ville bruke en struct i C (noe som x, y koordinater eller RGB-farger for eksempel).

For alt annet kan du bruke ordbok eller et verktøy klasse som denne :

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

Jeg tror "definitive" diskusjonen er her , i den publiserte versjonen av Python Cookbook.

Svarte 30/08/2008 kl. 14:38
kilden bruker

stemmer
75

Kanskje du er på jakt etter Structs uten konstruktører:

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
Svarte 21/09/2010 kl. 15:15
kilden bruker

stemmer
56

Hva med en ordbok?

Noe sånt som dette:

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

Deretter kan du bruke dette til å manipulere verdier:

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

Og verdiene trenger ikke å være strenger. De kan være ganske mye annen gjenstand.

Svarte 30/08/2008 kl. 14:35
kilden bruker

stemmer
48

Oppdatering : Data Classes

Med introduksjonen av data klasser i Python 3.7 får vi svært nær.

Det følgende eksempel er lik den NamedTuple eksempelet nedenfor, men den resulterende gjenstand er foranderlig, og det gir mulighet for standardverdier.

@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)"

Dette spiller pent med den nye skrivemodulen i tilfelle du ønsker å bruke mer spesifikke typen merknader.

Jeg har ventet desperat for dette! Hvis du spør meg, data klasser og den nye NamedTuple erklæringen, kombinert med å skrive modulen er en gudegave!

Forbedret NamedTuple declartaion

Siden Python 3.6 ble det ganske enkelt og vakkert (IMHO), så lenge du kan leve med uforanderlighet.

En ny måte å erklære NamedTuples ble innført, noe som gjør det mulig for typen kommentarer i tillegg:

from typing import NamedTuple

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

Bruk det slik:

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

Eller som dette hvis du virkelig ønsker å:

my_item = MyStruct('foo', 0, ['bar'], {'baz': 'qux'}, Foo('bar'))
Svarte 31/07/2017 kl. 22:54
kilden bruker

stemmer
24

dF: det er ganske kult ... Jeg visste ikke at jeg kunne få tilgang feltene i en klasse ved hjelp av dict.

Mark: situasjoner som jeg skulle ønske jeg hadde denne er nettopp når jeg ønsker en tuppel, men ingenting som "tung" som en ordbok.

Du får tilgang innen en klasse ved hjelp av en ordbok fordi feltene i en klasse, er dens metoder og alle dens egenskaper lagres internt ved hjelp dicts (minst i CPython).

... Hvilket fører oss til andre kommentar. Tro at Python dicts er "tung" er et ekstremt ikke-pythonistic konsept. Og lese slike kommentarer dreper min Python Zen. Det er ikke bra.

Du skjønner, når du deklarerer en klasse du faktisk lage en ganske kompleks wrapper rundt en ordbok - så, om noe, er du legge mer overhead enn ved hjelp av en enkel ordbok. En overhead som for øvrig, er meningsløst i alle fall. Hvis du arbeider på ytelse kritiske applikasjoner, bruk C eller noe.

Svarte 30/08/2008 kl. 15:20
kilden bruker

stemmer
15

Man kan også passere init- parametere for å instansvariabelene ved stilling

# 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
Svarte 30/08/2008 kl. 15:53
kilden bruker

stemmer
12

Du kan underklasse C struktur som er tilgjengelig i standard bibliotek. Den ctypes modulen gir en struktur klasse . Eksempelet fra docs:

>>> 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
>>>
Svarte 25/06/2015 kl. 23:50
kilden bruker

stemmer
9

Når jeg trenger en "instant data objekt som også oppfører seg som en ordbok" (jeg vet ikke ! Tenker på C structs), tenker jeg på denne søte hack:

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

Nå kan du bare si:

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

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

Perfekt hendig for de gangene du trenger en "data bag som er IKKE en klasse", og for når namedtuples er uforståelig ...

Svarte 13/09/2013 kl. 17:40
kilden bruker

stemmer
7

Du får tilgang C-Style struct i python på følgende måte.

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

hvis du bare vil bruke gjenstand for 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)

Hvis du ønsker å opprette en rekke gjenstander av 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)

Merk: i stedet for 'cstruct' navn, kan du bruke struct navn i stedet for var.I, var_f, var_str, kan du definere strukturen er medlem variabel.

Svarte 09/11/2014 kl. 07:37
kilden bruker

stemmer
6

Jeg vil også legge til en løsning som bruker spor :

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

Definitivt sjekke dokumentasjonen for spilleautomater, men en rask forklaring av spilleautomater er at det er pyton måte å si: "Hvis du kan låse disse attributtene og bare disse attributtene inn i klassen, slik at du begår at du ikke vil legge noen nye attributter når klassen instansieres (ja du kan legge til nye attributter i en klasse eksempel, se eksempel under) så vil jeg gjøre unna med stort minne allokering som gir mulighet for å legge til nye attributter til en klasse forekomst og bruke akkurat det jeg trenger for disse avlange attributter".

Eksempel på å legge til attributter i klassen forekomst (altså ikke bruk av slisser):

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

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

Output: 8

Eksempel på forsøk på å legge til attributter til klassen forekomst hvor sporene ble brukt:

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

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

Utgang: AttributeError: 'punkt' objekt har ingen attributten 'z'

effektivt dette kan fungerer som en struct og bruker mindre minne enn en klasse (som en struct ville, selv om jeg ikke har forsket på akkurat hvor mye). Det anbefales å bruke spilleautomater hvis du vil lage en stor mengde tilfeller av objektet, og ikke trenger å legge til attributter. Et punkt objekt er et godt eksempel på dette som det er sannsynlig at man kan instantiate mange poeng å beskrive et datasett.

Svarte 12/04/2018 kl. 06:17
kilden bruker

stemmer
5

Noen svarene her er massivt forseggjort. Det enkleste alternativet jeg har funnet er (fra: http://norvig.com/python-iaq.html ):

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

Initial:

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

legge mer:

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

Edit: Sorry så ikke dette eksemplet allerede lenger ned.

Svarte 05/08/2017 kl. 00:39
kilden bruker

stemmer
4

Jeg skrev en dekoratør som du kan bruke på en metode for å gjøre det slik at alle de argumenter som sendes i, eller noen mislighold, er tildelt forekomsten.

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

En rask demonstrasjon. Legg merke til at jeg bruker en posisjons argument a, bruker standardverdien for b, og en navngitt argument c. Jeg deretter skrive ut alle tre henvisning self, for å vise at de har blitt ordentlig tildelt før metoden er angitt.

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

Legg merke til at min dekoratør skal arbeide med noen metode, ikke bare __init__.

Svarte 08/09/2015 kl. 02:38
kilden bruker

stemmer
3

Dette kan være litt sent, men jeg gjorde en løsning ved hjelp av Python Meta-klasser (dekoratør versjonen nedenfor også).

Når __init__kalles under kjøring, griper det hver av de argumentene og deres verdi og tildeler dem som instansvariabler til klassen. På denne måten kan du lage en struct-lignende klasse uten å tilordne hver verdi manuelt.

Mitt eksempel har ingen feilkontroll slik at det er lettere å følge.

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 

Her er den i aksjon.

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


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

Jeg postet den på reddit og / u / matchu postet en dekoratør versjon som er renere. Jeg vil oppfordre deg til å bruke den med mindre du ønsker å utvide metaclass versjon.

>>> 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
>>> 
Svarte 23/03/2015 kl. 14:32
kilden bruker

stemmer
1

Det er en python pakke akkurat for dette formålet. se cstruct2py

cstruct2pyer en ren python bibliotek for å generere python klasser fra C-kode og bruke dem til å pakke og pakke-data. Biblioteket kan analysere C headres (structs, fagforeninger, enums og arrays erklæringer) og etterligne dem i python. De genererte Pytonske klasser kan analysere og pakke dataene.

For eksempel:

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"

Hvordan å bruke

Først må vi generere Pytonske structs:

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

Nå kan vi importere alle navnene fra C-kode:

parser.update_globals(globals())

Vi kan også gjøre det direkte:

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

Ved hjelp av typer og definerer fra den C-kode

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

Utgangen vil være:

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

Clone

For klone cstruct2pyrun:

git clone https://github.com/st0ky/cstruct2py.git --recursive
Svarte 11/12/2018 kl. 09:34
kilden bruker

stemmer
1

Følgende løsning til en struct er inspirert av den namedtuple implementering og noen av de tidligere svarene. Men i motsetning til namedtuple er det foranderlig, i sine verdier, men som c-stil struct uforanderlig i navn / attributter, som en normal klasse eller dict ikke.

_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

bruk:

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'
Svarte 29/03/2018 kl. 16:25
kilden bruker

stemmer
1

Personlig liker jeg denne varianten også. Den strekker @ dF svar .

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

Den støtter to moduser av initialiseringen (som kan være blandet):

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

Dessuten skrives det bedre:

print(mystruct2)
# Prints: {'field3': 3, 'field1': 1, 'field2': 2}
Svarte 06/11/2017 kl. 15:38
kilden bruker

stemmer
1

Jeg ser ikke dette svaret her, så jeg figuren jeg skal legge det siden jeg lener Python akkurat nå, og bare oppdaget det. Den Python opplæringen (Python 2 i dette tilfelle) gir følgende enkle og effektive eksempel:

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

Det vil si at en tom klasse objekt blir opprettet, og deretter startes, og feltene er tilsatt dynamisk.

Den opp-siden til dette er det veldig enkelt. Ulempen er at det er ikke spesielt selv dokumentere (de tiltenkte medlemmer er ikke oppført noe sted i klassen "definisjon"), og ikke-fikserte felt kan føre til problemer når den åpnes. De to problemene kan løses ved å:

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

Nå på et øyeblikk kan du i det minste se hvilke felt programmet vil vente.

Begge er utsatt for skrivefeil, john.slarly = 1000vil lykkes. Likevel, det fungerer.

Svarte 08/11/2016 kl. 14:06
kilden bruker

stemmer
0

Her er en rask og skitne triks:

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

Hvordan det fungerer? Det bare gjenbruke den innebygde klasse Warning(avledet fra Exception) og bruke det som det var du eier definert klasse.

Den gode poeng er at du ikke trenger å importere eller definere noe først, at "Warning" er et kort navn, og at det gjør det også klart at du gjør noe skittent som ikke skal brukes andre steder enn et lite script av deg.

Forresten, jeg prøvde å finne noe enda enklere som ms = object(), men kunne ikke (denne siste exemple ikke fungerer). Hvis du har en, er jeg interessert.

Svarte 26/09/2019 kl. 16:56
kilden bruker

stemmer
0

Her er en løsning som bruker en klasse (aldri startes) for å holde data. Jeg liker at denne måten innebærer svært lite å skrive og krever ingen ekstra pakker etc.

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

Du kan legge til flere felt senere, etter behov:

myStruct.field3 = 3

For å få verdiene, er feltene åpnes som vanlig:

>>> myStruct.field1
'one'
Svarte 09/04/2019 kl. 11:10
kilden bruker

stemmer
0

Hvis du ikke har en 3.7 for @dataclass og trenger mutability, kan følgende kode fungere for deg. Det er ganske selv dokumentere og IDE-vennlig (autofullfør), hindrer skrive ting to ganger, og er lett utvides og det er svært enkelt å teste at alle instansvariabler er fullstendig initialisert:

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)
Svarte 28/01/2019 kl. 11:27
kilden bruker

stemmer
0

https://stackoverflow.com/a/32448434/159695 fungerer ikke i Python3.

https://stackoverflow.com/a/35993/159695 arbeider i Python3.

Og jeg strekker det å legge til standardverdier.

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')
Svarte 30/10/2017 kl. 13:40
kilden bruker

stemmer
0

Jeg tror Python struktur ordboken er egnet for dette kravet.

d = dict{}
d[field1] = field1
d[field2] = field2
d[field2] = field3
Svarte 08/07/2017 kl. 19:17
kilden bruker

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