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

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

383 голосов
/ 30 августа 2008

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

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

Ответы [ 22 ]

316 голосов
/ 30 августа 2008

Используйте с именем tuple , который был добавлен в модуль collection в стандартной библиотеке 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")
111 голосов
/ 01 августа 2017

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

С введением Классов данных в Python 3.7 мы очень близки.

Следующий пример аналогичен приведенному ниже примеру NamedTuple , но в результате получается объект mutable , и он допускает значения по умолчанию.

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 в сочетании с модулем typing - это находка!

Улучшено объявление 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'))
96 голосов
/ 30 августа 2008

Вы можете использовать кортеж для многих вещей, где вы будете использовать структуру в C (например, координаты x, y или цвета RGB).

Для всего остального вы можете использовать словарь или служебный класс, такой как этот :

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

Я думаю, что "окончательное" обсуждение - здесь , в опубликованной версии Python Cookbook.

79 голосов
/ 21 сентября 2010

Возможно, вы ищете структуры без конструкторов:

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
63 голосов
/ 30 августа 2008

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

Примерно так:

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

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

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

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

25 голосов
/ 30 августа 2008

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

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

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

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

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

16 голосов
/ 30 августа 2008

Вы также можете передать параметры 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
15 голосов
/ 12 апреля 2018

Я также хотел бы добавить решение, которое использует слотов :

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'

Это может эффективно работать как структура и использовать меньше памяти, чем класс (как это сделала бы структура, хотя я не исследовал, сколько именно). Рекомендуется использовать слоты, если вы будете создавать большое количество экземпляров объекта и не нужно добавлять атрибуты. Точечный объект является хорошим примером этого, поскольку вполне вероятно, что можно описать множество точек для описания набора данных.

14 голосов
/ 26 июня 2015

Вы можете создать подкласс структуры C, которая доступна в стандартной библиотеке. Модуль ctypes обеспечивает класс структуры . Пример из документов:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = [("x", c_int),
...                 ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print point.x, point.y
10 20
>>> point = POINT(y=5)
>>> print point.x, point.y
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: too many initializers
>>>
>>> class RECT(Structure):
...     _fields_ = [("upperleft", POINT),
...                 ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print rc.upperleft.x, rc.upperleft.y
0 5
>>> print rc.lowerright.x, rc.lowerright.y
0 0
>>>
9 голосов
/ 13 сентября 2013

Всякий раз, когда мне нужен «объект мгновенных данных, который также ведет себя как словарь» (я не думаю о структурах 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'])

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...