Ale*_*der 105 python mutable namedtuple
任何人都可以修改namedtuple或提供替代类,以便它适用于可变对象吗?
主要是为了可读性,我想要类似于namedtuple这样做:
from Camelot import namedgroup
Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
Point(x=100, y=0)
Run Code Online (Sandbox Code Playgroud)
必须可以腌制生成的对象.并且根据命名元组的特征,当表示时,输出的排序必须与构造对象时参数列表的顺序相匹配.
int*_*ath 113
有一个可变的替代collections.namedtuple- recordclass.
它具有相同的API和内存占用,namedtuple并且它支持分配(它也应该更快).例如:
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)
Run Code Online (Sandbox Code Playgroud)
对于python 3.6及更高版本recordclass(自0.5起)支持typehints:
from recordclass import recordclass, RecordClass
class Point(RecordClass):
x: int
y: int
>>> Point.__annotations__
{'x':int, 'y':int}
>>> 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)
Run Code Online (Sandbox Code Playgroud)
有一个更完整的例子(它还包括性能比较).
由于0.9 recordclass库提供了另一种变体 - recordclass.structclass工厂功能.它可以生成类,其实例占用的内存少于__slots__基于实例的内存.这对于具有属性值的实例非常重要,属性值并非旨在具有参考周期.这是一个说明性的例子.
fun*_*ure 27
types.SimpleNamespace是在Python 3.3中引入的,它支持所请求的要求.
from types import SimpleNamespace
t = SimpleNamespace(foo='bar')
t.ham = 'spam'
print(t)
namespace(foo='bar', ham='spam')
print(t.foo)
'bar'
import pickle
with open('/tmp/pickle', 'wb') as f:
pickle.dump(t, f)
Run Code Online (Sandbox Code Playgroud)
ken*_*nes 23
似乎这个问题的答案是否定的.
下面非常接近,但它在技术上并不可变.这是创建一个namedtuple()具有更新x值的新实例:
Point = namedtuple('Point', ['x', 'y'])
p = Point(0, 0)
p = p._replace(x=10)
Run Code Online (Sandbox Code Playgroud)
另一方面,您可以创建一个简单的类__slots__,它应该适用于频繁更新类实例属性:
class Point:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
Run Code Online (Sandbox Code Playgroud)
为了添加这个答案,我认为__slots__这里很好用,因为在创建大量类实例时它的内存效率很高.唯一的缺点是您无法创建新的类属性.
这是一个相关的线程,说明了内存效率 - 字典与对象 - 哪个更有效,为什么?
这个帖子的答案中引用的内容是一个非常简洁的解释,为什么__slots__更高效的内存 - Python插槽
Ali*_*Ali 23
最新的命名列表 1.7在 2016 年 1月11日之前通过Python 2.7和Python 3.5传递所有测试.它是一个纯python实现,而它recordclass是一个C扩展.当然,这取决于您的要求是否首选C扩展名.
你的测试(但也见下面的注释):
from __future__ import print_function
import pickle
import sys
from namedlist import namedlist
Point = namedlist('Point', 'x y')
p = Point(x=1, y=2)
print('1. Mutation of field values')
p.x *= 10
p.y += 10
print('p: {}, {}\n'.format(p.x, p.y))
print('2. String')
print('p: {}\n'.format(p))
print('3. Representation')
print(repr(p), '\n')
print('4. Sizeof')
print('size of p:', sys.getsizeof(p), '\n')
print('5. Access by name of field')
print('p: {}, {}\n'.format(p.x, p.y))
print('6. Access by index')
print('p: {}, {}\n'.format(p[0], p[1]))
print('7. Iterative unpacking')
x, y = p
print('p: {}, {}\n'.format(x, y))
print('8. Iteration')
print('p: {}\n'.format([v for v in p]))
print('9. Ordered Dict')
print('p: {}\n'.format(p._asdict()))
print('10. Inplace replacement (update?)')
p._update(x=100, y=200)
print('p: {}\n'.format(p))
print('11. Pickle and Unpickle')
pickled = pickle.dumps(p)
unpickled = pickle.loads(pickled)
assert p == unpickled
print('Pickled successfully\n')
print('12. Fields\n')
print('p: {}\n'.format(p._fields))
print('13. Slots')
print('p: {}\n'.format(p.__slots__))
Run Code Online (Sandbox Code Playgroud)
Python 2.7上的输出
1. Mutation of field values
p: 10, 12
2. String
p: Point(x=10, y=12)
3. Representation
Point(x=10, y=12)
4. Sizeof
size of p: 64
5. Access by name of field
p: 10, 12
6. Access by index
p: 10, 12
7. Iterative unpacking
p: 10, 12
8. Iteration
p: [10, 12]
9. Ordered Dict
p: OrderedDict([('x', 10), ('y', 12)])
10. Inplace replacement (update?)
p: Point(x=100, y=200)
11. Pickle and Unpickle
Pickled successfully
12. Fields
p: ('x', 'y')
13. Slots
p: ('x', 'y')
与Python 3.5的唯一区别在于namedlist它变小了,大小为56(Python 2.7报告64).
请注意,我已将测试10更改为就地更换.该namedlist有_replace()哪些做了浅拷贝的方法,这使我感觉良好,因为namedtuple在标准库的工作方式.更改_replace()方法的语义会令人困惑.在我看来,该_update()方法应该用于就地更新.或者我可能没理解你的测试10的意图?
Kas*_*mvd 16
作为此任务的非常Pythonic替代方案,从Python-3.7开始,您可以使用
dataclasses不仅行为像mutable一样的模块,NamedTuple因为它们使用普通的类定义,它们也支持其他类的特性.
从PEP-0557:
尽管它们使用了一种非常不同的机制,但数据类可以被认为是"具有默认值的可变命名元组".因为数据类使用普通的类定义语法,所以您可以自由地使用继承,元类,文档字符串,用户定义的方法,类工厂和其他Python类功能.
提供了一个类装饰器,它检查具有类型注释的变量的类定义,如PEP 526 "变量注释的语法"中所定义.在本文档中,此类变量称为字段.使用这些字段,装饰器将生成的方法定义添加到类中以支持实例初始化,repr,比较方法以及" 规范"部分中描述的可选的其他方法.这样的类被称为数据类,但该类没有什么特别之处:装饰器将生成的方法添加到类中并返回给定的类.
PEP-0557中引入了此功能,您可以在提供的文档链接中更详细地了解它.
例:
In [20]: from dataclasses import dataclass
In [21]: @dataclass
...: class InventoryItem:
...: '''Class for keeping track of an item in inventory.'''
...: name: str
...: unit_price: float
...: quantity_on_hand: int = 0
...:
...: def total_cost(self) -> float:
...: return self.unit_price * self.quantity_on_hand
...:
Run Code Online (Sandbox Code Playgroud)
演示:
In [23]: II = InventoryItem('bisc', 2000)
In [24]: II
Out[24]: InventoryItem(name='bisc', unit_price=2000, quantity_on_hand=0)
In [25]: II.name = 'choco'
In [26]: II.name
Out[26]: 'choco'
In [27]:
In [27]: II.unit_price *= 3
In [28]: II.unit_price
Out[28]: 6000
In [29]: II
Out[29]: InventoryItem(name='choco', unit_price=6000, quantity_on_hand=0)
Run Code Online (Sandbox Code Playgroud)
以下是Python 3的一个很好的解决方案:使用最小类__slots__和Sequence抽象基类; 不会做花哨的错误检测等,但它可以工作,并且行为大多像一个可变的元组(除了类型检查).
from collections import Sequence
class NamedMutableSequence(Sequence):
__slots__ = ()
def __init__(self, *a, **kw):
slots = self.__slots__
for k in slots:
setattr(self, k, kw.get(k))
if a:
for k, v in zip(slots, a):
setattr(self, k, v)
def __str__(self):
clsname = self.__class__.__name__
values = ', '.join('%s=%r' % (k, getattr(self, k))
for k in self.__slots__)
return '%s(%s)' % (clsname, values)
__repr__ = __str__
def __getitem__(self, item):
return getattr(self, self.__slots__[item])
def __setitem__(self, item, value):
return setattr(self, self.__slots__[item], value)
def __len__(self):
return len(self.__slots__)
class Point(NamedMutableSequence):
__slots__ = ('x', 'y')
Run Code Online (Sandbox Code Playgroud)
例:
>>> p = Point(0, 0)
>>> p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
>>> p
Point(x=100, y=0)
Run Code Online (Sandbox Code Playgroud)
如果需要,您也可以使用方法来创建类(尽管使用显式类更透明):
def namedgroup(name, members):
if isinstance(members, str):
members = members.split()
members = tuple(members)
return type(name, (NamedMutableSequence,), {'__slots__': members})
Run Code Online (Sandbox Code Playgroud)
例:
>>> Point = namedgroup('Point', ['x', 'y'])
>>> Point(6, 42)
Point(x=6, y=42)
Run Code Online (Sandbox Code Playgroud)
在Python 2,你需要稍微调整它-如果你继承Sequence,类将有一个__dict__和__slots__将停止工作.
Python 2中的解决方案是不继承Sequence,但是object.如果isinstance(Point, Sequence) == True需要,您需要将NamedMutableSequence作为基类注册 到Sequence:
Sequence.register(NamedMutableSequence)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
28145 次 |
| 最近记录: |