С-подібні структури в Python


447

Чи є спосіб зручно визначити С-подібну структуру в Python? Я втомився писати такі речі, як:

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

5
Напівпов'язані, алгебраїчні типи даних були б абсолютно чудовими, але, щоб їх добре використовувати, зазвичай потрібно узгодження шаблонів.
Едвард Я. Ян

51
Чи є щось погано з цим методом, окрім як нудно писати?
levesque

2
Можливо, вам стане корисним dstruct: github.com/dorkitude/dstruct
Kyle Wild

10
@levesque важче переосмислити без помилок, важче читати з MyStruct = namedtuple("MyStruct", "field1 field2 field3")
першого

1
pandas.Series(a=42).aце потрібно зробити, якщо ваш науковець ...
Марк Хорват

Відповіді:


341

Скористайтеся названим кортежем , який був доданий до модуля колекції у стандартній бібліотеці на Python 2.6. Можна також скористатися іменованим рецептом кортежу Raymond Hettinger, якщо вам потрібно підтримати Python 2.4.

Це добре для вашого основного прикладу, але також охоплює купу крайових справ, до яких ви можете зіткнутися і пізніше. Ваш фрагмент вище буде записаний так:

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

Новостворений тип можна використовувати так:

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

Ви також можете використовувати названі аргументи:

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

164
... але nametuple незмінна. Приклад в ОП є змінним.
mhowison

28
@mhowison - У моєму випадку це лише плюс.
ArtOfWarfare

3
Приємне рішення. Як би ви провели крізь масив цих кортежів? Я б припустив, що поля 1-3 повинні мати однакові назви для кортежних об'єктів.
Майкл Сміт

2
imetuple може мати щонайменше чотири аргументи, тож як ми можемо зіставити структуру з більшою кількістю членів даних із відповідним найменуванням
Kapil

3
@Kapil - Другим аргументом для nametuple повинен бути список імен членів. Цей список може бути будь-якої довжини.
ArtOfWarfare

226

Оновлення : Класи даних

З впровадженням Класів даних у Python 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

З моменту Python 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
Мате, ти щойно зробив мій день - незмінні дикти - дякую: D
Дмитро Архипенко

10
dataclassМодуль нового в Python 3.7 , але ви можете pip install dataclasses. Це опора на Python 3.6. pypi.org/project/dataclasses/#description
Lavande

+1 для покращеної декларації NamedTuple. Старий спосіб насправді було неприємно читати, якщо у вас було кілька змінних…
gebbissimo

@Lavande Чи можу я знати, які зламні зміни відбулися між 3,6 та 3,7, що вам доведеться підтримувати одну незначну версію назад ...?
Фіолетовий лід

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.


5
Чи порожній клас зробив би те саме?
Курт Лю

44
Зауважте, якщо ви новачок у python: кортежі читаються лише один раз, на відміну від структур C
LeBleu

2
@KurtLiu Ні, це, мабуть, сказало бTypeError: this constructor takes no arguments
Євгеній Сергєєв,

84

Можливо, ви шукаєте конструкції без конструкторів:

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), у вас все добре. Звичайно, ви можете вдарити проти PEP-8 або "дружніх" перевірок IDE, як "Клас PyCharm" не має методу init .
Томаш Гандор

4
Я експериментував побічний ефект, описаний Ченнінг Муром. Не варто економити кілька selfключових слів і конструкторський рядок, якщо ви запитаєте мене. Буду вдячний, якщо Хосе міг би відредагувати свою відповідь, щоб додати попереджувальне повідомлення про ризик випадкового обміну цінностями у всіх випадках.
Стефан C.

@ChanningMoore: Я намагався відтворити проблему, яку ви описували, але не вдалося. Чи можете ви навести мінімальний робочий приклад, коли випливає проблема?
gebbissimo

67

Як щодо словника?

Щось на зразок цього:

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

Тоді ви можете використовувати це для маніпулювання значеннями:

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

І значення не повинні бути рядками. Вони можуть бути майже будь-яким іншим об'єктом.


34
Це був і мій підхід, але я вважаю, що це небезпечно саме тому, що словник може прийняти що завгодно за ключ. Не буде помилки, якщо я встановив myStruct ["ffield"], коли я мав намір встановити myStruct ["поле"]. Проблема може (або може не з’явитися), коли я пізніше буду використовувати або повторно використовувати myStruct ["поле"]. Мені подобається підхід PabloG.
мобабо

Те ж саме питання існує і з PabloG. Спробуйте додати до нього такий код: pt3.w = 1 print pt3.w Мовою з диктами краще їх використовувати, особливо для об'єктів, що серіалізуються, оскільки ви можете автоматично використовувати імпорт json для збереження їх та інших бібліотек серіалізації, доки у вас немає дивних речі всередині вашого диктату. Дікти - це рішення зберігати дані та логіку окремо і краще, ніж структури для людей, які не хочуть писати спеціальні функції серіалізації та несеріалізації та не хочуть використовувати такі портативні серіалізатори, як соління.
Poikilos

27

dF: це досить круто ... Я не знав, що можу отримати доступ до полів у класі за допомогою dict.

Позначте: ситуації, які б я хотів, щоб це було саме в тому випадку, коли я хочу мати кортеж, але нічого такого «важкого», як словник.

Ви можете отримати доступ до полів класу за допомогою словника, оскільки поля класу, його методи та всі його властивості зберігаються всередині, використовуючи дикти (принаймні в CPython).

... Що призводить нас до вашого другого коментаря. Вважати, що дикти Python є "важкими" - це надзвичайно непітоністична концепція. І читання таких коментарів вбиває мого Python Zen. Це не добре.

Розумієте, коли ви оголошуєте клас, ви насправді створюєте досить складну обгортку навколо словника - тож, якщо нічого, ви додаєте більше накладних витрат, ніж за допомогою простого словника. Накладні витрати, які, до речі, безглузді в будь-якому випадку. Якщо ви працюєте над найважливішими додатками, використовуйте C або щось подібне.


5
№1, Cython! = CPython. Я думаю, ви говорили про CPython, про реалізацію Python, написаного на C, а не про Cython, про проект перетину компіляції коду Python у код C. Я відредагував вашу відповідь, щоб виправити це. # 2, я думаю, коли він сказав, що дикти важкі, він мав на увазі синтаксис. self['member']на 3 символи довше self.member, і всі ці символи відносно не підходять до зап'ястя.
ArtOfWarfare

19

Можна підкласирувати структуру С, яка доступна в стандартній бібліотеці. Модуль ctypes забезпечує клас Structure . Приклад із документів:

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

Однозначно перевірте документацію на слоти, але швидке пояснення слотів полягає в тому, що це спосіб сказати python: "Якщо ви можете заблокувати ці атрибути і лише ці атрибути в класі, щоб ви взяли на себе зобов’язання, що не додасте жодних нових атрибутів після класу примірник (так, ви можете додати нові атрибути до екземпляра класу, див. приклад нижче), тоді я позбудусь великого розподілу пам’яті, що дозволяє додавати нові атрибути до екземпляра класу та використовувати саме те, що мені потрібно для цих прорізів атрибутів, що ».

Приклад додавання атрибутів до екземпляра класу (при цьому не використовуються слоти):

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).
Джонатан Комар

10

Кожного разу, коли мені потрібен "миттєвий об'єкт даних, який також поводиться як словник" (я не думаю про структури С!), Я думаю про цей симпатичний хак:

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) ;-)
Марк Хорват

8

Ви отримуєте доступ до структури C-Style в python наступним чином.

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 (версія для декораторів також нижче).

Коли __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
>>> 

Чорт - Я витратив сьогодні дві години на написання власного декоратора, щоб це зробити, і тоді я це знайшов. У будь-якому разі, розміщувати мій, оскільки він обробляє значення за замовчуванням, а ваші - ні. stackoverflow.com/a/32448434/901641
ArtOfWarfare

+1 для згадування func_code. Почав копати в цьому напрямку і знайшов там багато цікавого.
вогнепальний вогник

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 посилання 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

Я не бачу цієї відповіді тут, тому вважаю, що додам її, оскільки я зараз схиляюсь до Python і лише відкрив її. Підручник з Python (у цьому випадку Python 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

Наступне рішення структури натхнене реалізацією названих пар та деякими попередніми відповідями. Однак, на відміну від nametuple, він є змінним за своїми значеннями, але подібно структурі стилю 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

Для цього є пакет python. див. cstruct2py

cstruct2pyє чистою бібліотекою python для генерування класів python із коду С та використання їх для упаковки та розпакування даних. Бібліотека може розбирати заголовки C (структури, об'єднання, перерахунки та масиви) та імітувати їх у python. Створені пітонічні класи можуть розбирати та упаковувати дані.

Наприклад:

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"

Як користуватись

Спочатку нам потрібно створити пітонічні структури:

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

Тепер ми можемо імпортувати всі імена з коду С:

parser.update_globals(globals())

Ми також можемо це зробити безпосередньо:

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

Використання типів та визначень із коду С

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

Я думаю, що словник структури Python підходить для цієї вимоги.

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

Якщо у вас немає 3,7 для @dataclass і вам потрібні зміни, наступний код може працювати для вас. Це досить самодокументоване та зручне для 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

NamedTuple - це зручно. але там ніхто не ділиться продуктивністю та сховищем.

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__інформації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.