55

为什么 Python 本身不支持记录类型?这是一个可变版本的命名元组的问题。

我可以使用namedtuple._replace. 但是我需要将这些记录放在一个集合中,并且由于namedtuple._replace创建了另一个实例,我还需要修改这个很快就会变得混乱的集合。

背景:我有一个设备,我需要通过 TCP/IP 轮询它来获取其属性。即它的表示是一个可变对象。

编辑:我有一组需要轮询的设备。

编辑:我需要使用 PyQt 遍历显示其属性的对象。我知道我可以添加特殊方法,例如__getitem__and __iter__,但我想知道是否有更简单的方法。

编辑:我更喜欢属性固定的类型(就像它们在我的设备中一样),但是是可变的。

4

11 回答 11

53

蟒蛇 <3.3

你的意思是这样的?

class Record(object):
    __slots__= "attribute1", "attribute2", "attribute3",

    def items(self):
        "dict style items"
        return [
            (field_name, getattr(self, field_name))
            for field_name in self.__slots__]

    def __iter__(self):
        "iterate over fields tuple/list style"
        for field_name in self.__slots__:
            yield getattr(self, field_name)

    def __getitem__(self, index):
        "tuple/list style getitem"
        return getattr(self, self.__slots__[index])

>>> r= Record()
>>> r.attribute1= "hello"
>>> r.attribute2= "there"
>>> r.attribute3= 3.14

>>> print r.items()
[('attribute1', 'hello'), ('attribute2', 'there'), ('attribute3', 3.1400000000000001)]
>>> print tuple(r)
('hello', 'there', 3.1400000000000001)

请注意,提供的方法只是可能方法的一个示例。

Python ≥3.3 更新

您可以使用types.SimpleNamespace

>>> import types
>>> r= types.SimpleNamespace()
>>> r.attribute1= "hello"
>>> r.attribute2= "there"
>>> r.attribute3= 3.14

dir(r)将为您提供属性名称(.startswith("__")当然,过滤掉所有)。

于 2011-03-30T20:08:34.557 回答
18

你有什么理由不能使用普通字典吗?在您的特定情况下,这些属性似乎没有特定的顺序。

或者,您也可以使用类实例(具有良好的属性访问语法)。__slots__如果您希望避免__dict__为每个实例创建一个,则可以使用。

我还刚刚找到了"records" 的配方,它被描述为可变的命名元组。它们是使用类实现的。

更新:

由于您说顺序对您的方案很重要(并且您想遍历所有属性),OrderedDict因此似乎是要走的路。这是collectionsPython 2.7 标准模块的一部分;对于 Python < 2.7,互联网上还有其他实现。

要添加属性样式访问,您可以像这样子类化它:

from collections import OrderedDict

class MutableNamedTuple(OrderedDict):
    def __init__(self, *args, **kwargs):
        super(MutableNamedTuple, self).__init__(*args, **kwargs)
        self._initialized = True

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        if hasattr(self, '_initialized'):
            super(MutableNamedTuple, self).__setitem__(name, value)
        else:
            super(MutableNamedTuple, self).__setattr__(name, value)

然后你可以这样做:

>>> t = MutableNamedTuple()
>>> t.foo = u'Crazy camels!'
>>> t.bar = u'Yay, attribute access'
>>> t.foo
u'Crazy camels!'
>>> t.values()
[u'Crazy camels!', u'Yay, attribute access']
于 2011-03-08T03:05:12.930 回答
11

这可以使用一个空类及其实例来完成,如下所示:

>>> class a(): pass
... 
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>> 
于 2011-03-08T03:19:23.570 回答
10

有一个类似于 namedtuple 但可变的库,称为记录类型。

包首页: http: //pypi.python.org/pypi/recordtype

简单的例子:

from recordtype import recordtype

Person = recordtype('Person', 'first_name last_name phone_number')
person1 = Person('Trent', 'Steele', '637-3049')
person1.last_name = 'Terrence';

print person1
# Person(first_name=Trent, last_name=Terrence, phone_number=637-3049)

简单的默认值示例:

Basis = recordtype('Basis', [('x', 1), ('y', 0)])

person1按顺序遍历 的字段:

map(person1.__getattribute__, Person._fields)
于 2013-01-15T13:08:55.720 回答
4

这个答案重复了另一个collections.namedtuple-recordclass有一个可变的替代方案

它具有相同的 API 和最小的内存占用(实际上它也更快)。它支持任务。例如:

from recordclass import recordclass

Point = recordclass('Point', 'x y')

>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)

有更完整的示例(它还包括性能比较)。

于 2015-04-30T17:41:44.387 回答
4

这个问题很老,但为了完整起见,Python 3.7 的数据类几乎是记录。

>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class MyRecord:
...     name: str
...     age: int = -1
...
>>> rec = MyRecord('me')
>>> rec.age = 127
>>> print(rec)
MyRecord(name='me', age=127)

attrs 第三方库为 Python 2 和 Python 3 提供了更多功能。如果要求更多的是您无法在本地保存的东西,而不是专门使用 stdlib,那么供应商依赖关系也没有错。dephell 有一个很好的帮手。

于 2020-08-23T06:26:06.450 回答
1

在 Python 中密切相关的 Existence of mutable named tuple?问题 13 测试用于比较namedtuple.

截至 2016 年 1 月 11 日,最新的namedlist 1.7通过Python 2.7 和 Python 3.5 的所有这些测试。它是一个纯 Python 实现。

根据这些测试,第二好的候选者recordclass是 C 扩展。当然,是否首选 C 扩展取决于您的要求。

有关更多详细信息,尤其是对于测试,请参阅Python 中是否存在可变命名元组?

于 2016-01-11T14:35:47.557 回答
0

你可以做类似这个dict子类的事情,它是它自己的__dict__。基本概念与 ActiveState AttrDict配方相同,但实现更简单。结果比您需要的更易变,因为实例的属性和它们的值都是可变的。尽管属性没有排序,但您可以遍历当前属性和/或它们的值。

class Record(dict):
    def __init__(self, *args, **kwargs):
        super(Record, self).__init__(*args, **kwargs)
        self.__dict__ = self
于 2013-03-16T07:52:22.297 回答
0

基于随着时间的推移收集的几个有用的技巧,这个“frozenclass”装饰器几乎完成了所有需要的事情:http: //pastebin.com/fsuVyM45

由于该代码超过 70% 是文档和测试,所以我不会在这里多说。

于 2013-12-10T20:18:40.427 回答
0

这是我制作的一个完整的可变命名元组,它的行为就像一个列表并且完全兼容它。

class AbstractNamedArray():
    """a mutable collections.namedtuple"""
    def __new__(cls, *args, **kwargs):
        inst = object.__new__(cls)  # to rename the class
        inst._list = len(cls._fields)*[None]
        inst._mapping = {}
        for i, field in enumerate(cls._fields):
            inst._mapping[field] = i
        return inst

    def __init__(self, *args, **kwargs):
        if len(kwargs) == 0 and len(args) != 0:
            assert len(args) == len(self._fields), 'bad number of arguments'
            self._list = list(args)
        elif len(args) == 0 and len(kwargs) != 0:
            for field, value in kwargs.items():
                assert field in self._fields, 'field {} doesn\'t exist'
                self._list[self._mapping[field]] = value
        else:
            raise ValueError("you can't mix args and kwargs")

    def __getattr__(self, x):
        return object.__getattribute__(self, '_list')[object.__getattribute__(self, '_mapping')[x]]

    def __setattr__(self, x, y):
        if x in self._fields:
            self._list[self._mapping[x]] = y
        else:
            object.__setattr__(self, x, y)

    def __repr__(self):
        fields = []
        for field, value in zip(self._fields, map(self.__getattr__, self._fields)):
            fields.append('{}={}'.format(field, repr(value)))
        return '{}({})'.format(self._name, ', '.join(fields))

    def __iter__(self):
        yield from self._list

    def __list__(self):
        return self._list[:]

    def __len__(self):
        return len(self._fields)

    def __getitem__(self, x):
        return self._list[x]

    def __setitem__(self, x, y):
        self._list[x] = y

    def __contains__(self, x):
        return x in self._list

    def reverse(self):
        self._list.reverse()

    def copy(self):
        return self._list.copy()


def namedarray(name, fields):
    """used to construct a named array (fixed-length list with named fields)"""
    return type(name, (AbstractNamedarray,), {'_name': name, '_fields': fields})
于 2015-05-10T13:59:15.447 回答
0

正如tzot 所说,由于 Python ≥3.3,Python确实有一个可变版本的 namedtuple: types.SimpleNamespace

这些东西与新的C# 9 Records非常相似。

以下是一些使用示例:

位置构造函数参数

>>> import types
>>>
>>> class Location(types.SimpleNamespace):
...   def __init__(self, lat=0, long=0):
...     super().__init__(lat=lat, long=long)
...
>>> loc_1 = Location(49.4, 8.7)

漂亮的代表

>>> loc_1
Location(lat=49.4, long=8.7)

可变的

>>> loc_2 = Location()
>>> loc_2
Location(lat=0, long=0)
>>> loc_2.lat = 49.4
>>> loc_2
Location(lat=49.4, long=0)

相等的值语义

>>> loc_2 == loc_1
False
>>> loc_2.long = 8.7
>>> loc_2 == loc_1
True

可以在运行时添加属性

>>> loc_2.city = 'Heidelberg'
>>> loc_2
于 2020-12-02T21:07:12.217 回答