【问题标题】:Why does Python not support record type? (i.e. mutable namedtuple)为什么 Python 不支持记录类型? (即可变的命名元组)
【发布时间】:2011-07-10 19:33:07
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 创建一个类,或者使用一个字典。两者都是可变的,都允许您按名称访问其中的值。
  • @dappawit,这是正确的。但是使用 dict,我将无法方便地将属性作为字段。我正在避免上课,因为我需要遍历将其视为属性集合的对象。我将编辑我的帖子以指定此要求。我知道我总是可以添加特殊的方法来把它当作一个集合来对待。但我想知道是否有更简单的方法。
  • 一个类实例有一个__dict__ 属性,它是一个字典。你可以遍历它。看看 Cameron 的回答和 Chris Lutz 的评论。
  • 感谢 dappawit。我想使用现有类型而不使用特殊方法模拟集合类型。但是,是的,我必须这样做。

标签: python collections namedtuple


【解决方案1】:

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

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

我还发现了一个recipe for "records",它被描述为可变命名元组。它们是使用类实现的。

更新:

由于您说顺序对您的方案很重要(并且您想遍历所有属性)OrderedDict 似乎是要走的路。这是 Python 2.7 标准 collections 模块的一部分;对于 Python implementations 在互联网上流传。

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

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

【讨论】:

  • 您甚至可以继承 dict 类,并使 (IIRC) __getattr____setattr__ 分别与 __getitem____setitem__ 同义。
  • @Chris:我认为我最喜欢这种方法(而且你的记忆是正确的,这些是正确的方法)
  • +1 用于指定 slots 为每个类创建一次,而 dict 为每个实例创建。我已经编辑了我的问题以包括订购的需要。另外,我知道这个唱片配方;但是我想知道为什么 Python 社区认为不需要标准记录类型。
  • 如果您需要订购,请尝试 OrderedDict。我相信它也在集合模块中。
  • 感谢 dappawit。我更喜欢属性固定的类型(就像它们在我的设备中一样),但是是可变的。我想我可能不得不选择 Cameron 指定的记录类型。
【解决方案2】:

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

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

【讨论】:

  • 另外,请参阅this answerthis stackoverflow question 了解更多信息。
  • 谢谢阿巴飞。我更喜欢属性固定的类型(就像它们在我的设备中一样),但是是可变的。我已经相应地更新了问题。
【解决方案3】:

Python

你的意思是这样的?

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("__"))。

【讨论】:

  • 太好了。感谢您的回答。只是我希望有一个内置/标准库数据结构。
  • SimpleNamespace 满足需求。谢谢。
  • @u0b34a0f6ae 查看 SimpleNamespace 的实现方式,将其放入 types 是有道理的。如果您查看types 模块中的许多“类”,您会注意到它们都是通过使用type 函数提取支持特定值的类型(例如LambdaType = type(lambda: None))而获得的。猜猜 SimpleNamespace 是如何实现的? SimpleNamespace = type(sys.implementation).
  • @u0b34a0f6ae 因为它不是一个集合,所以像class X(): pass 这样的空类也不是一个集合。最重要的是,它没有迭代或大小的概念。为什么你认为它应该在collections
  • 请注意,list(r.__dict__) 将返回用户定义的属性名称,无需“__”过滤。
【解决方案4】:

有一个类似于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)

【讨论】:

  • namedlist 是同一作者的更新包,它支持 python 3,并于 2014 年积极开发。
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

      这个答案重复了another onecollections.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)
      

      还有更完整的example(还包括性能比较)。

      【讨论】:

        【解决方案8】:

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

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

        【讨论】:

          【解决方案9】:

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

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

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

          更多细节,尤其是测试,见Existence of mutable named tuple in Python?

          【讨论】:

            【解决方案10】:

            这个问题很老了,但为了完整起见,Python 3.7 有 dataclasses 这几乎是记录。

            >>> 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 有一个很好的帮手。

            【讨论】:

              【解决方案11】:

              作为tzotstated,由于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
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2022-01-03
                • 2018-12-24
                • 1970-01-01
                • 2017-01-20
                • 1970-01-01
                • 2020-02-12
                • 1970-01-01
                相关资源
                最近更新 更多