C-подобные структуры в Python

447

Есть ли способ удобно определить C-подобную структуру в Python? Я устал писать такие вещи, как:

class MyStruct():
    def __init__(self, field1, field2, field3):
        self.field1 = field1
        self.field2 = field2
        self.field3 = field3
WESC
источник
5
В некотором роде алгебраические типы данных были бы абсолютно замечательными, но для их правильного использования обычно требуется сопоставление с образцом.
Эдвард З. Ян
51
Что-то не так с этим методом, кроме как утомительно писать?
Левеск
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. Также возможно использовать именованный кортеж Раймонда Хеттингера рецепт если вам нужна поддержка 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")
GZ.
источник
164
... но именованный кортеж неизменен. Пример в ОП изменчив.
Mhowison
28
@mhowison - В моем случае это просто плюс.
ArtOfWarfare
3
Хорошее решение. Как бы вы перебрали массив этих кортежей? Я бы предположил, что поля 1-3 должны иметь одинаковые имена для всех объектов кортежа.
Майкл Смит
2
namedtuple может иметь не более четырех аргументов, поэтому как мы можем отобразить структуру с большим количеством членов данных с соответствующим namedtuple
Kapil
3
@Kapil - Второй аргумент namedtuple должен быть списком имен членов. Этот список может быть любой длины.
ArtOfWarfare,
228

Обновить : классы данных

С введением классов данных в 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 он стал довольно простым и красивым (ИМХО), пока можно жить с неизменностью .

Был представлен новый способ объявления 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'))
Rotareti
источник
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 Cookbook.

дР.
источник
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
Это так же близко, как и к struct - short 'class' без методов, 'fields' (атрибуты класса, я знаю) со значениями по умолчанию. Пока это не изменяемый тип (dict, list), все в порядке. Конечно, вы можете выполнить проверку PEP-8 или «дружественные» проверки IDE, например, у «класса PyCharm нет метода init ».
Томаш Гандор
4
Я экспериментировал с побочным эффектом, описанным Ченнингом Муром. Не стоит экономия нескольких selfключевых слов и строки конструктора, если вы спросите меня. Я был бы признателен, если бы Хосе мог отредактировать свой ответ, добавив предупреждающее сообщение о риске случайного обмена значениями между экземплярами.
Стефан С.
@ChanningMoore: Я пытался воссоздать проблему, которую вы описывали, но не удалось. Не могли бы вы представить минимальный рабочий пример, где проблема всплывает?
gebbissimo
67

Как насчет словаря?

Что-то вроде этого:

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

Затем вы можете использовать это для управления значениями:

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

И значения не должны быть строками. Они могут быть практически любым другим объектом.

Марк Бик
источник
34
Это был и мой подход, но я чувствую, что это опасно именно потому, что словарь может принять что-нибудь за ключ. Не будет ошибки, если я установлю myStruct ["ffield"], когда я собираюсь установить myStruct ["field"]. Проблема может (или не может) стать очевидной, когда я использую или повторно использую myStruct ["field"] позже. Мне нравится подход PabloG.
mobabo
Та же проблема существует с PabloG. Попробуйте добавить к нему следующий код: pt3.w = 1 print pt3.w на языке с dicts лучше использовать их, особенно для сериализуемых объектов, поскольку вы можете автоматически использовать импорт json для их сохранения и других библиотек сериализации, если у вас нет странных вещи внутри вашего диктата. Dicts - это решение, позволяющее хранить данные и логику отдельно, и они лучше, чем структуры для людей, которые не хотят писать собственные функции сериализации и десериализации и не хотят использовать непортативные сериализаторы, такие как pickle.
Пойкилос
27

dF: это круто ... я не знал, что могу получить доступ к полям в классе с помощью dict.

Марк: ситуации, в которых я хотел бы, чтобы это было именно тогда, когда я хочу кортеж, но ничего более «тяжелого», чем словарь.

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

... Что приводит нас к вашему второму комментарию. Вера в то, что Python dicts «тяжелые», является крайне непитонистской концепцией. И чтение таких комментариев убивает мой Python Zen. Это не хорошо.

Видите ли, когда вы объявляете класс, вы на самом деле создаете довольно сложную оболочку вокруг словаря - так что, если что-нибудь, вы добавляете больше накладных расходов, чем при использовании простого словаря. Накладные расходы, которые, кстати, бессмысленны в любом случае. Если вы работаете с приложениями, критичными к производительности, используйте C или что-то в этом роде.

Висент Марти
источник
5
# 1, Cython! = CPython. Я думаю, что вы говорили о CPython, реализации Python, написанной на C, а не Cython, проекте по кросс-компиляции кода Python в C-код. Я отредактировал ваш ответ, чтобы исправить это. # 2, я думаю, когда он сказал, что диктат тяжелый, он имел в виду синтаксис. self['member']на 3 символа длиннее self.member, и все эти символы относительно дружелюбны.
ArtOfWarfare
19

Вы можете создать подкласс структуры C, которая доступна в стандартной библиотеке. Модуль 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
PabloG
источник
7
Обновление по позиции игнорирует порядок объявления атрибутов и использует вместо них их алфавитную сортировку. Так что если вы измените порядок строк в Point3dStructобъявлении, Point3dStruct(5, 6)не будет работать, как ожидалось. Странно, что никто не написал это за все 6 лет.
ляпис
Не могли бы вы добавить версию Python 3 в ваш удивительный код? Отличная работа! Мне нравится, что вы берете что-то абстрактное и делаете это явным со вторым конкретным классом. Это должно быть хорошо для обработки ошибок / отлова. Для Python 3 просто измените print> print()и attrs[n]> next(attrs)(фильтр теперь является собственным итерируемым объектом и требует next).
Джонатан Комар
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'])

Идеально подходит для тех случаев, когда вам нужна «сумка данных, которая НЕ является классом», и когда именованные кортежи непостижимы ...

Phlip
источник
Я использую 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

редактировать: извините, не видел этот пример уже внизу.

w_jay
источник
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
>>> 
user124757
источник
Черт возьми - сегодня я потратил два часа на написание своего собственного декоратора, а потом нашел это. В любом случае, опубликовать мой, потому что он обрабатывает значения по умолчанию, а ваш нет. stackoverflow.com/a/32448434/901641
ArtOfWarfare
+1 за упоминание func_code. Начал копать в этом направлении и нашел там много интересного.
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 ссылки 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__.

ArtOfWarfare
источник
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'
Jochen
источник
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}
normanius
источник
2

Следующее решение для структуры вдохновлено реализацией namedtuple и некоторыми из предыдущих ответов. Однако, в отличие от namedtuple, он является изменяемым по своим значениям, но, как и структура c-style, неизменным в именах / атрибутах, чего нет у обычного класса или 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

Применение:

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'
PS1
источник
2

Для этого есть пакет python. смотри cstruct2py

cstruct2pyявляется чистой библиотекой Python для генерации классов Python из кода C и использования их для упаковки и распаковки данных. Библиотека может анализировать заголовки 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')

Теперь мы можем импортировать все имена из кода 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

Я думаю, что словарь структуры 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)
gebbissimo
источник
0

Вот быстрый и подвох:

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

Как это работает? Он просто повторно использует встроенный класс Warning(производный от Exception) и использует его так, как он был определен вами.

Положительным моментом является то, что вам не нужно сначала что-либо импортировать или определять, что «Предупреждение» - это короткое имя, и оно также дает понять, что вы делаете что-то грязное, что не должно использоваться где-либо еще, кроме небольшого сценария.

Кстати, я пытался найти что-то еще проще, ms = object()но не смог (последний пример не работает). Если у вас есть, мне интересно.

calandoa
источник
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]

Tioneb
источник
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__информации.

Carson
источник