Python 如何支持记录类型? (即可变的命名元组)

发布于 2024-10-20 18:58:34 字数 381 浏览 2 评论 0原文

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

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

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

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

我需要使用 PyQt 迭代显示其属性的对象。我知道我可以添加特殊方法,例如 __getitem__ 和 __iter__ ,但我想知道是否有更简单的方法。

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

Why does Python not support a record type natively? It's a matter of having a mutable version of namedtuple.

I could use namedtuple._replace. But I need to have these records in a collection and since namedtuple._replace creates another instance, I also need to modify the collection which becomes messy quickly.

Background:
I have a device whose attributes I need to get by polling it over TCP/IP. i.e. its representation is a mutable object.

I have a set of devices for whom I need to poll.

I need to iterate through the object displaying its attributes using PyQt. I know I can add special methods like __getitem__ and __iter__, but I want to know if there is an easier way.

I would prefer a type whose attribute are fixed (just like they are in my device), but are mutable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

此生挚爱伱 2024-10-27 18:58:34

Python ≥3.3

可以使用 types.SimpleNamespace

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

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

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

You can use types.SimpleNamespace:

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

dir(r) would provide you with the attribute names (filtering out all .startswith("__"), of course).

Python <3.3

You could use something like this:

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)

Note that the methods provided are just a sample of possible methods.

烟雨凡馨 2024-10-27 18:58:34

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

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

我还刚刚找到了一个“记录”配方,它被描述为可变的命名-元组。它们是使用类来实现的。

既然您说顺序对于您的场景很重要(并且您想要迭代所有属性),则 OrderedDict 似乎是正确的选择。这是 Python 2.7 起标准 collections 模块的一部分;互联网上还有其他的 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']

Is there any reason you can't use a regular dictionary? It seems like the attributes don't have a specific ordering in your particular situation.

Alternatively, you could also use a class instance (which has nice attribute access syntax). You could use __slots__ if you wish to avoid having a __dict__ created for each instance.

I've also just found a recipe for "records", which are described as mutable named-tuples. They are implemented using classes.

Since you say order is important for your scenario (and you want to iterate through all the attributes) an OrderedDict seems to be the way to go. This is part of the standard collections module as of Python 2.7; there are other implementations floating around the internet for Python < 2.7.

To add attribute-style access, you can subclass it like so:

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)
    

Then you can do:

>>> 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']
孤檠 2024-10-27 18:58:34

为了完整起见,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 有一个很好的帮手可以做到这一点。

Just for the sake of completeness, Python 3.7 has dataclasses which are pretty much records.

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

The attrs third party library provides more functionality for both Python 2 and Python 3. Nothing wrong with vendoring dependencies either if the requirement is more around things you can't keep locally rather than specifically only using the stdlib. dephell has a nice helper for doing that.

末蓝 2024-10-27 18:58:34

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

>>> class a(): pass
... 
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>> 

This can be done using an empty class and instances of it, like this:

>>> class a(): pass
... 
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>> 
只涨不跌 2024-10-27 18:58:34

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

包主页: 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)

There's a library similar to namedtuple, but mutable, called recordtype.

Package home: http://pypi.python.org/pypi/recordtype

Simple example:

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)

Simple default value example:

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

Iterate through the fields of person1 in order:

map(person1.__getattribute__, Person._fields)
我不咬妳我踢妳 2024-10-27 18:58:34

此答案与另一个答案重复
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)

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

This answer duplicates another one.
There is a mutable alternative to collections.namedtuple - recordclass.

It has same API and minimal memory footprint (actually it also faster). It support assignments. For example:

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)

There is more complete example (it also include performance comparisons).

鼻尖触碰 2024-10-27 18:58:34

在密切相关的 Existence of mutable named tuple in Python? 中,问题 13 测试用于比较 的 6 个可变替代方案命名元组

最新namedlist 1.7 在 Python 2.7 和 Python 3.5 上通过了所有这些测试这是一个纯 Python 实现。

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

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

In the closely related Existence of mutable named tuple in Python? question 13 tests are used for comparing 6 mutable alternatives to namedtuple.

The latest namedlist 1.7 passes all of these tests with both Python 2.7 and Python 3.5 as of Jan 11, 2016. It is a pure python implementation.

The second best candidate according to these tests is the recordclass which is a C extension. Of course, it depends on your requirements whether a C extension is preferred or not.

For further details, especially for the tests, see Existence of mutable named tuple in Python?

找回味觉 2024-10-27 18:58:34

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

由于该代码超过 70% 是文档和测试,因此我在此不再多说。

Based on several useful tricks gathered over time, this "frozenclass" decorator does pretty much everything needed: http://pastebin.com/fsuVyM45

Since that code is over 70% documentation and tests, I won't say more here.

巷雨优美回忆 2024-10-27 18:58:34

这是我制作的一个完整的可变命名元组,其行为类似于列表并且与其完全兼容。

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

Here is a complete mutable namedtuple I made, which behaves like a list and is totally compatible with it.

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})
娇女薄笑 2024-10-27 18:58:34

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

class Record(dict):
    def __init__(self, *args, **kwargs):
        super(Record, self).__init__(*args, **kwargs)
        self.__dict__ = self

You could do something like thisdictsubclass which is its own __dict__. The basic concept is the same as that of the ActiveState AttrDict recipe, but the implementation is simpler. The result is something more mutable than you need since both an instance's attributes and their values are changeable. Although the attributes aren't ordered, you can iterate through the current ones and/or their values.

class Record(dict):
    def __init__(self, *args, **kwargs):
        super(Record, self).__init__(*args, **kwargs)
        self.__dict__ = self
苦行僧 2024-10-27 18:58:34

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

这些东西与新的 非常相似C# 9 记录

以下是一些使用示例:

位置构造函数参数

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

As tzot stated, since Python ≥3.3, Python does have a mutable version of namedtuple: types.SimpleNamespace.

These things are very similar to the new C# 9 Records.

Here are some usage examples:

Positional constructor arguments

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

Pretty repr

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

Mutable

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

Value semantics for equality

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

Can add attributes at runtime

>>> loc_2.city = 'Heidelberg'
>>> loc_2
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文