跳转到主要内容

namedtuple的可变变体 -- recordclass,支持赋值、紧凑数据类和其他节省内存的变体。

项目描述

# Recordclass library

Recordclass 是一个 MIT 许可 的 python 库。它最初是为了解决快速“可变”的 namedtuple 替代方案的问题而开始的“概念验证”(参见 stackoverflow 上的问题)。它进一步发展,以提供更多节省内存、快速和灵活的类型。

Recordclass 库提供类似记录/数据的类,默认情况下不参与循环 垃圾回收(GC)机制,但支持仅 引用计数 的垃圾回收。此类实例没有内存中的 PyGC_Head 前缀,这减少了它们的大小,并且实例创建和分配的路径要快一些。在需要尽可能限制对象大小时,这可能会很有意义,前提是它们永远不会成为应用程序中引用循环的一部分。例如,当对象按照惯例代表具有简单类型值的字段记录时(例如 intfloatstrdate/time/datetimetimedelta 等)。

为了说明这一点,考虑一个带有类型提示的简单类

class Point:
    x: int
    y: int

通过默许协议,类 Point 的实例应该具有类型为 int 的属性 xy。分配其他类型的值,这些值不是 int 的子类,应被视为违反协议。

其他例子是非递归数据结构,其中所有叶元素表示原子类型的值。当然,在 Python 中,没有阻止你在脚本或应用程序代码中创建引用循环。但许多情况下,只要开发者理解自己在做什么,并谨慎地在代码库中使用此类,就可以避免这种情况。另一种选择是使用静态代码分析器以及类型注解来监控对类型提示的遵守。

该库建立在基类 dataobject 之上。类型为特殊的元类 datatype。它控制子类的创建,这些子类不会参与循环 GC,并且默认不包含 PyGC_Head-前缀、__dict____weakref__。因此,此类实例所需的内存较少。其内存占用与具有 __slots__ 的类实例的内存占用相似,但不包含 PyGC_Head。因此,内存大小的差异等于 PyGC_Head 的大小。它还调整实例的 basicsize,创建字段的描述符等。通过 class statement 创建的 dataobject 的所有子类都支持类似 attrs/dataclasses 的 API。例如

    from recordclass import dataobject, astuple, asdict
    class Point(dataobject):
        x:int
        y:int

    >>> p = Point(1, 2)
    >>> astuple(p)
    (1, 2)
    >>> asdict(p)
    {'x':1, 'y':2}

recordclass 工厂创建基于 dataobject 的子类,具有指定的字段和类似 namedtuple 的 API。默认情况下,它也不会参与循环 GC。

    >>> from recordclass import recordclass
    >>> Point = recordclass('Point', 'x y')
    >>> p = Point(1, 2)
    >>> p.y = -1
    >>> print(p._astuple)
    (1, -1)
    >>> x, y = p
    >>> print(p._asdict)
    {'x':1, 'y':-1}

它还提供了一个用于创建具有指定字段名的 dataobject 子类的工厂函数 make_dataclass。这些子类支持类似 attrs/dataclasses 的 API。它相当于使用 class statement 创建 dataobject 的子类。例如

    >>> Point = make_dataclass('Point', 'x y')
    >>> p = Point(1, 2)
    >>> p.y = -1
    >>> print(p.x, p.y)
    1 -1

如果想要使用某个序列进行初始化,则

    >>> p = Point(*sequence)

还有一个用于创建可以视为简单对象紧凑数组的 dataobject 子类的工厂函数 make_arrayclass。例如

    >>> Pair = make_arrayclass(2)
    >>> p = Pair(2, 3)
    >>> p[1] = -1
    >>> print(p)
    Pair(2, -1)

该库还提供了 lightlist(不可变)和 litetuple 类,它们被视为为了节省内存而类似于列表和元组的轻量级容器。它们也不应该参与循环 GC。可变版本的 litetuple 称为 mutabletuple。例如

    >>> lt = litetuple(1, 2, 3)
    >>> mt = mutabletuple(1, 2, 3)
    >>> lt == mt
    True
    >>> mt[-1] = -3
    >>> lt == mt
    False
    >>> print(sys.getsizeof((1,2,3)), sys.getsizeof(litetuple(1,2,3)))
    64 48

注意,如果想要从某些可迭代对象创建 litetuplemutabletuple,则

    >>> seq = [1,2,3]
    >>> lt = litetuple(*seq)
    >>> mt = mutabletuple(*seq)

内存占用

以下表格解释了基于 dataobject 的对象和 litetuples 的内存占用

tuple/namedtuple 带有 __slots__ 的类 recordclass/dataobject litetuple/mutabletuple
g+b+s+n×p g+b+n×p b+n×p b+s+n×p

其中

  • b = sizeof(PyObject)
  • s = sizeof(Py_ssize_t)
  • n = 项数
  • p = sizeof(PyObject*)
  • g = sizeof(PyGC_Head)

这在您绝对确定不会出现引用循环的情况下很有用。例如,当所有字段值都是原子类型实例时。结果,实例的大小减少了 24-32 字节(CPython 3.4-3.7)和 16 字节(CPython >=3.8)。

性能计数器

以下表格是使用 tools/perfcounts.py 脚本测量的性能计数器

  • recordclass 0.21,python 3.10,debian/testing linux,x86-64
id 大小 getattr setattr getitem setitem getkey setkey iterate copy
litetuple 48 0.18 0.2 0.33 0.19
mutabletuple 48 0.18 0.21 0.21 0.33 0.18
tuple 64 0.24 0.21 0.37 0.16
namedtuple 64 0.75 0.23 0.21 0.33 0.21
class+slots 56 0.68 0.29 0.33
dataobject 40 0.25 0.23 0.29 0.2 0.22 0.33 0.2
dataobject+gc 56 0.27 0.22 0.29 0.19 0.21 0.35 0.22
dict 232 0.32 0.2 0.24 0.35 0.25
dataobject+map 40 0.25 0.23 0.3 0.29 0.29 0.32 0.2
  • recordclass 0.21,python 3.11,debian/testing linux,x86-64
id 大小 getattr setattr getitem setitem getkey setkey iterate copy
litetuple 48 0.11 0.11 0.18 0.09
mutabletuple 48 0.11 0.11 0.12 0.18 0.08
tuple 64 0.1 0.08 0.17 0.1
namedtuple 64 0.49 0.13 0.11 0.17 0.13
class+slots 56 0.31 0.06 0.06
dataobject 40 0.13 0.06 0.06 0.11 0.12 0.16 0.12
dataobject+gc 56 0.14 0.06 0.06 0.1 0.12 0.16 0.14
dict 184 0.2 0.12 0.13 0.19 0.13
dataobject+map 40 0.12 0.07 0.06 0.15 0.16 0.16 0.12
class 56 0.35 0.06 0.06
  • recordclas 0.21,python3.12,debian/testing linux,x86-64
id 大小 getattr setattr getitem setitem getkey setkey iterate copy
litetuple 48 0.13 0.12 0.19 0.09
mutabletuple 48 0.13 0.11 0.12 0.18 0.09
tuple 64 0.11 0.09 0.16 0.09
namedtuple 64 0.52 0.13 0.11 0.16 0.12
class+slots 56 0.34 0.08 0.07
dataobject 40 0.14 0.08 0.08 0.11 0.12 0.17 0.12
dataobject+gc 56 0.15 0.08 0.07 0.12 0.12 0.17 0.13
dict 184 0.19 0.11 0.14 0.2 0.12
dataobject+map 40 0.14 0.08 0.08 0.16 0.17 0.17 0.12
class 48 0.41 0.08 0.08

recordclass的主要仓库位于GitHub

这里还有一个简单的示例

更多示例可以在examples文件夹中找到。

快速入门

安装

从源目录安装

安装

>>> python3 setup.py install

运行测试

>>> python3 test_all.py

从PyPI安装

安装

>>> pip3 install recordclass

运行测试

>>> python3 -c "from recordclass.test import *; test_all()"

使用dataobject的快速入门

Dataobject是用于创建具有快速实例创建和较小内存占用数据类的基类。它们提供类似dataclass的API。

首先加载库存

>>> from recordclass import dataobject, asdict, astuple, as_dataclass, as_record

以某种方式定义类

class Point(dataobject):
    x: int
    y: int

或者

@as_dataclass()
class Point:
    x: int
    y: int

或者

@as_record
def Point(x:int, y:int): pass

或者

>>> Point = make_dataclass("Point", [("x",int), ("y",int)])

或者

>>> Point = make_dataclass("Point", {"x":int, "y",int})

字段的注解被定义为__annotations__中的字典

>>> print(Point.__annotations__)
{'x': <class 'int'>, 'y': <class 'int'>}

存在默认文本表示

>>> p = Point(1, 2)
>>> print(p)
Point(x=1, y=2)

实例具有CPython对象可能的最小内存占用,其中只包含Python对象

>>> sys.getsizeof(p) # the output below for python 3.8+ (64bit)
40
>>> p.__sizeof__() == sys.getsizeof(p) # no additional space for cyclic GC support
True

默认情况下实例是可变的

>>> p_id = id(p)
>>> p.x, p.y = 10, 20
>>> id(p) == p_id
True
>>> print(p)
Point(x=10, y=20)

存在asdictastuple函数,用于转换为dicttuple

>>> asdict(p)
{'x':10, 'y':20}
>>> astuple(p)
(10, 20)

默认情况下,dataobject的子类是可变的。如果想要使其不可变,则可以使用选项readonly=True

class Point(dataobject, readonly=True):
    x: int
    y: int

>>> p = Point(1,2)
>>> p.x = -1
. . . . . . . . . . . . .
TypeError: item is readonly

默认情况下,dataobject的子类不是可迭代的。如果想要使其可迭代,则可以使用选项iterable=True

class Point(dataobject, iterable=True):
    x: int
    y: int

>>> p = Point(1,2)
>>> for x in p: print(x)
1
2

也支持默认值:

class CPoint(dataobject):
    x: int
    y: int
    color: str = 'white'

或者

>>> CPoint = make_dataclass("CPoint", [("x",int), ("y",int), ("color",str)], defaults=("white",))

>>> p = CPoint(1,2)
>>> print(p)
Point(x=1, y=2, color='white')

但是

class PointInvalidDefaults(dataobject):
    x:int = 0
    y:int

不允许。没有默认值的字段不能出现在有默认值的字段之后。

从0.21版开始,存在copy_default选项(在创建实例时分配默认值的副本)

 class Polygon(dataobject, copy_default=True):
    points: list = []

>>> pg1 = Polygon()
>>> pg2 = Polygon()
>>> assert pg1.points == pg2.points
True
>>> assert id(pg1.points) != id(pg2.points)
True

从0.21版开始,Factory允许设置计算默认值的工厂函数

from recordclass import Factory

class A(dataobject, copy_default=True):
    x: tuple = Factory( lambda: (list(), dict()) )

>>> a = A()
>>> b = A()
>>> assert a.x == b.x
True
>>> assert id(a.x[0]) != id(b.x[0])
True
>>> assert id(a.x[1]) != id(b.x[1])
True

如果有人想定义类属性,则可以使用ClassVar技巧

class Point(dataobject):
    x:int
    y:int
    color:ClassVar[int] = 0

>>> print(Point.__fields__)
('x', 'y')
>>> print(Point.color)
0

如果ClassVar属性的默认值未指定,则它将仅从__fields___中排除。

从Python 3.10开始,默认指定__match_args__,因此__match_args__ == __fields__。用户可以在定义时定义它自己的

class User(dataobject):
    first_name: str
    last_name: str
    age: int
    __match_args__ = 'first_name', 'last_name'

或者

from recordclass import MATCH
class User(dataobject):
    first_name: str
    last_name: str
    _: MATCH
    age: int

或者

User = make_dataclass("User", "first_name last_name * age")

使用recordclass的快速入门

recordclass工厂函数旨在创建支持namedtuple API的类,可以是可变的和不可变的,提供快速实例创建和最小内存占用。

首先加载库存

>>> from recordclass import recordclass

使用recordclass的示例

>>> Point = recordclass('Point', 'x y')
>>> p = Point(1,2)
>>> print(p)
Point(1, 2)
>>> print(p.x, p.y)
1 2
>>> p.x, p.y = 1, 2
>>> print(p)
Point(1, 2)
>>> sys.getsizeof(p) # the output below is for 64bit cpython3.8+
32

使用类语句和类型提示的示例

>>> from recordclass import RecordClass

class Point(RecordClass):
   x: int
   y: int

>>> print(Point.__annotations__)
{'x': <class 'int'>, 'y': <class 'int'>}
>>> p = Point(1, 2)
>>> print(p)
Point(1, 2)
>>> print(p.x, p.y)
1 2
>>> p.x, p.y = 1, 2
>>> print(p)
Point(1, 2)

默认情况下,基于recordclass的类实例不参与循环GC,因此它们比基于namedtuple的实例更小。如果想要在具有引用循环的场景中使用它,则必须使用选项gc=True(默认为gc=False

>>> Node = recordclass('Node', 'root children', gc=True)

或者

class Node(RecordClass, gc=True):
     root: 'Node'
     chilren: list

recordclass工厂还可以指定字段的类型

>>> Point = recordclass('Point', [('x',int), ('y',int)])

或者

>>> Point = recordclass('Point', {'x':int, 'y':int})

使用基于dataobject的类与映射协议

class FastMapingPoint(dataobject, mapping=True):
    x: int
    y: int

或者

FastMapingPoint = make_dataclass("FastMapingPoint", [("x", int), ("y", int)], mapping=True)

>>> p = FastMappingPoint(1,2)
>>> print(p['x'], p['y'])
1 2
>>> sys.getsizeof(p) # the output below for python 3.10 (64bit)
32

使用基于dataobject的类处理无引用循环的递归数据

存在deep_dealloc选项(默认值为False)用于递归数据结构的分配

class LinkedItem(dataobject):
    val: object
    next: 'LinkedItem'

class LinkedList(dataobject, deep_dealloc=True):
    start: LinkedItem = None
    end: LinkedItem = None

    def append(self, val):
        link = LinkedItem(val, None)
        if self.start is None:
            self.start = link
        else:
            self.end.next = link
        self.end = link

如果没有deep_dealloc=True,则在链表长度太大时,LinkedList实例的分配将失败。但可以通过链表的__del__方法来清除链表解决

def __del__(self):
    curr = self.start
    while curr is not None:
        next = curr.next
        curr.next = None
        curr = next

deep_dealloc=True时,存在内建的更快地分配方法,使用终结机制。在这种情况下,不需要__del__方法来清除链表。

注意:对于具有gc=True的类,此方法被禁用:在这些情况下使用Python的循环GC。

有关更多详细信息,请参阅笔记本example_datatypes

变更

0.22.1:

  • 添加pyproject.toml

  • 添加pytest.ini以运行pytest

      >>> pip3 install -e .
      >>> pytest
    

0.22.0.3:

  • 重命名examples\test_*.py文件,因为它们被pytest检测为测试。
  • 修复了litelist0.22.0.2后的段错误。

0.22.0.2

  • 在GitHub上使用正确的标签发布。

0.22.0.1

  • 使用as_dataclass修复回归问题。

0.22

  • 为应该表现得更像简单数据结构的子类添加一个基类datastruct
  • 修复与__match_args__相关的错误 (#6)。
  • 开始支持Python 3.13。

0.21.1

  • 允许指定__match_args__。例如,

       class User(dataobject):
           first_name: str
           last_name: str
           age: int
           __match_args__ = 'first_name', 'last_name'
    

    或者

        User = make_dataclass("User", "first_name last_name * age")
    
  • 为被视为结构的def风格的数据类声明添加@as_record适配器。例如

      @as_record()
      def Point(x:float, y:float, meta=None): pass
    
      >>> p = Point(1,2)
      >>> print(p)
      Point(x=1, y=2, meta=None)
    

    几乎等同于

      Point = make_dataclass('Point', [('x':float), ('y',float),'meta'], (None,))
    
  • 选项fast_new将在0.22版本中移除。它将始终通过创建设置为fast_new=True。可以指定自己的__new__,例如

      class Point(dataobject):
          x:int
          y:int
    
          def __new__(cls, x=0, y=0):
               return dataobject.__new__(cls, x, y)
    
  • 修复与_PyUnicodeWriter相关的错误,针对Python 3.13。

0.21

  • 添加一个新的选项copy_default(默认值为False),允许为字段分配默认值的副本。例如

     class A(dataobject, copy_default=True):
          l: list = []
    
     a = A()
     b = A()
     assert(a.l == b.l)
     assert(id(a.l) != id(b.l))
    
  • 允许继承选项:copy_defaultgciterable。例如

     class Base(dataobject, copy_default=True):
        pass
    
    class A(Base):
          l: list = []
    
     a = A()
     b = A()
     assert a.l == b.l
     assert id(a.l) != id(b.l)
    
  • 为默认值添加Factory以指定工厂函数。例如

      from recordclass import Factory
      class A(dataobject):
          x: tuple = Factory(lambda: (list(), dict()))
    
      a = A()
      b = A()
      assert a.x == ([],{})
      assert id(a.x) != id(b.x)
      assert id(a.x[0]) != id(b.x[0])
      assert id(a.x[1]) != id(b.x[1])
    
      from recordclass import Factory
      class A(dataobject, copy_default=True):
          l: list = []
          x: tuple = Factory(lambda: (list(), dict()))
    
      a = A()
      b = A()
      assert a.x == ([],{})
      assert id(a.x) != id(b.x)
      assert a.l == []
      assert id(a.l) != id(b.l)
    
    • Recordclass支持Python 3.12(在linux/debian 11/12和通过appveyor的windows上测试过)。

0.20.1

  • 基于dataobject类的类改进了sqlite的row_factory。
  • 将recordclass仓库从bitbucket移动到github

0.20

  • 库代码库与Python 3.12兼容(仅在linux上测试过,在appveyor上直到Python 3.12支持)。
  • 修复通过update函数更新只读属性时的错误。

0.19.2

  • 对于具有无效关键字参数的Cls(**kwargs),异常消息更加精确 (#37)。
  • 为Python >= 3.11添加参数immutable_type。如果immutable_type=True,则生成的类(不是实例)将是不可变的。如果类不包含用户定义的__init____new__,则实例创建将更快(通过vectorcall协议)。

0.19.1

  • 修复与C.attr=value相关的回归问题(默认情况下使用不可变类)。

0.19

  • litetuplemutabletuple添加vectorcall协议。

  • dataobject添加vectorcall协议。

  • 现在,数据对象的op.__hash__默认返回id(op)。选项hashable=True使数据对象可以通过值进行散列。

  • 现在,基于dataobject的类、litetuplemutabletuple从Python 3.11开始支持实例创建和getattr/setattr的代码特殊化。

  • 修复当子类具有非平凡的__init__时的make函数。

  • 对于具有非平凡__init__的基于dataobject的子类,可能还需要定义__reduce__。例如

    def __reduce__(self):
      from recordclass import dataobject, make
      tp, args = dataobject.__reduce__(self)
      return make, (tp, args)
    

0.18.4

  • 修复一个错误 #35,在继承过程中复制字段名称,并将其与类级别属性混合。
  • 允许使用ClassVar定义类级别字段。

0.18.3

  • 修复字段默认值为元组时的错误。
  • 修复默认值传播到子类的问题。
  • 修复在dill上下文中序列化的一些问题。

0.18.2

  • 当字段具有默认值或kwargs时,略微提高了默认__init__的性能。
  • 移除实验性的pypy支持:速度慢且难以预测内存占用。
  • 排除实验性的cython模块。

0.18.1.1

  • 重新打包0.18.1,设置use_cython=0

0.18.1

  • 允许在用户定义的__init__方法中初始化字段,而不是在__new__中(问题29)。如果用户定义了__init__,则它负责初始化所有字段。请注意,此功能仅适用于可变字段。具有readonly=True的类的实例必须在默认的__new__中初始化。例如

      class A(dataobject):
            x:int
            y:int
    
            def __init__(self, x, y):
                self.x = x
                self.y = y
    
  • fast_new=True是默认值。

  • sqlite3添加make_row_factory

      class Planet(dataobject):
          name:str
          radius:int
    
      >>> con = sql.connect(":memory:")
      >>> cur = con.execute("SELECT 'Earth' AS name, 6378 AS radius")
      >>> cur.row_factory = make_row_factory(Planet)
      >>> row = cur.fetchone()
      >>> print(row)
      Planet(name='Earth', radius=6378)
    

0.18.0.1

  • 对于Python < 3.10,排除测试_dataobject_match.py(用于测试match语句)。

0.18

  • 支持Python 3.11。
  • 使数据对象受益于Python 3.11的代码特殊化。
  • 修复__new__中具有默认值的参数的问题,这些参数没有__repr__可以解释为创建默认值的有效Python表达式。
  • 添加对typing.ClassVar的支持。
  • 添加Py_TPFLAGS_SEQUENCEPy_TPFLAGS_MAPPING
  • 为支持基于数据对象子类的匹配协议,添加 __match_args__

0.17.5

  • 成功编译、构建和测试了 Python 3.11。

0.17.4

  • 修复了 3.11 中缺失 _PyObject_GC_Malloc 的错误。

0.17.3

  • 修复了兼容性问题:恢复 gnu98 C 语法。
  • 修复了使用 "Py_SIZE(op)" 和 "Py_TYPE(op)" 作为 l-value 的遗留问题。

0.17.2

  • 添加了对 Python 3.10 的支持。
  • 没有使用 "Py_SIZE(op)" 和 "Py_TYPE(op)" 作为 l-value。

0.17.1

  • 修复了 setup.py 中 cython=1 的打包问题。

0.17

  • 现在 recordclass 库可以编译为 pypy3,但仍然没有与 pypy3 的完全运行时兼容性。

  • 略微提高了 litetuple / mutabletuple 的性能。

  • 略微提高了基于 dataobject 的子类的性能。

  • 添加适配器 as_dataclass。例如

      @as_dataclass()
      class Point:
          x:int
          y:int
    
  • 模块 _litelist 是用纯 C 实现的。

  • 使 dataobject.copy 更快。

0.16.3

  • 为 recordclasses 添加了按键赋值的功能。

      A = recordclass("A", "x y", mapping=True)
      a = A(1,2)
      a['x'] = 100
      a['y'] = 200
    

0.16.2

  • 修复了 0.16.1 中的打包错误。

0.16.1

  • 添加 dictclass 工厂函数,用于生成具有 dict-like API 且没有字段属性访问的类。特点:快速实例创建,小内存占用。

0.16

  • RecordClass 开始成为具有 sequence=True 和类似 namedtuple API 的数据对象的直接子类。对于类创建,使用工厂函数 recordclass(name, fields, **kw)(允许指定类型)代替 RecordClass(name, fields, **kw)

  • make_dataclass 的选项 api='dict' 添加到支持 dict-like API 的类中。

  • 现在无法使用 del 或内置的 delattr 从其类中删除 dataobject 的属性。例如

      >>> Point = make_dataclass("Point", "x y")
      >>> del Point.x
      ...........
      AttributeError: Attribute x of the class Point can't be deleted
    
  • 现在无法使用 del 或内置的 delattr 删除字段的值。例如

      >>> p = Point(1, 2)
      >>> del p.x
      ...........
      AttributeError: The value can't be deleted"
    

    相反,可以使用赋值给 None

      >>> p = Point(1, 2)
      >>> p.x = None
    
  • 略微提高了具有 sequence=True 选项的数据对象类索引访问的性能。

0.15.1

  • 现在可以通过类声明中的关键字参数指定选项 readonlyiterable。例如

      class Point(dataobject, readonly=True, iterable=True):
           x:int
           y:int
    
  • 添加 update(cls, **kwargs) 函数以更新属性值。

0.15

  • 现在库只支持 Python >= 3.6
  • 现在可以在类声明中的关键字参数中指定 gcfast_new 选项。
  • 添加了一个函数 astuple(ob),用于将数据对象实例 ob 转换为元组。
  • 删除基于 datatuple 的类。
  • 添加函数 make(cls, args, **kwargs) 以创建类 cls 的实例。
  • 添加函数 clone(ob, **kwargs) 以克隆数据对象实例 ob
  • 将 structclass 作为 make_dataclass 的别名。
  • 为基于数据对象的递归子类实例的分配添加选项 'deep_dealloc' (@clsconfig(deep_dealloc=True))。

0.14.3:

  • 现在 dataobject 的子类默认支持可迭代和可哈希协议。

0.14.2:

  • 修复了 Python 3.9 的编译问题。

0.14.1:

  • 修复了在基于 recordclass 的类中子类化的 hash 问题时。

0.14:

  • 为生成的 dataobject-based 类添加了 doc,以支持 inspect.signature
  • 为快速实例创建添加了 fast_new 参数/选项。
  • 修复了 litelist 中的 refleak。
  • 修复了 dataobject/datatuple 的序列协议能力。
  • 修复了 StructClass 的类型接口。

0.13.2

  • 修复了数据对象的深度复制问题 #14。

0.13.1

  • 恢复 join_classes 并添加新函数 join_dataclasses

0.13.0.1

  • 删除冗余的调试代码。

0.13

  • 使 recordclass 编译并在 cpython 3.8 上工作。
  • 由于 bitbucket 将停止支持 mercurial 存储库,将存储库移动到 git
  • 修复了一些潜在的引用泄露。

0.12.0.1

  • 修复了缺失的 .h 文件。

0.12

  • clsconfig 现在成为调整基于数据对象类的首选装饰器。
  • 修复了 mutabletuples 的连接问题(问题 #10)。

0.11.1:

  • 现在 dataobject 实例可以更快地分配。

0.11:

  • memoryslots 重命名为 mutabletuple.
  • mutabletupleimmutabletuple 不参与循环垃圾回收。
  • 添加 litelist 类型用于类似列表的对象,它不参与循环垃圾回收。

0.10.3:

  • 引入 DataclassStorage 和 RecordclassStorage。它们允许缓存类并使用它们而无需创建新的类。
  • 添加 iterable 装饰器和参数。现在具有字段的 dataobject 默认不可迭代。
  • astuple 移动到 dataobject.c

0.10.2

  • 修复了 dataobject 的 __copy__ 错误。
  • 修复了自 0.8.5 版本以来出现的记录类和结构类的 pickling 错误(感谢 Connor Wolf)。

0.10.1

  • 现在默认情况下,如果 dataobject 有字段,则不支持序列协议,但支持迭代。
  • 出于可用性原因,默认 argsonly=False。

0.10

  • 发明了一个新的工厂函数 make_class,用于在不支持 GC 的情况下创建不同类型的 dataobject 类。
  • 发明了一个新的元类 datatype 和新的基类 dataobject,用于使用 class 语句创建 dataobject 类。它禁用了 GC 支持,但可以通过装饰器 dataobject.enable_gc 启用。它支持类型提示(Python >= 3.6)和默认值。当类型提示应用于所有数据属性时,它可能不指定 __fields__ 中的字段名称序列(Python >= 3.6)。
  • 现在基于 recordclass 的类也可能不支持循环垃圾回收。这通过 PyGC_Head 的大小减少了内存占用。现在默认情况下,基于 recordclass 的类不支持循环垃圾回收。

0.9

  • 将版本更改为 0.9 以指示一个进步。
  • 清理 dataobject.__cinit__

0.8.5

  • 使基于 arrayclass 的对象支持 setitem/getitem,使基于 structclass 的对象能够不支持它们。默认情况下,如之前一样,基于 structclass 的对象支持 setitem/getitem 协议。
  • 现在只有 dataobject 的实例可以与基于 arrayclassstructclass 的实例进行比较。
  • 现在生成的类可以具有哈希性。

0.8.4

  • 改进了对结构类和数组类只读模式的支持。
  • 为 arrayclass 添加测试。

0.8.3

  • 将类型提示支持添加到基于结构类的类中。

0.8.2

  • __dict__ 类中删除 usedictgcweaklist

0.8.1

  • 默认情况下,通过从源代码构建 recordclass 来移除 Cython 依赖关系 [问题 #7]。

0.8

  • 添加 structclass 工厂函数。它是 recordclass 的类似物,但与 recordclassnamedtuple 相比,其实例具有更小的内存占用(目前使用 Cython 实现)。
  • 添加 arrayclass 工厂函数,该函数生成用于创建固定大小数组的类。这种方法的好处是内存占用也较小(目前使用 Cython 实现)。
  • structclass 工厂现在具有 gc 参数。如果 gc=False(默认值),则创建的类的实例将关闭循环垃圾回收支持。
  • 添加函数 join(C1, C2) 以连接两个基于 structclass 的类 C1 和 C2。
  • 添加 sequenceproxy 函数以从类实例创建不可变和可哈希的代理对象,这些实例通过索引进行访问(目前使用 Cython 实现)。
  • 添加通过 idiom ob['attrname'] 访问记录类对象属性的支持(问题 #5)。
  • readonly 参数添加到记录类工厂中,以生成不可变的 namedtuple。与 collection.namedtuple 相比,它使用与常规记录类相同的描述符来提高性能。

0.7

  • 使 mutabletuple 对象的创建更快。作为副作用:当字段数 >= 8 时,记录类实例的创建时间不会大于具有 __slots__ 的数据类的实例创建时间。
  • 记录类工厂函数现在以与 3.7 版本的 namedtuple 相同的方式创建新的记录类类,没有编译生成的类 Python 源代码。

0.6

  • 在recordclass工厂函数中添加对默认值的支持,与Python 3.7中namedtuple相同的新增功能。

0.5

  • 版本更改为0.5

0.4.4

  • 在RecordClass中添加对默认值的支持(来自Pedro von Hertwig的补丁)
  • 为RecordClass添加测试(从python的NamedTuple测试中采用)

0.4.3

  • 为Python 3.6添加类型支持(来自Vladimir Bolshakov的补丁)
  • 解决内存泄漏问题。

0.4.2

  • 修复属性getter/setter中的内存泄漏。

项目详情


发行历史 发行通知 | RSS订阅

下载文件

下载您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分布

recordclass-0.22.1.tar.gz (1.3 MB 查看散列)

上传

构建分布

recordclass-0.22.1-cp312-cp312-win_amd64.whl (248.8 kB 查看散列)

上传 CPython 3.12 Windows x86-64

recordclass-0.22.1-cp311-cp311-win_amd64.whl (248.7 kB 查看散列)

上传 CPython 3.11 Windows x86-64

recordclass-0.22.1-cp310-cp310-win_amd64.whl (248.2 kB 查看散列)

上传 CPython 3.10 Windows x86-64

recordclass-0.22.1-cp39-cp39-win_amd64.whl (248.3 kB 查看哈希值)

上传于 CPython 3.9 Windows x86-64

recordclass-0.22.1-cp38-cp38-win_amd64.whl (248.4 kB 查看哈希值)

上传于 CPython 3.8 Windows x86-64

由以下支持