为什么Python本身不支持记录类型?这是一个具有可变版本的namedtuple的问题。
我可以使用namedtuple._replace
。但我需要在集合中包含这些记录,并且由于namedtuple._replace
创建了另一个实例,我还需要修改快速变得混乱的集合。
背景: 我有一个设备,我需要通过TCP / IP轮询它的属性。即它的表示是一个可变对象。
编辑: 我有一套我需要轮询的设备。
编辑:
我需要使用PyQt遍历显示其属性的对象。我知道我可以添加__getitem__
和__iter__
等特殊方法,但我想知道是否有更简单的方法。
编辑: 我更喜欢一种属性固定的类型(就像它们在我的设备中一样),但它是可变的。
答案 0 :(得分:48)
你的意思是这样的吗?
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)
请注意,提供的方法只是可能方法的一个示例。
您可以使用types.SimpleNamespace
:
>>> import types
>>> r= types.SimpleNamespace()
>>> r.attribute1= "hello"
>>> r.attribute2= "there"
>>> r.attribute3= 3.14
dir(r)
会为您提供属性名称(当然,过滤掉所有.startswith("__")
)。
答案 1 :(得分:18)
你有什么理由不能使用普通字典吗?在您的特定情况下,属性似乎没有特定的顺序。
或者,您也可以使用类实例(具有良好的属性访问语法)。如果您希望避免为每个实例创建__slots__
,则可以使用__dict__
。
我还发现了一个recipe for "records",它被描述为可变的命名元组。它们是使用类实现的。
更新
由于您说订单对您的方案很重要(并且您想要遍历所有属性),OrderedDict
似乎是要走的路。从Python 2.7开始,这是标准collections
模块的一部分;互联网上还有其他implementations浮动用于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']
答案 2 :(得分:10)
这可以使用空类及其实例来完成,如下所示:
>>> class a(): pass
...
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>>
答案 3 :(得分:9)
有一个类似于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)
答案 4 :(得分:4)
此答案重复another one。
有一个可变的替代collections.namedtuple
- recordclass。
它与namedtuple
具有相同的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(还包括性能比较)。
答案 5 :(得分:1)
在密切相关的Existence of mutable named tuple in Python?问题中,13个测试用于比较6个可变替代namedtuple
。
截至2016年1月11日,最新的namedlist 1.7 passes所有这些测试都包含Python 2.7和Python 3.5 。这是一个纯粹的python实现。
根据这些测试,第二个最佳候选者是recordclass
,它是C扩展。当然,这取决于您的要求是否首选C扩展名。
有关详细信息,尤其是测试,请参阅Existence of mutable named tuple in Python?
答案 6 :(得分:0)
您可以执行类似此dict
子类的操作,它是自己的__dict__
。基本概念与ActiveState AttrDict配方的基本概念相同,但实现更简单。结果是比你需要的更可变的东西,因为实例的属性和它们的值都是可变的。虽然属性没有排序,但您可以遍历当前属性和/或它们的值。
class Record(dict):
def __init__(self, *args, **kwargs):
super(Record, self).__init__(*args, **kwargs)
self.__dict__ = self
答案 7 :(得分:0)
基于随着时间的推移收集的一些有用的技巧,这个“冻结类”装饰器几乎完成了所需的一切:http://pastebin.com/fsuVyM45
由于该代码超过70%的文档和测试,我在这里不会多说。
答案 8 :(得分: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})
答案 9 :(得分:0)
这个问题很老,但是为了完整起见,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为此提供了很好的帮助。
答案 10 :(得分:0)
与tzot stated一样,由于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