Структуры в C++ | OTUS
Один из способов определения пользовательских типов в языке программирования C++ заключается в использовании структур. Способ этот был унаследован еще от языка Си.
Структура — это производный тип данных, представляющий собой какую-либо конкретную определенную сущность, впрочем, как и класс. В связи с вышесказанным, применительно к языку C++ структуры нередко также называют классами. Говоря по правде, в реальной жизни различия между ними не так уж и велики.
Чтобы определить структуру, применяют ключевое слово struct. Что касается формата определения, то он выглядит так:
При этом Имя_структуры — это произвольный идентификатор, к которому применимы такие же правила, как и к наименованию переменных.
Далее после имени структуры в фигурных скобках помещают Компоненты_структуры — набор описаний объектов и функций, составляющих эту структуру.
Определение и инициализация
Рассмотрим, как это выглядит на примере. Определим простейшую структуру:
Итак, определена структура person, имеющая 2 элемента: age (тип int) и name (тип string).
После того, как структура определена, ее можно использовать. В начале можно определить объект структуры — речь идет, по сути, об обычной переменной, которая станет представлять созданный выше тип. Кроме того, после создания переменной структуры мы можем обращаться к ее элементам, получая их значения либо присваивая им новые значения. Чтобы обращаться к элементам структуры, используют операцию «точка»:
Опять же, повторимся, что по своей сути структура похожа на класс, что означает, что посредством структур мы тоже можем определять сущности в целях применения их в нашей программе. Одновременно с этим, все члены структуры, для которых не применяется спецификатор доступа (private, public), по дефолту являются открытыми (public). А в классе, как известно, все его члены, для которых спецификатор доступа не указан, являются закрытыми (private).
Идем дальше. У разработчика есть возможность инициализировать структуру путем присвоения ее переменным значений посредством синтаксиса инициализации:
Инициализация структур схожа с инициализацией массивов, для чего в фигурных скобках передают значения для элементов структуры по порядку. При этом, раз в структуре person первым определено свойство, представляющее тип int, то есть число, то и в скобках сначала идет число. Ну и так далее по порядку для всех элементов структуры.
Класс в виде структуры
Следующий момент — любой класс можно представить в качестве структуры и наоборот. Рассмотрим следующий класс:
Этот класс определяет сущность человека, а также содержит приватные и публичные переменные и функции. Однако для определения этой же сущности мы можем использовать вместо класса структуру:
При этом с точки зрения итогового результата работы программы большой разницы мы увидим.
Когда использовать структуры?
Обычно их используют при описании данных, имеющих лишь набор публичных атрибутов (речь идет об открытых переменных). К примеру, как структура person, определенная в начале статьи. В некоторых случаях такие сущности также называют aggregate classes.
По материалам сайта https://metanit.com/cpp.
Урок 23. Паттерн 15. Рост размеров структур
24 Янв 2012
Сам по себе рост размера структур не является ошибкой, но может приводить к потреблению необоснованного количества памяти и в результате к замедлению скорости работы программы. Будем рассматривать данный паттерн не как ошибку, но как причину неэффективности 64-битного кода.
Данные в структурах в языке Си++ выравниваются таким образом, чтобы обеспечить более эффективный к ним доступ. Некоторые микропроцессоры вообще не могут напрямую обращаться к не выровненным данным и компилятору приходиться генерировать специальный код для обращения к таким данным. Те же микропроцессоры, которые могут обращаться к не выровненным данным, все равно делают это намного менее эффективно. Поэтому компилятор Си++ оставляет пустые ячейки между полями структур, чтобы обеспечить их выравнивание по адресам машинных слов и тем самым ускорить к ним обращение. Можно отключить выравнивание, используя специальные директивы #pragma, чтобы сократить объем используемой памяти, но нас этот вариант сейчас не интересует. Часто можно значительно сократить объем расходуемой памяти простым изменением порядка полей в структуре, без потери производительности.
Рассмотрим следующую структуру:
struct MyStruct { bool m_bool; char *m_pointer; int m_int; };
На 32-битной системе эта структура займет 12 байт, и сократить этот размер не представляется возможным. Каждое поле выровнено по границе 4 байта. Даже если m_bool перенести в конец, это ничего не изменит. Компилятор все равно сделает размер структуры кратным 4 байтам для выравнивания таких структур в массивах.
В случае 64-битной сборки структура MyStruct займет уже 24 байта. Это понятно. В начале идет один байт под m_bool и 7 неиспользуемых байт для выравнивания, так как указатель занимает 8 байт и должен быть выровнен по границе 8 байт. Затем 4 байта для m_int и 4 неиспользуемых байта, для выравнивания структуры по границе 8 байт.
К счастью, дело можно легко поправить, переместив m_bool в конец структуры, как показано ниже:
struct MyStructOpt { char *m_pointer; int m_int; bool m_bool; };
Структура MyStructOpt займет уже не 24, а 16 байт. Визуально расположение полей представлено на рисунке 1. Весьма существенная экономия, если мы будем использовать, например, 10 миллионов элементов. В этом случае мы сэкономим 80 мегабайт памяти, но что еще более важно, можем повысить производительность. Если структур будет немного, то нет разницы, какого они размера. Доступ будет происходить с одинаковой скоростью. Но когда элементов много, то начинает играть роль кэш, количество обращений к памяти и так далее. И можно с уверенностью утверждать, что обработка 160 мегабайт данных займет меньше времени, чем 240 мегабайт. Даже простой доступ ко всем элементам массива для чтения, уже будет более быстр.
Рисунок 1 — Расположение полей в структурах MyStruct и MyStructOpt
Не всегда изменение последовательности полей в структурах возможно или удобно. Но если таких структур миллионы, то следует не пожалеть немного времени на рефакторинг. Результат такой простой оптимизации, как изменение последовательности полей, может дать весьма впечатляющие результаты.
Вы, наверное, зададите вопрос, по каким правилам компилятор выравнивает данные. Мы ответим кратко, а если интересно познакомиться с этой темой более подробно, то мы отсылаем вас к книге Джеффри Рихтер — Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows. Там этот вопрос рассматривается достаточно подробно.
В целом правило выравнивание следующее: каждое поле выравнивается по адресу, кратному размеру данного поля. Поле типа size_t на 64-битной системе будет выровнено по границе 8 байт, int по границе 4 байта, short по границе 2 байта. Поля типа char не выравниваются. Размер структуры выравнивается до размера, кратному размеру его максимального элемента. Поясним это выравнивание на примере:
struct ABCD { size_t m_a; char m_b; };
Элементы займут 8 + 1 = 9 байт. Но если размер структуры будет 9 байт, то, если мы захотим создать массив структур ABCD[2], поле m_a второй структуры будет лежать по не выровненному адресу. Вследствие этого компилятор дополнит структуру 7 пустыми байтами до размера 16 байт.
Может показаться сложным процесс оптимизации последовательности полей. Но можно предложить очень простой и очень эффективный способ. Достаточно расположить поля в порядке убывания их размера. Этого будет совершенно достаточно. В этом случае поля начнут располагаться без лишних зазоров. Например, возьмем следующую структуру размером 40 байт
struct MyStruct { int m_int; size_t m_size_t; short m_short; void *m_ptr; char m_char; };
и простой сортировкой последовательности полей по убыванию размера:
struct MyStructOpt { void *m_ptr; size_t m_size_t; int m_int; short m_short; char m_char; };
мы сделаем из нее структуру размером всего 24 байт.
Инструмент PVS-Studio позволяет обнаружить структуры в коде 64-битных приложений, перестановка полей в которых, позволит сократить их размер. На неоптимальные структуры анализатор выдает диагностическое сообщение V802.
Анализатор не всегда выдает сообщение о неэффективности структур, так как старается сократить количество излишних предупреждений. Например, анализатор не выдает предупреждение на сложные классы, являющимися наследниками, поскольку такие объекты обычно создаются в малом количестве. Пример:
class MyWindow : public CWnd { bool m_isActive; size_t m_sizeX, m_ sizeY; char m_color[3]; ... };
Размер данной структуры может быть сокращен, но это не имеет практического смысла.
Авторы курса: Андрей Карпов ([email protected]), Евгений Рыжков ([email protected]).
Правообладателем курса «Уроки разработки 64-битных приложений на языке Си/Си++» является ООО «Системы программной верификации». Компания занимается разработкой программного обеспечения в области анализа исходного кода программ. Сайт компании: http://www.viva64.com.
Для получения триального ключа
заполните форму ниже:
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных.
объявлений структур | Microsoft Learn
- Статья
- 4 минуты на чтение
«Объявление структуры» называет тип и определяет последовательность значений переменных (называемых «членами» или «полями» структуры), которые могут иметь разные типы. Необязательный идентификатор, называемый «тегом», дает имя типа структуры и может использоваться в последующих ссылках на тип структуры. Переменная этого типа структуры содержит всю последовательность, определенную этим типом. Структуры в C подобны типам, известным как «записи» в других языках.
Синтаксис
Структура-Ор-Университет-спецификатор :
Struct-Or-Union
Struct-Or-Union :
Struct
Union
Структура-декларация :
СТРУКЦИОННАЯ ДЕКЛАРАТАЦИО0020
список-объявлений-структур объявление-структур
объявление-структур :
список-описателей-описателей описание-структуры описание-0;
Спецификатор-квалификатор :
Тип-специфический. список :
Struct-Declarator Структура-декларатор-спир. выражение-константа
Объявление типа структуры не отводит место для структуры. Это всего лишь шаблон для последующих объявлений структурных переменных.
Ранее определенный идентификатор (тег) может использоваться для ссылки на тип структуры, определенный в другом месте. В этом случае struct-declaration-list не может быть повторен, пока определение видимо. Объявления указателей на структуры и определения типов для типов структур могут использовать тег структуры до определения типа структуры. Однако определение структуры должно встречаться до любого фактического использования размера полей. Это неполное определение типа и тега типа. Чтобы это определение было завершено, определение типа должно появиться позже в той же области.
struct-declaration-list определяет типы и имена членов структуры. Аргумент struct-declaration-list содержит одно или несколько объявлений переменных или битовых полей.
Каждая переменная, объявленная в списке struct-declaration-list , определяется как элемент типа структуры. Объявления переменных в struct-declaration-list имеют ту же форму, что и другие объявления переменных, обсуждаемые в этом разделе, за исключением того, что объявления не могут содержать спецификаторы класса хранения или инициализаторы. Члены структуры могут иметь любые типы переменных, кроме типа 9.0028 void
, неполный тип или тип функции.
Элемент не может быть объявлен как имеющий тип структуры, в которой он появляется. Однако член может быть объявлен как указатель на тип структуры, в которой он появляется, если у типа структуры есть тег. Это позволяет создавать связанные списки структур.
Структуры имеют ту же область видимости, что и другие идентификаторы. Идентификаторы структур должны отличаться от других тегов структур, объединений и перечислений с такой же видимостью.
Каждое объявление-структуры в списке -объявлений-структур
Доступ к вложенным структурам также можно получить, как если бы они были объявлены на уровне файловой области. Например, учитывая это объявление:
struct a { интервал х; структура б { инт у; } переменная2; } переменная1;
обе эти декларации допустимы:
struct a var3; структура б var4;
Примеры
Эти примеры иллюстрируют объявления структур:
struct employee /* Определяет структурную переменную с именем temp */ { имя персонажа[20]; внутренний идентификатор; длинный класс; } темп;
Структура сотрудника
состоит из трех элементов: имя
, идентификатор
и класс
. Элемент name
представляет собой массив из 20 элементов, а id
и class
являются простыми членами с типом int
и long
соответственно. Идентификатор сотрудника
является идентификатором структуры.
структура работник студент, преподаватель, персонал;
В этом примере определяются три структурные переменные: студент
, преподавательский состав
и персонал
. Каждая структура имеет одинаковый список из трех членов. Члены объявляются со структурным типом 9.0045 сотрудник , определенный в предыдущем примере.
struct /* Определяет анонимную структуру и */ { /* структурная переменная с именем комплекс */ плавать х, у; } сложный;
Структура комплекса
состоит из двух элементов типа float
, x
и y
. Тип структуры не имеет тега и поэтому является безымянным или анонимным.
образец структуры /* Определяет структуру с именем x */ { символ с; поплавок *пф; образец структуры *next; } Икс;
Первые два элемента структуры — переменная char
и указатель на значение с плавающей запятой
. Третий член, next
, объявлен как указатель на определяемый тип структуры ( пример
).
Анонимные структуры могут быть полезны, когда тег named не нужен. Это тот случай, когда одно объявление определяет все экземпляры структуры. Например:
структура { интервал х; инт у; } моя структура;
Встроенные конструкции часто анонимны.
структура { struct /* Анонимная структура */ { интервал х, у; } точка; тип int; } ш;
Специально для Microsoft
Компилятор допускает массив без размера или нулевого размера в качестве последнего члена структуры. Это может быть полезно, если размер константного массива различается при использовании в различных ситуациях. Объявление такой структуры выглядит так:
struct
идентификатор { набор объявлений тип имя-массива []; };
Массивы без размера могут появляться только как последний член структуры. Структуры, содержащие объявления неразмерных массивов, могут быть вложены в другие структуры, если в каких-либо окружающих структурах не объявлены дополнительные члены. Массивы таких структур не допускаются. Оператор sizeof
при применении к переменной этого типа или к самому типу принимает 0 в качестве размера массива.
Объявления структур также можно указывать без декларатора, если они являются членами другой структуры или объединения. Имена полей продвигаются во вмещающую структуру. Например, безымянная структура выглядит так:
struct s { плавать у; структура { в а, б, в; }; символ ул[10]; } *p_s; . . . p_s->b = 100; /* Ссылка на поле в структуре s */
Информацию о ссылках на структуры см. в разделе Структура и члены объединения. 9
Деклараторы и объявления переменных Задавать вопрос
спросил
Изменено 2 года, 4 месяца назад
Просмотрено 774k раз
617
Новинка! Сохраняйте вопросы или ответы и организуйте свой любимый контент.
Узнать больше.
Есть ли способ удобно определить C-подобную структуру в Python? Я устал писать такие вещи, как:
class MyStruct(): def __init__(я, поле1, поле2, поле3): само.поле1 = поле1 само.поле2 = поле2 self.field3 = поле3
- питон
- структура
10
Обновление : классы данныхС введением классов данных в Python 3.7 мы очень близки.
Следующий пример подобен приведенному ниже примеру NamedTuple , но результирующий объект является изменяемым и допускает значения по умолчанию.
из классов данных импортировать класс данных @dataclass Класс Точка: х: плавающий у: плавать г: число с плавающей запятой = 0,0 р = точка (1,5, 2,5) print(p) # Точка(x=1.5, y=2.5, z=0.0)
Это прекрасно сочетается с новым модулем набора текста, если вы хотите использовать более конкретные аннотации типов.
Я так ждал этого! Если вы спросите меня, Data Classes и новое объявление NamedTuple в сочетании с модулем typing — это находка!
Улучшенное объявление NamedTuple
Начиная с Python 3.6 стало довольно просто и красиво (ИМХО), пока можно жить с неизменностью .
Был представлен новый способ объявления NamedTuple, который также позволяет использовать аннотации типов:
из ввода import NamedTuple Пользователь класса (NamedTuple): название: ул. класс MyStruct (NamedTuple): фу: ул бар: инт баз: список qux: пользователь my_item = MyStruct('foo', 0, ['baz'], пользователь('питер')) print(my_item) # MyStruct(foo='foo', bar=0, baz=['baz'], qux=User(name='peter'))
9
Используйте именованный кортеж, который был добавлен в модуль коллекций стандартной библиотеки в Python 2.6. Также можно использовать рецепт именованного кортежа Раймонда Хеттингера, если вам нужна поддержка Python 2. 4.
Это хорошо для вашего базового примера, но также охватывает множество крайних случаев, с которыми вы можете столкнуться позже. Ваш фрагмент выше будет записан как:
из коллекций import namedtuple МояСтруктура = namedtuple("МояСтруктура", "поле1 поле2 поле3")
Вновь созданный тип можно использовать следующим образом:
m = MyStruct("foo", "bar", "baz")
Вы также можете использовать именованные аргументы:
m = MyStruct(field1="foo", field2="bar", field3="baz")
8
Вы можете использовать кортеж для многих вещей, где вы использовали бы структуру в C (например, что-то вроде координат x, y или цветов RGB).
Для всего остального вы можете использовать словарь или служебный класс, подобный этому:
>>> class Bunch: ... def __init__(self, **kwds): ... self.__dict__.update(kwds) ... >>> mystruct = Bunch (поле1=значение1, поле2=значение2)
Я думаю, что «окончательное» обсуждение находится здесь, в опубликованной версии Поваренной книги Python.
5
Возможно, вы ищете структуры без конструкторов:
класс Пример: имя = '' среднее = 0,0 values = None # Здесь нельзя инициализировать список! s1 = Образец () s1.name = "образец 1" s1.значения = [] s1.values.append(1) s1.values.append(2) s1.values.append(3) s2 = Образец () s2.name = "образец 2" s2.значения = [] s2.values.append(4) для v в s1.values: # печатает 1,2,3 --> ОК. напечатать v Распечатать "***" для v в s2.values: # печатает 4 --> OK. напечатать v
8
Как насчет словаря?
Примерно так:
myStruct = {'field1': 'some val', 'field2': 'some val'}
Затем вы можете использовать это для управления значениями:
print myStruct['field1'] myStruct['field2'] = 'некоторые другие значения'
И значения не обязательно должны быть строками. Они могут быть практически любым другим объектом.
2
dF: это довольно круто… я не знаю, что я мог получить доступ к полям в класс, использующий dict.
Марк: ситуации, которые я хотел бы иметь это именно тогда, когда я хочу кортеж но ничего такого «тяжелого», как толковый словарь.
Вы можете получить доступ к полям класса с помощью словаря, потому что поля класса, его методы и все его свойства хранятся внутри с помощью словарей (по крайней мере, в CPython).
…Что приводит нас к вашему второму комментарию. Вера в то, что словари Python «тяжелые», является крайне непитоновской концепцией. И чтение таких комментариев убивает мой Python Zen. Это не хорошо.
Видите ли, когда вы объявляете класс, вы на самом деле создаете довольно сложную оболочку для словаря, так что, во всяком случае, вы добавляете больше накладных расходов, чем при использовании простого словаря. Накладные расходы, которые, кстати, бессмысленны в любом случае. Если вы работаете над приложениями, критически важными для производительности, используйте C или что-то в этом роде.
1
Я также хотел бы добавить решение, использующее слоты:
class Point: __slots__ = ["х", "у"] def __init__(я, х, у): я.х = х селф.у = у
Определенно проверьте документацию по слотам, но краткое объяснение слотов заключается в том, что это способ python сказать: «Если вы можете заблокировать эти атрибуты и только эти атрибуты в классе таким образом, что вы фиксируете, что вы не будете добавлять новые атрибуты один раз создается экземпляр класса (да, вы можете добавить новые атрибуты в экземпляр класса, см. пример ниже), тогда я избавлюсь от выделения большого объема памяти, позволяющего добавлять новые атрибуты в экземпляр класса, и использовать только то, что мне нужно для этих slotted атрибутов».
Пример добавления атрибутов к экземпляру класса (без использования слотов):
class Point: def __init__(я, х, у): я. х = х селф.у = у p1 = точка (3,5) p1.z = 8 печать (p1.z)
Вывод: 8
Пример попытки добавить атрибуты в экземпляр класса, где использовались слоты:
class Point: __slots__ = ["х", "у"] def __init__(я, х, у): я.х = х селф.у = у p1 = точка (3,5) p1.z = 8
Вывод: AttributeError: объект «Point» не имеет атрибута «z»
Это может эффективно работать как структура и использовать меньше памяти, чем класс (как и структура, хотя я точно не исследовал, сколько именно). Рекомендуется использовать слоты, если вы будете создавать большое количество экземпляров объекта и вам не нужно добавлять атрибуты. Точечный объект является хорошим примером этого, поскольку вполне вероятно, что можно создать множество точек для описания набора данных.
1
Вы можете создать подкласс структуры C, доступной в стандартной библиотеке. Модуль ctypes предоставляет класс Structure. Пример из документации:
>>> from ctypes import * >>> класс POINT(Структура): ... _fields_ = [("x", c_int), ... ("у", c_int)] ... >>> точка = ПУНКТ(10, 20) >>> вывести точку.x, точку.y 10 20 >>> точка = ПУНКТ(y=5) >>> вывести точку.x, точку.y 0 5 >>> ТОЧКА(1, 2, 3) Traceback (последний последний вызов): Файл "", строка 1, в ? ValueError: слишком много инициализаторов >>> >>> класс RECT(Структура): ... _fields_ = [("верхний левый", POINT), ... ("внизу справа", POINT)] ... >>> rc = RECT(точка) >>> напечатать rc.upperleft.x, rc.upperleft.y 0 5 >>> напечатать rc.lowerright.x, rc.lowerright.y 0 0 >>>
0
Вы также можете передать параметры инициализации в переменные экземпляра по позиции
# Класс абстрактной структуры Структура класса: def __init__ (я, *argv, **argd): если длина (аргумент): # Обновление по словарю self.__dict__.update (argd) еще: # Обновление по позиции attrs = фильтр (лямбда x: x[0:2] != "__", dir(self)) для n в диапазоне (len (argv)): setattr(self, attrs[n], argv[n]) # Конкретный класс класс Point3dStruct (Структура): х = 0 у = 0 г = 0 pt1 = Point3dStruct() pt1. x = 10 распечатать pt1.x напечатать "-"*10 pt2 = Point3dStruct(5, 6) распечатать pt2.x, pt2.y напечатать "-"*10 pt3 = Point3dStruct (x=1, y=2, z=3) напечатать pt3.x, pt3.y, pt3.z напечатать "-"*10
2
Всякий раз, когда мне нужен «мгновенный объект данных, который также ведет себя как словарь» (я не не думаю о структурах C!), я думаю об этом милом хаке:
class Map(dict): def __init__(я, **kwargs): super(Map, self).__init__(**kwargs) self.__dict__ = я
Теперь вы можете просто сказать:
struct = Map(field1='foo', field2='bar', field3=42) self.assertEquals('bar', struct.field2) self.assertEquals (42, структура ['field3'])
Идеально подходит для тех случаев, когда вам нужен «пакет данных, который НЕ является классом», и когда именованные кортежи непонятны…
1
Некоторые ответы здесь очень сложные. Самый простой вариант, который я нашел, это (из: http://norvig.com/python-iaq.html):
class Struct: «Структура, в которой могут быть определены любые поля». def __init__(self, **entries): self.__dict__.update(entries)
Инициализация:
>>> options = Struct(answer=42, linelen=80, font='courier') >>> варианты.ответ 42
добавив еще:
>>> options.cat = "собака" >>> варианты.cat собака
изменить: Извините, что не видел этот пример ниже.
2
Вы получаете доступ к структуре C-Style в python следующим образом.
класс cstruct: переменная_i = 0 переменная_f = 0,0 var_str = ""
объект = cstruct () obj.var_i = 50 obj.var_f = 50,00 obj.var_str = "пятьдесят" print "cstruct: obj i=%d f=%f s=%s" %(obj.var_i, obj.var_f, obj.var_str)
obj_array = [cstruct() для i в диапазоне (10)] obj_array[0].var_i = 10 obj_array[0]. var_f = 10,00 obj_array[0].var_str = "десять" # идем дальше и заполняем оставшиеся экземпляры массива структурой #распечатать все значение для я в диапазоне (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 определите переменную-член вашей структуры.
1
Это может быть немного поздно, но я сделал решение, используя метаклассы Python (версия декоратора также ниже).
Когда __init__
вызывается во время выполнения, он захватывает каждый из аргументов и их значение и назначает их как переменные экземпляра вашему классу. Таким образом, вы можете создать структуроподобный класс без необходимости вручную назначать каждое значение.
В моем примере нет проверки ошибок, поэтому его легче понять.
класс MyStruct (тип): def __call__(cls, *args, **kwargs): имена = cls. __init__.func_code.co_varnames[1:] self = type.__call__(cls, *args, **kwargs) для имени, значение в zip (имена, аргументы): setattr(я, имя, значение) для имени значение в kwargs.iteritems(): setattr(я, имя, значение) вернуть себя
Вот он в действии.
>>> класс MyClass(объект): __metaclass__ = МояСтруктура def __init__(я, а, б, в): проходить >>> мой_экземпляр = МойКласс(1, 2, 3) >>> my_instance.a 1 >>>
Я разместил его на Reddit, а /u/matchu опубликовал более чистую версию декоратора. Я бы посоветовал вам использовать его, если вы не хотите расширять версию метакласса.
>>> по умолчанию init_all_args(fn): @обертки(fn) def wrapper_init(self, *args, **kwargs): имена = fn.func_code.co_varnames[1:] для имени, значение в zip (имена, аргументы): setattr(я, имя, значение) для имени значение в kwargs.iteritems(): setattr(я, имя, значение) вернуть завернутый_инит >>> Тест класса (объект): @init_all_args def __init__(я, а, б): проходить >>> а = тест (1, 2) >>> а. а. 1 >>>
2
Я написал декоратор, который можно использовать для любого метода, чтобы все переданные аргументы или любые значения по умолчанию присваивались экземпляру.
def argumentsToAttributes (метод): имя_аргумента = method.func_code.co_varnames[1:] # Создать словарь значений по умолчанию: defaultsDict = {} defaults = method.func_defaults if method.func_defaults else () для i значение по умолчанию в перечислении (значения по умолчанию, начало = len (имена аргументов) - len (значения по умолчанию)): defaultsDict[имя_аргумента[i]] = по умолчанию def newMethod(self, *args, **kwargs): # Используйте позиционные аргументы. для имени, значение в zip(argumentNames, args): setattr(я, имя, значение) # Добавьте аргументы ключевого слова. Если чего-то не хватает, используйте значение по умолчанию. для имени в argumentsNames[len(args):]: setattr (я, имя, kwargs. get (имя, defaultsDict [имя])) # Запустить все, что еще нужно сделать методу. метод(я, *args, **kwargs) вернуть новый метод
Быстрая демонстрация. Обратите внимание, что я использую позиционный аргумент a
, использую значение по умолчанию для b
и именованный аргумент c
. Затем я печатаю все 3 ссылки на self
, чтобы показать, что они были правильно назначены до ввода метода.
класс А(объект): @argumentsToAttributes def __init__(self, a, b = 'Невидимый', c = 'Привет'): распечатать (я) распечатать (я.б) печать (я.с) А('Почему', с = 'Ничего')
Обратите внимание, что мой декоратор должен работать с любым методом, а не только с __init__
.
Я не вижу здесь этого ответа, поэтому думаю, что добавлю его, так как сейчас я склоняюсь к Python и только что обнаружил его. Учебное пособие по Python (в данном случае Python 2) дает следующий простой и эффективный пример:
class Employee: проходить john = Employee() # Создать пустую запись сотрудника # Заполняем поля записи john. name = 'Джон Доу' john.dept = 'компьютерная лаборатория' джон.зарплата = 1000
То есть создается пустой объект класса, затем создается его экземпляр, а поля добавляются динамически.
Преимущество этого в том, что он действительно прост. Недостатком является то, что он не особенно самодокументирован (предполагаемые члены нигде не перечислены в «определении» класса), а неустановленные поля могут вызвать проблемы при доступе. Эти две проблемы могут быть решены с помощью:
class Сотрудник: защита __init__ (сам): self.name = None # или что-то подобное self.dept = Нет self.salary = Нет
Теперь с первого взгляда можно хотя бы увидеть, какие поля будет ожидать программа.
Оба склонны к опечаткам, john.slarly = 1000
получится. Тем не менее, это работает.
Вот решение, которое использует класс (никогда не созданный) для хранения данных. Мне нравится, что этот способ почти не требует набора текста и не требует дополнительных пакетов и т. д.
class myStruct: поле1 = "один" поле2 = "2"
При необходимости вы можете добавить дополнительные поля позже:
myStruct.field3 = 3
Для получения значений к полям обращаются как обычно:
>>> myStruct.field1 'один'
4
Лично мне этот вариант тоже нравится. Он расширяет ответ @dF.
структура класса: def __init__(self, *sequential, **named): fields = dict(zip(sequential, [None]*len(sequential)), **named) self.__dict__.update(поля) защита __repr__(сам): вернуть строку (я.__dict__)
Поддерживает два режима инициализации (которые можно комбинировать):
# Структура с полем1, полем2, полем3, которые инициализируются значением Нет. mystruct1 = struct("поле1", "поле2", "поле3") # Структура с field1, field2, field3, которые инициализируются в соответствии с аргументами. mystruct2 = структура (поле1 = 1, поле2 = 2, поле3 = 3)
Кроме того, он печатает лучше:
print(mystruct2) # Выводит: {'field3': 3, 'field1': 1, 'field2': 2}
Именно для этой цели существует пакет python. см. cstruct2py
cstruct2py
— это чистая библиотека Python для создания классов Python из кода C и использования их для упаковки и распаковки данных. Библиотека может анализировать заголовки C (объявления структур, объединений, перечислений и массивов) и эмулировать их в python. Сгенерированные классы Python могут анализировать и упаковывать данные.
Например:
typedef struct { интервал х; инт у; } Точка; после создания класса pythonic... р = точка (х = 0x1234, у = 0x5678) p.packed == "\x34\x12\x00\x00\x78\x56\x00\x00"
Как пользоваться
Сначала нам нужно сгенерировать pythonic структуры:
import cstruct2py парсер = cstruct2py.c2py.Parser() parser.parse_file('примеры/example.h')
Теперь мы можем импортировать все имена из кода C:
parser.update_globals(globals())
Мы также можем сделать это напрямую:
A = parser.parse_string('struct A { int x; int y;};')
Использование типов и определений из кода C
a = A() ах = 45 распечатать buf = a. packed б = А (баф) печатать б с = А('аааа11112222', 2) печатать с печать репр(с)
Вывод будет:
{'x':0x2d, 'y':0x0} {'х': 0x2d, 'у': 0x0} {'х': 0x31316161, 'у': 0x32323131} А('аа111122', х=0x31316161, у=0x32323131)
Клон
Для клона cstruct2py
запустите:
git clone https://github.com/st0ky/cstruct2py.git --recursive
0
Вот быстрый и грязный трюк:
>>> ms = Warning() >>> мс.foo = 123 >>> ms.bar = 'акафрит'
Как это работает? Он просто повторно использует встроенный класс Warning
(производный от Exception
) и использует его так, как это был ваш собственный определенный класс.
Положительные моменты в том, что вам не нужно ничего сначала импортировать или определять, что «Предупреждение» — это короткое имя, и это также дает понять, что вы делаете что-то грязное, что не должно использоваться где-либо еще, кроме вашего небольшого скрипта. .
Кстати, я пытался найти что-то еще проще, например, ms = object()
, но не смог (последний пример не работает). Если у вас есть, мне интересно.
0
NamedTuple удобен. но там производительность и память никто не разделяет.
от ввода импорта NamedTuple import guppy # pip install guppy импортировать время Пользователь класса: def __init__(self, name: str, uid: int): self.name = имя self.uid = идентификатор пользователя класс UserSlot: __slots__ = ('имя', 'идентификатор пользователя') def __init__(self, name: str, uid: int): self.name = имя self.uid = идентификатор пользователя класс UserTuple (NamedTuple): # __slots__ = () # AttributeError: невозможно перезаписать атрибут NamedTuple __slots__ название: ул. идентификатор: интервал def get_fn(obj, attr_name: str): деф получить(): getattr(obj, attr_name) вернуть получить
, если «проверка памяти»: obj = [User('Carson', 1) for _ in range(1000000)] # Всего: 189138883 obj_slot = [UserSlot('Carson', 1) for _ in range(1000000)] # 77718299 <-- победитель obj_namedtuple = [UserTuple('Carson', 1) for _ in range(1000000)] # 85718297 print(guppy. hpy().heap()) # Запускаем эту функцию отдельно. """ Количество индексов % Размер % Совокупный % Вид (класс / определение класса) 0 1000000 24 112000000 34 112000000 34 dict of __main__.User 1 1000000 24 64000000 19176000000 53 __main__.UserTuple 2 1000000 24 56000000 17 232000000 70 __main__.Пользователь 3 1000000 24 56000000 17 288000000 87 __main__.UserSlot ... """ если «тест производительности»: obj = Пользователь('Карсон', 1) obj_slot = UserSlot('Карсон', 1) obj_tuple = UserTuple('Карсон', 1) time_normal = мин (timeit.repeat (get_fn (объект, 'имя'), повтор = 20)) print(time_normal) # 0.12550550000000005 time_slot = min (timeit.repeat (get_fn (obj_slot, 'имя'), повтор = 20)) печать (временной_слот) # 0.13686000008 time_tuple = мин (timeit.repeat (get_fn (obj_tuple, 'имя'), повтор = 20)) print(time_tuple) # 0.16006120000000124 print(time_tuple/time_slot) # 1.1694481584580898 # Слот почти на 17% быстрее, чем NamedTuple в Windows.
(Питон 3.7.7)
Если ваш __dict__
не используется, выберите между __slots__
(более высокая производительность и хранилище) и NamedTuple
(очистить для чтения и использования)
Вы можете просмотреть эту ссылку (использование слотов ), чтобы получить больше __slots__
информации.
https://stackoverflow.com/a/32448434/159695 не работает в Python3.
https://stackoverflow.com/a/35993/159695 работает в Python3.
И я расширяю его, добавляя значения по умолчанию.
класс myStruct: def __init__(я, **kwds): селф.х=0 self.__dict__.update(kwds) # Должен быть последним, чтобы принять назначенную переменную-член. защита __repr__(сам): args = ['%s=%s' % (k, repr(v)) для (k,v) в vars(self).items()] вернуть '%s(%s)' % ( self.__class__.__qualname__, ', '.join(args)) а=мояСтруктура() б = моя структура (х = 3, у = 'тест') c=myStruct(x='str') >>> а моя структура (х = 0) >>> б myStruct(x=3, y='тест') >>> с мояСтруктура(х='строка')
1
Следующее решение структуры основано на реализации namedtuple и некоторых предыдущих ответах. Однако, в отличие от namedtuple, он является изменчивым в своих значениях, но, как и структура c-стиля, неизменяемой в именах/атрибутах, чего нет в обычном классе или словаре.
_class_template = """\ класс {имя_типа}: def __init__(я, *args, **kwargs): поля = {имена_полей!r} для х в полях: setattr(я, х, нет) для имени, значение в zip (поля, аргументы): setattr(я, имя, значение) для имени значение в kwargs.items(): setattr(я, имя, значение) защита __repr__(сам): вернуть ул (вары (сам)) def __setattr__(я, имя, значение): если имя отсутствует в {field_names!r}: поднять KeyError("недопустимое имя: %s" % имя) object.__setattr__(я, имя, значение) """ структура def (имя_типа, имена_полей): class_definition = _class_template.format( имя_типа = имя_типа, имена_полей = имена_полей) namespace = dict(__name__='struct_%s' % typename) exec (определение_класса, пространство имен) результат = пространство имен [имя типа] результат. _источник = определение_класса вернуть результат
Использование:
Person = struct('Person', ['firstname','lastname']) универсальный = человек () Майкл = Человек('Майкл') Джонс = Человек (фамилия = 'Джонс') В [168]: michael.middlename = 'бен' Traceback (последний последний вызов): Файл "", строка 1, в michael.middlename = 'бен' Файл " ", строка 19, в __setattr__ KeyError: 'недопустимое имя: отчество'
Если у вас нет 3.7 для @dataclass и вам нужна изменяемость, вам может подойти следующий код. Он достаточно самодокументирован и удобен для IDE (автозаполнение), предотвращает повторную запись, легко расширяется, и очень просто проверить, что все переменные экземпляра полностью инициализированы:
класс Params(): защита __init__(сам): self.var1 : int = Нет self.var2 : ул = Нет защита are_all_defined (я): для ключа, значение в self.__dict__.items(): assert (значение не None), "переменная экземпляра {} по-прежнему None".