파이썬에서 C와 같은 구조


447

파이썬에서 C와 유사한 구조를 편리하게 정의하는 방법이 있습니까? 나는 다음과 같은 것들을 쓰는 데 지쳤다.

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

5
반-관련 적으로, 대수적 데이터 타입은 절대적으로 훌륭하지만, 잘 사용하려면 일반적으로 패턴 매칭이 필요합니다.
Edward Z. Yang

51
지루한 것 외에이 방법에 문제가 있습니까?
levesque

2
struct를 유용하게 사용할 수 있습니다 : github.com/dorkitude/dstruct
Kyle Wild

10
@levesque 오타없이 리팩토링하기가 어렵고 코드를 감추는 동안 한 눈에보기가 더 어렵습니다.MyStruct = namedtuple("MyStruct", "field1 field2 field3")
sam boosalis

1
pandas.Series(a=42).a를 A 데이터 과학자 경우 ... 그것을해야
마크 바스

답변:


341

Python 2.6의 표준 라이브러리에서 collections 모듈 에 추가 된 named tuple을 사용하십시오 . Python 2.4를 지원해야하는 경우 Raymond Hettinger의 명명 된 튜플 레시피 를 사용할 수도 있습니다 .

기본 예제에는 좋지만 나중에 발생할 수있는 많은 경우도 다룹니다. 위의 조각은 다음과 같이 작성됩니다.

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

새로 작성된 유형은 다음과 같이 사용할 수 있습니다.

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

명명 된 인수를 사용할 수도 있습니다.

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

164
...하지만 tuple이라는 이름은 불변입니다. OP의 예제는 변경 가능합니다.
mhowison

28
@ mhowison-내 경우에는 단지 플러스입니다.
ArtOfWarfare

3
좋은 해결책. 이 튜플 배열을 어떻게 반복합니까? 필자는 1-3 필드가 튜플 객체에서 동일한 이름을 가져야한다고 가정합니다.
Michael Smith

2
namedtuple은 최대 4 개의 인수를 가질 수 있으므로 명명 된 tuple이있는 더 많은 데이터 멤버로 구조를 매핑하는 방법
Kapil

3
@Kapil-namedtuple의 두 번째 인수는 멤버 이름 목록이어야합니다. 이 목록은 길이가 될 수 있습니다.
ArtOfWarfare

226

업데이트 : 데이터 클래스

의 도입으로 데이터 클래스파이썬 3.7 우리는 매우 가까이.

다음 예제는 아래 NamedTuple 예제 와 비슷 하지만 결과 객체는 변경 가능 하며 기본값을 허용합니다.

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)

보다 구체적인 유형 주석을 사용하려는 경우 새로운 타이핑 모듈에서 잘 작동합니다 .

나는 이것을 필사적으로 기다리고 있었다! 나에게 묻는다면 데이터 클래스 와 새로운 NamedTuple 선언은 타이핑 모듈 과 결합되어 신의 선물입니다!

향상된 NamedTuple 선언

파이썬 3.6 부터는 불변성으로 살 수있는 한 꽤 간단하고 아름답습니다 (IMHO) .

NamedTuples을 선언 하는 새로운 방법 이 도입되어 유형 주석 도 가능합니다.

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
불변의 dicts - - 메이트, 당신은 그냥 내 일을했습니다 감사합니다 : D
드미트리 Arkhipenko을

10
dataclass모듈은 Python 3.7의 새로운 기능이지만 할 수 있습니다 pip install dataclasses. 파이썬 3.6의 백 포트입니다. pypi.org/project/dataclasses/#description
Lavande

향상된 NamedTuple 선언의 경우 +1 몇 가지 변수가 있다면 구식은 정말 불쾌합니다 ...
gebbissimo

@Lavande 3.6에서 3.7 사이의 주요 변경 사항이 하나의 부 버전을 백 포트해야한다는 것을 알 수 있습니까?
Purple Ice

1
@PurpleIce 그것은 PEP 557의 구현이었다 데이터 클래스는 @dataclass세부 사항은 여기에 있습니다 : pypi.org/project/dataclasses/#description
Lavande

96

C에서 구조체를 사용하는 많은 것들 (예 : x, y 좌표 또는 RGB 색상)에 튜플을 사용할 수 있습니다.

다른 모든를 들어 사전, 또는 같은 유틸리티 클래스를 사용할 수있는 이 일을 :

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

필자 는 Python Cookbook의 출판 된 버전에서 "결정적인"토론이 여기 있다고 생각합니다 .


5
빈 수업이 똑같이됩니까?
커트 리우

44
파이썬을 처음 사용하는 경우 참고 : C 구조체와 달리 튜플은 일단 읽기 전용입니다
LeBleu

2
@KurtLiu 아니오, 아마TypeError: this constructor takes no arguments
Evgeni Sergeev

84

아마도 생성자가없는 Structs를 찾고있을 것입니다 :

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
여기서 수행하는 작업은 기술적으로 작동하지만 많은 사용자에게 이것이 작동하는 이유 를 즉시 알 수는 없습니다 . 귀하의 선언 class Sample:은 즉시 아무것도하지 않습니다. 그들은 클래스 속성을 설정합니다. 이것들은 항상 예를 ​​들어서 접근 할 수 있습니다 Sample.name.
채닝 무어

22
당신이있어 실제로 일을하는 것은 객체 인스턴스 속성을 추가 s1하고 s2런타임에. 달리 금지되지 않는 한, name클래스에 name속성 이 있는지 여부에 관계없이 언제든지 클래스의 모든 인스턴스 에서 속성을 추가하거나 수정할 수 있습니다 . 아마도이 작업을 수행 할 때 가장 큰 기능적 문제는 설정 한 여부에 따라 동일한 클래스의 다른 인스턴스가 다르게 작동한다는 것입니다 name. 업데이트 Sample.name하면 명시 적으로 설정된 name속성이 없는 객체 가 새 객체를 반환합니다 name.
채닝 무어

2
이것은 메소드가없는 짧은 '클래스', 기본값이있는 '필드'(클래스 속성, 알고 있음)에 가깝습니다. 변경 가능한 유형이 아닌 한 (dict, list) 괜찮습니다. 물론 PyCharm의 "class has init method" 와 같이 PEP-8 또는 "친숙한"IDE 검사를 수행 할 수 있습니다 .
Tomasz Gandor

4
채닝 무어가 묘사 한 부작용을 실험했습니다. self당신이 저에게 요청하면 몇 가지 키워드와 생성자 라인 의 경제 가치가 없습니다 . Jose가 실수로 값을 공유하는 위험에 대한 경고 메시지를 추가하기 위해 Jose가 자신의 답변을 편집 할 수 있다면 고맙겠습니다.
Stéphane C.

@ChanningMoore : 설명하려는 문제를 재현하려고했지만 실패했습니다. 문제가 나타나는 최소한의 실례를 제시 할 수 있습니까?
gebbissimo

67

사전은 어때요?

이 같은:

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

그런 다음이를 사용하여 값을 조작 할 수 있습니다.

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

그리고 값은 문자열 일 필요는 없습니다. 그것들은 다른 어떤 대상이 될 수 있습니다.


34
이것은 나의 접근 방식이기도하지만 사전이 키에 대해 아무것도 받아 들일 수 있기 때문에 정확하게 위험하다고 생각합니다. myStruct [ "field"]를 설정할 때 myStruct [ "ffield"]를 설정해도 오류가 발생하지 않습니다. 나중에 myStruct [ "field"]를 사용하거나 다시 사용할 때 문제가 명백해질 수 있습니다. PabloG의 접근 방식이 마음에 듭니다.
mobabo

PabloG와 동일한 문제가 있습니다. 다음 코드를 그의 코드에 추가해보십시오 : pt3.w = 1 print pt3.w dicts가있는 언어의 경우, 특히 직렬화되는 객체에 대해 사용하는 것이 좋습니다. 이상한 경우가 아니면 import json을 사용하여 자동으로 가져 오기 및 다른 직렬화 라이브러리를 저장할 수 있기 때문에 당신의 받아쓰기 안에 물건. Dicts는 데이터와 논리를 분리하여 유지하는 솔루션으로, 사용자 정의 serialize 및 serialize 기능을 작성하지 않고 pickle과 같은 이식 불가능한 serializer를 사용하지 않으려는 사람들에게는 struct보다 낫습니다.
Poikilos

27

dF : 정말 멋지다 ... dict를 사용하여 클래스의 필드에 액세스 할 수 있다는 것을 몰랐다.

Mark : 제가 원했던 상황은 정확히 터플을 원할 때지만 사전처럼 "무거운"것은 아닙니다.

클래스의 필드, 메서드 및 모든 속성은 dicts (적어도 CPython)를 사용하여 내부적으로 저장되므로 사전을 사용하여 클래스의 필드에 액세스 할 수 있습니다.

... 두 번째 의견으로 우리를 안내합니다. 파이썬 딕트가 "무거운 것"이라고 믿는 것은 비 파이썬 적 개념입니다. 그리고 그러한 주석을 읽으면 Python Zen이 죽습니다. 그 좋지 않다.

클래스를 선언 할 때 실제로 사전 주위에 꽤 복잡한 래퍼를 만드는 것입니다. 따라서 간단한 사전을 사용하는 것보다 오버 헤드가 더 많이 발생합니다. 어쨌든 의미가없는 오버 헤드. 성능이 중요한 응용 프로그램을 작업하는 경우 C 등을 사용하십시오.


5
# 1, Cython! = CPython. 파이썬 코드를 C 코드로 크로스 컴파일하는 프로젝트 인 Cython이 아닌 C로 작성된 Python의 구현 인 CPython에 대해 이야기하고 있다고 생각합니다. 그 문제를 해결하기 위해 답변을 편집했습니다. # 2, 나는 그가 dicts가 무겁다 고 말했을 때, 그는 구문을 언급하고 있다고 생각합니다. self['member']은 (는) 3 자보다 길며 self.member해당 문자는 모두 상대적으로 비 친화적입니다.
ArtOfWarfare

19

표준 라이브러리에서 사용 가능한 C 구조를 서브 클래스화할 수 있습니다. 하는 ctypes 모듈이 제공하는 구조 클래스 . 문서의 예 :

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

또한 슬롯 을 사용하는 솔루션을 추가하고 싶습니다 .

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

슬롯에 대한 문서를 확실히 확인하지만 슬롯에 대한 간단한 설명은 파이썬이 말하는 방식입니다. "만약 이러한 속성과 이러한 속성 만 클래스에 잠글 수 있다면 클래스에 한 번만 새 속성을 추가하지 않을 것을 약속합니다 인스턴스화됩니다 (예 : 클래스 인스턴스에 새 속성을 추가 할 수 있습니다 (아래 예 참조). 그러면 클래스 인스턴스에 새 속성을 추가하고 이러한 슬롯 속성에 필요한 것만 사용할 수있는 대용량 메모리 할당을 제거 합니다. "

클래스 인스턴스에 속성을 추가하는 예 (따라서 슬롯을 사용하지 않음) :

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

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

출력 : 8

슬롯이 사용 된 클래스 인스턴스에 속성을 추가하려는 예 :

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

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

출력 : AttributeError : 'Point'개체에 'z'특성이 없습니다.

이것은 효과적으로 구조체로 작동 할 수 있으며 클래스보다 메모리를 적게 사용합니다 (구조는 정확히 얼마만큼 연구하지는 않았지만). 많은 양의 객체 인스턴스를 작성하고 속성을 추가 할 필요가없는 경우 슬롯을 사용하는 것이 좋습니다. 점 개체는 데이터 집합을 설명하기 위해 많은 점을 인스턴스화 할 수 있기 때문에 이에 대한 좋은 예입니다.


17

위치별로 init 매개 변수를 인스턴스 변수에 전달할 수도 있습니다.

# 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
위치 별 업데이트는 속성의 선언 순서를 무시하고 대신 알파벳순 정렬을 사용합니다. 따라서 Point3dStruct선언 에서 행 순서를 변경하면 Point3dStruct(5, 6)예상대로 작동하지 않습니다. 6 년 동안 아무도이 글을 쓰지 않은 것은 이상합니다.
lapis

멋진 코드에 Python 3 버전을 추가 할 수 있습니까? 훌륭한 일! 나는 당신이 추상적 인 것을 취하고 두 번째 특정 클래스로 명시 적으로 만드는 것을 좋아합니다. 오류 처리 / 캐치에 좋습니다. Python 3의 경우 print> print()attrs[n]>를 변경 하면됩니다 next(attrs)(필터는 자체 반복 가능한 객체이며을 필요로합니다 next).
Jonathan Komar

10

"사전처럼 동작하는 즉각적인 데이터 객체"가 필요할 때마다 ( C 구조체를 생각 하지 않습니다 !)이 귀여운 핵을 생각합니다.

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

이제 당신은 말할 수 있습니다 :

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

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

"클래스가 아닌 데이터 백"이 필요할 때와 명명 된 튜플을 이해할 수없는 경우에 매우 편리합니다 ...


나는 pandas.Series (a = 42) ;-)를 사용합니다
Mark Horvath

8

다음과 같은 방법으로 파이썬에서 C 스타일 구조체에 액세스합니다.

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

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)

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)

참고 : 'cstruct'이름 대신 var_i, var_f, var_str 대신 구조체 이름을 사용하십시오. 구조의 멤버 변수를 정의하십시오.


3
이것이 stackoverflow.com/a/3761729/1877426의 것과 다른가요 ?
lagweezle

8

여기에 대한 답변 중 일부는 매우 정교합니다. 내가 찾은 가장 간단한 옵션은 다음과 같습니다 ( http://norvig.com/python-iaq.html ).

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

초기화 중 :

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

더 추가 :

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

편집 : 죄송하지만이 예제는 이미 더 이상 보지 못했습니다.


5

이것은 약간 늦을 수도 있지만 Python Meta-Classes (아래 장식 버전도 사용)를 사용하여 솔루션을 만들었습니다.

__init__런타임 중에가 호출 되면 각 인수와 해당 값을 가져 와서 클래스에 인스턴스 변수로 지정합니다. 이렇게하면 모든 값을 수동으로 할당하지 않고도 구조체와 같은 클래스를 만들 수 있습니다.

내 예제에는 오류 검사가 없으므로 따르기가 더 쉽습니다.

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 

여기에 작동합니다.

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


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

나는 그것을 reddit에 게시 했고 / u / matchu 는 더 깨끗한 데코레이터 버전을 게시했습니다. 메타 클래스 버전을 확장하지 않으려면 사용하는 것이 좋습니다.

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

Damnit-오늘 2 시간 동안 내 데코레이터를 작성하여이 작업을 수행 한 후 발견했습니다. 어쨌든, 내 값은 기본값을 처리하기 때문에 게시하지 않습니다. stackoverflow.com/a/32448434/901641
ArtOfWarfare

func_code를 언급 한 +1 그 방향으로 파기 시작했고 그곳에서 흥미로운 것들이 많이 발견되었습니다.
wombatonfire

5

전달 된 모든 인수 또는 기본값이 인스턴스에 할당되도록 모든 메소드에서 사용할 수있는 데코레이터를 작성했습니다.

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

빠른 시연. 위치 인수 a를 사용하고에 대한 기본값 b과 명명 된 인수를 사용 c합니다. 그런 다음 3 개의 referenceencing을 모두 인쇄 self하여 메소드가 입력되기 전에 올바르게 할당되었음을 표시합니다.

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

내 데코레이터는 단지뿐만 아니라 모든 방법으로 작동해야합니다 __init__.


5

나는이 대답을 여기에서 볼 수 없으므로 지금 파이썬에 기대어 그것을 발견했기 때문에 그것을 추가 할 것이라고 생각합니다. 파이썬 튜토리얼 (이 경우 파이썬 2)하기 간단하고 효과적인 예이다 :

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

즉, 빈 클래스 객체가 만들어지고 인스턴스화되고 필드가 동적으로 추가됩니다.

이것의 장점은 정말 간단합니다. 단점은 특히 자체 문서화가 아니며 (의도 된 멤버는 "정의"클래스의 어느 곳에도 나열되지 않음) 설정되지 않은 필드는 액세스 할 때 문제를 일으킬 수 있다는 것입니다. 이 두 가지 문제는 다음과 같이 해결할 수 있습니다.

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

이제 한 번에 프로그램에서 어떤 필드를 기대할 수 있는지 알 수 있습니다.

둘 다 오타하기 쉽고 john.slarly = 1000성공합니다. 여전히 작동합니다.


4

다음은 클래스를 사용하여 데이터를 보유하지 않는 솔루션입니다. 나는이 방법이 타이핑이 거의 없으며 추가 패키지 등이 필요하지 않다는 것을 좋아합니다 .

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

필요에 따라 나중에 더 많은 필드를 추가 할 수 있습니다.

myStruct.field3 = 3

값을 얻기 위해 평소와 같이 필드에 액세스합니다.

>>> myStruct.field1
'one'

2

개인적으로, 나는이 변종도 좋아합니다. @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__)

두 가지 초기화 모드를 지원합니다 (혼합 가능).

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

또한 더 잘 인쇄됩니다.

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

2

구조체에 대한 다음 솔루션은 명명 된 튜플 구현과 이전 답변 중 일부에서 영감을 얻었습니다. 그러나 명명 된 튜플과 달리 값은 변경 가능하지만 이름 / 속성에서 변경할 수없는 c 스타일 구조체와 비슷합니다. 일반 클래스 또는 딕 트는 그렇지 않습니다.

_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

용법:

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

이 목적을 위해 정확하게 파이썬 패키지가 있습니다. cstruct2py 참조

cstruct2pyC 코드에서 파이썬 클래스를 생성하고이를 사용하여 데이터를 압축하고 압축을 풀기위한 순수 파이썬 라이브러리입니다. 라이브러리는 C 헤더 (구조, 공용체, 열거 형 및 배열 선언)를 구문 분석하고이를 파이썬으로 에뮬레이션 할 수 있습니다. 생성 된 파이 토닉 클래스는 데이터를 구문 분석하고 압축 할 수 있습니다.

예를 들면 다음과 같습니다.

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"

사용하는 방법

먼저 pythonic 구조체를 생성해야합니다.

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

이제 C 코드에서 모든 이름을 가져올 수 있습니다.

parser.update_globals(globals())

직접 할 수도 있습니다 :

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

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)

출력은 다음과 같습니다.

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

클론

클론 cstruct2py실행의 경우 :

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

0

파이썬 구조 사전 이이 요구 사항에 적합하다고 생각합니다.

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

0

https://stackoverflow.com/a/32448434/159695 는 Python3에서 작동하지 않습니다.

https://stackoverflow.com/a/35993/159695 는 Python3에서 작동합니다.

그리고 기본값을 추가하도록 확장합니다.

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

@dataclass에 3.7이없고 변경이 필요한 경우 다음 코드가 적합 할 수 있습니다. 자체 문서화가 쉽고 IDE 친화적 (자동 완성)이며 두 번 쓰지 못하게하고 쉽게 확장 할 수 있으며 모든 인스턴스 변수가 완전히 초기화되었는지 테스트하는 것은 매우 간단합니다.

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

빠르고 더러운 트릭은 다음과 같습니다.

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

어떻게 작동합니까? 그냥 내장 클래스를 재사용합니다 Warning(Exception )를 재사용하고 자신이 정의한 클래스처럼 사용합니다.

좋은 점은 먼저 가져 오거나 정의 할 필요가 없으며 "경고"는 짧은 이름이며 작은 스크립트 이외의 다른 곳에서는 사용해서는 안되는 더러운 일을하고 있다는 것입니다.

그건 그렇고, 나는 더 간단한 것을 찾으려고 ms = object()했지만 할 수 없었습니다 (이 마지막 예는 작동하지 않습니다). 당신이 하나 있다면, 나는 관심이 있습니다.


0

이 작업을 수행하는 가장 좋은 방법은이 게시물에 설명 된대로 사용자 지정 사전 클래스를 사용하는 것입니다. https://stackoverflow.com/a/14620633/8484485

iPython 자동 완성 지원이 필요한 경우 다음과 같이 dir () 함수 를 정의하십시오 .

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

그런 다음 의사 구조를 다음과 같이 정의하십시오 (이것은 중첩됩니다)

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

그런 다음 다음과 같이 my_struct 내부의 값에 액세스 할 수 있습니다.

print(my_struct.com1.inst)

=>[5]


0

명명 된 튜플 은 편안합니다. 그러나 성능과 스토리지를 공유하는 사람은 없습니다.

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)

__dict__사용하지 않는 경우 __slots__(고성능 및 저장 용량)과NamedTuple (읽기 및 사용이 명확함

자세한 내용 이 링크 ( 슬롯 사용 )를 검토 __slots__하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.