Skip to main content

Mutable variants of tuple (mutabletuple) and collections.namedtuple (recordclass), which support assignments and more memory saving variants (dataobject, structclass, litelist, ...).

Project description

Recordclass library

Recordclass is MIT Licensed python library. It implements the type mutabletuple, which supports assignment operations, and factory function recordclass in order to create record-like classes – subclasses of the mutabletuple. The function recordclass is a variant of collection.namedtuple. It produces classes with the same API.

The recordclass library was started as a "proof of concept" for the problem of fast "mutable" alternative of namedtuple (see question on stackoverflow). It was evolved further in order to provide more memory saving, fast and flexible types for representation of data objects.

Later recordclass began to provide tools for creating data classes that do not participate in cyclic garbage collection (GC) mechanism, but support only reference counting mechanism. The instances of such classes have not PyGC_Head prefix in the memory, which decrease their size. For CPython 3.8 it is 16 bytes, for CPython 3.4-3.7 it is 24-32 bytes. This may make sense in cases where it is necessary to limit the size of objects as much as possible, provided that they will never be part of circular references in the application. For example, when an object represents a record, the fields of which, by contract, represent simple values (int, float, str, date/time/datetime, timedelta, etc.). Another example is non-recursive data structures in which all leaf elements represent simple values. Of course, in python, nothing prevents you from “shooting yourself in the foot" by creating the reference cycle in the script or application code. But in some cases, this can still be avoided provided that the developer understands what he is doing and uses such classes in the code with care.

First it provide the base class dataobject. The type of dataobject is special metaclass datatype. It control creation of subclasses of dataobject, which doesn't participate in cyclic GC by default (type flag Py_TPFLAGS_HAVE_GC=0). As the result the instance of such class need less memory. The difference is equal to the size of PyGC_Head. All dataobject-based classes doesn't support namedtuple-like API, but rather attrs/dataclasses-like API.

Second it provide another one base class datatuple (special subclass of dataobject). It creates variable sized instance like subclasses of the tuple.

Third it provide factory function make_dataclass for creation of subclasses of dataobject or ``datatuple` with the specified field names.

Four it provide factory function structclass for creation of subclasses of dataobject with namedtuple-like API.

Six it provide the class lightlist, which considers as list-like light container in order to save memory.

Main repository for recordclass is on bitbucket.

Note that starting from 0.13 it is a git-based repository. The old hg-based repository is here.

Here is also a simple example.

Quick start

Installation

Installation from directory with sources

Install:

>>> python setup.py install

Run tests:

>>> python test_all.py

Installation from PyPI

Install:

>>> pip install recordclass

Run tests:

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

Quick start with recordclass

First load inventory:

>>> from recordclass import recordclass

Example with 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 = 10, 20
>>> print(p)
Point(10, 20)

Example with RecordClass and typehints::

>>> from recordclass import RecordClass

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

>>> ptint(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 = 10, 20
>>> print(p)
Point(10, 20)
>>> sys.getsizeof(p) # the output below is for 64bit cpython3.7
40

Quick start with dataobject

First load inventory::

>>> from recordclass import dataobject, asdict

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

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

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

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

>>> p.x, p.y = 10, 20
>>> print(p)
Point(x=10, y=20)
>>> for x in p: print(x)
1
2
>>> asdict(p)
{'x':1, 'y':2}
>>> tuple(p)
(1, 2)

Another way – factory function make_dataclass:

>>> from recordclass import make_dataclass

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

Default values are also supported::

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

or

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

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

Recordclasses and dataobject-based classes may be cached in order to reuse them without duplication::

from recordclass import RecordclassStorage

>>> rs = RecordclassStorage()
>>> A = rs.recordclass("A", "x y")
>>> B = rs.recordclass("A", ["x", "y"])
>>> A is B
True

from recordclass import DataclassStorage

>>> ds = DataclassStorage()
>>> A = ds.make_dataclass("A", "x y")
>>> B = ds.make_dataclass("A", ["x", "y"])
>>> A is B
True

Recordclass

Recordclass was created as answer to question on stackoverflow.com.

Recordclass was designed and implemented as a type that, by api, memory footprint, and speed, would be almost identical to namedtuple, except that it would support assignments that could replace any element without creating a new instance, as in namedtuple (support assignments __setitem__ / setslice__).

The effectiveness of a namedtuple is based on the effectiveness of the tuple type in python. In order to achieve the same efficiency, it was created the type mutabletuple. The structure (PyMutableTupleObject) is identical to the structure of the tuple (PyTupleObject) and therefore occupies the same amount of memory as tuple.

Recordclass is defined on top of mutabletuple in the same way as namedtuple defined on top of tuple. Attributes are accessed via a descriptor (itemgetset), which provides quick access and assignment by attribute index.

The class generated by recordclass looks like::

from recordclass import mutabletuple, itemgetset

class C(mutabletuple, metaclass=recordobject):

    __fields__ = ('attr_1',...,'attr_m')

    attr_1 = itemgetset(0)
    ...
    attr_m = itemgetset(m-1)

    def __new__(cls, attr_1, ..., attr_m):
        'Create new instance of C(attr_1, ..., attr_m)'
        return mutabletuple.__new__(cls, attr_1, ..., attr_m)

etc. following the definition scheme of namedtuple.

As a result, recordclass takes up as much memory as namedtuple, supports fast access by __getitem__ / __setitem__ and by the name of the attribute through the descriptor protocol.

Comparisons

The following table explain memory footprints of recordclass-, recordclass2-base objects:

namedtuple class/__slots__ recordclass dataclass
$b+s+n*p$ $b+n*p$ $b+s+n*p$ $b+n*p-g$

where:

  • b = sizeof(PyObject)
  • s = sizeof(Py_ssize_t)
  • n = number of items
  • p = sizeof(PyObject*)
  • g = sizeof(PyGC_Head)

This is useful in that case when you absolutely sure that reference cycle isn't possible. For example, when all field values are instances of atomic types. As a result the size of the instance is decreased by 24-32 bytes (for cpython 3.4-3.7) and by 16 bytes for cpython 3.8::

class S:
    __slots__ = ('a','b','c')
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

R_gc = recordclass2('R_gc', 'a b c', cyclic_gc=True)
R_nogc = recordclass2('R_nogc', 'a b c')

s = S(1,2,3)
r_gc = R_gc(1,2,3)
r_nogc = R_nogc(1,2,3)
for o in (s, r_gc, r_nogc):
    print(sys.getsizeof(o))
64 64 40

Here are also table with some performance counters:

namedtuple class/__slots__ recordclass dataobject
new 739±24 ns 915±35 ns 763±21 ns 889±34 ns
getattr 84.0±1.7 ns 42.8±1.5 ns 39.5±1.0 ns 41.7±1.1 ns
setattr 50.5±1.7 ns 50.9±1.5 ns 48.8±1.0 ns

Changes:

0.13

  • Make recordclass compiled and work with cpython 3.8.
  • Move repository to git instead of mercurial since bitbucket will drop support of mercurial repositories.
  • Fix some potential reference leaks.

0.12.0.1

  • Fix missing .h files.

0.12

  • clsconfig now become the main decorator for tuning dataobject-based classes.
  • Fix concatenation of mutabletuples (issue #10).

0.11.1:

  • dataobject instances may be deallocated faster now.

0.11:

  • Rename memoryslots to mutabletuple.
  • mutabletuple and immutabletuple dosn't participate in cyclic garbage collection.
  • Add litelist type for list-like objects, which doesn't participate in cyglic garbage collection.

0.10.3:

  • Introduce DataclassStorage and RecordclassStorage. They allow cache classes and used them without creation of new one.
  • Add iterable decorator and argument. Now dataobject with fields isn't iterable by default.
  • Move astuple to dataobject.c.

0.10.2

  • Fix error with dataobject's __copy__.
  • Fix error with pickling of recordclasses and structclasses, which was appeared since 0.8.5 (Thanks to Connor Wolf).

0.10.1

  • Now by default sequence protocol is not supported by default if dataobject has fields, but iteration is supported.
  • By default argsonly=False for usability reasons.

0.10

  • Invent new factory function make_class for creation of different kind of dataobject classes without GC support by default.
  • Invent new metaclass datatype and new base class dataobject for creation dataobject class using class statement. It have disabled GC support, but could be enabled by decorator dataobject.enable_gc. It support type hints (for python >= 3.6) and default values. It may not specify sequence of field names in __fields__ when type hints are applied to all data attributes (for python >= 3.6).
  • Now recordclass-based classes may not support cyclic garbage collection too. This reduces the memory footprint by the size of PyGC_Head. Now by default recordclass-based classes doesn't support cyclic garbage collection.

0.9

  • Change version to 0.9 to indicate a step forward.
  • Cleanup dataobject.__cinit__.

0.8.5

  • Make arrayclass-based objects support setitem/getitem and structclass-based objects able to not support them. By default, as before structclass-based objects support setitem/getitem protocol.
  • Now only instances of dataobject are comparable to 'arrayclass'-based and structclass-based instances.
  • Now generated classes can be hashable.

0.8.4

  • Improve support for readonly mode for structclass and arrayclass.
  • Add tests for arrayclass.

0.8.3

  • Add typehints support to structclass-based classes.

0.8.2

  • Remove usedict, gc, weaklist from the class __dict__.

0.8.1

  • Remove Cython dependence by default for building recordclass from the sources [Issue #7].

0.8

  • Add structclass factory function. It's analog of recordclass but with less memory footprint for it's instances (same as for instances of classes with __slots__) in the camparison with recordclass and namedtuple (it currently implemented with Cython).
  • Add arrayclass factory function which produce a class for creation fixed size array. The benefit of such approach is also less memory footprint (it currently currently implemented with Cython).
  • structclass factory has argument gc now. If gc=False (by default) support of cyclic garbage collection will switched off for instances of the created class.
  • Add function join(C1, C2) in order to join two structclass-based classes C1 and C2.
  • Add sequenceproxy function for creation of immutable and hashable proxy object from class instances, which implement access by index (it currently currently implemented with Cython).
  • Add support for access to recordclass object attributes by idiom: ob['attrname'] (Issue #5).
  • Add argument readonly to recordclass factory to produce immutable namedtuple. In contrast to collection.namedtuple it use same descriptors as for regular recordclasses for performance increasing.

0.7

  • Make mutabletuple objects creation faster. As a side effect: when number of fields >= 8 recordclass instance creation time is not biger than creation time of instaces of dataclasses with __slots__.
  • Recordclass factory function now create new recordclass classes in the same way as namedtuple in 3.7 (there is no compilation of generated python source of class).

0.6

  • Add support for default values in recordclass factory function in correspondence to same addition to namedtuple in python 3.7.

0.5

  • Change version to 0.5

0.4.4

  • Add support for default values in RecordClass (patches from Pedro von Hertwig)
  • Add tests for RecorClass (adopted from python tests for NamedTuple)

0.4.3

  • Add support for typing for python 3.6 (patches from Vladimir Bolshakov).
  • Resolve memory leak issue.

0.4.2

  • Fix memory leak in property getter/setter

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

recordclass-0.13.tar.gz (159.4 kB view details)

Uploaded Source

Built Distributions

recordclass-0.13-cp38-cp38-win_amd64.whl (128.5 kB view details)

Uploaded CPython 3.8Windows x86-64

recordclass-0.13-cp38-cp38-win32.whl (116.5 kB view details)

Uploaded CPython 3.8Windows x86

recordclass-0.13-cp37-cp37m-win_amd64.whl (126.9 kB view details)

Uploaded CPython 3.7mWindows x86-64

recordclass-0.13-cp37-cp37m-win32.whl (114.7 kB view details)

Uploaded CPython 3.7mWindows x86

recordclass-0.13-cp36-cp36m-win_amd64.whl (127.0 kB view details)

Uploaded CPython 3.6mWindows x86-64

recordclass-0.13-cp36-cp36m-win32.whl (114.7 kB view details)

Uploaded CPython 3.6mWindows x86

recordclass-0.13-cp27-cp27m-win_amd64.whl (110.8 kB view details)

Uploaded CPython 2.7mWindows x86-64

recordclass-0.13-cp27-cp27m-win32.whl (101.8 kB view details)

Uploaded CPython 2.7mWindows x86

File details

Details for the file recordclass-0.13.tar.gz.

File metadata

  • Download URL: recordclass-0.13.tar.gz
  • Upload date:
  • Size: 159.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13.tar.gz
Algorithm Hash digest
SHA256 5b81be0d9ee26e1f79217b2aea936f11633785757cb2652f622334c618f6ac10
MD5 fee190aedcdc96a13757c110f4932306
BLAKE2b-256 850535c60ecdf36df90fd1f6d3acad4983e2f4570bded5f55512d1fd663842b9

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: recordclass-0.13-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 128.5 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 bdb302882bc9ef30d2f30815c63f62bed63346ec9372032604d1625f6e05b064
MD5 77bd6b1056cead34024c0385b838d234
BLAKE2b-256 393375e3a5d3cb24bd5ab10e00ec86fd55c86d22d8229da5c20d52836d0def64

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp38-cp38-win32.whl.

File metadata

  • Download URL: recordclass-0.13-cp38-cp38-win32.whl
  • Upload date:
  • Size: 116.5 kB
  • Tags: CPython 3.8, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp38-cp38-win32.whl
Algorithm Hash digest
SHA256 bc49bf758916ceb0eab5b4d650274c5b637faf752c54e837328e101c6cfe2b1b
MD5 0119c89bb651685f350cc9797c55876a
BLAKE2b-256 a5da1f915e5203a2da03db9cca958c032606724fb254cee3a38ddfc56f759a26

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp37-cp37m-win_amd64.whl.

File metadata

  • Download URL: recordclass-0.13-cp37-cp37m-win_amd64.whl
  • Upload date:
  • Size: 126.9 kB
  • Tags: CPython 3.7m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp37-cp37m-win_amd64.whl
Algorithm Hash digest
SHA256 6c4da1b7e947d98818f5ec2dd43fdecc32297be70a639ca780eddea4850dc95d
MD5 423c10cb877f7a87b63a7f8f3dc7b78c
BLAKE2b-256 9ed505c8fb477fe6eaeebbd2c23e7a1096210d47bc196a7bbf2092cb1f123d7f

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp37-cp37m-win32.whl.

File metadata

  • Download URL: recordclass-0.13-cp37-cp37m-win32.whl
  • Upload date:
  • Size: 114.7 kB
  • Tags: CPython 3.7m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp37-cp37m-win32.whl
Algorithm Hash digest
SHA256 0fe22addc55b337fbbf4b20e54fc78a13cc28aaae8de4f8b7d088355d6c46c7e
MD5 31f9c5dc44ead250b97aa321a6b2b6d4
BLAKE2b-256 e0b48f5217fd9b5d20c45bc1b05241eb77a419325d10e8026cf5fad51e13b116

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp36-cp36m-win_amd64.whl.

File metadata

  • Download URL: recordclass-0.13-cp36-cp36m-win_amd64.whl
  • Upload date:
  • Size: 127.0 kB
  • Tags: CPython 3.6m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp36-cp36m-win_amd64.whl
Algorithm Hash digest
SHA256 5e7e8ecb3c084b7d82a096576b25a289cca05cd943507a08a99f7ab9c2e3c83f
MD5 a75de4dae56995ae37bccada75930f43
BLAKE2b-256 51cbbb21df0c5071d3fb26d9e344078ceb4bc97b7fa38e9298d96921a9579378

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp36-cp36m-win32.whl.

File metadata

  • Download URL: recordclass-0.13-cp36-cp36m-win32.whl
  • Upload date:
  • Size: 114.7 kB
  • Tags: CPython 3.6m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp36-cp36m-win32.whl
Algorithm Hash digest
SHA256 04713c4c62d901e0e1bb78d3e2685c3f13c9616e4fc8bd7610e8edda6133181a
MD5 248841028064eb637955be4433d0a9eb
BLAKE2b-256 d41325a5506caa620af56b29324ddabb7819af76683e831ccb26b7cc04d842db

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp27-cp27m-win_amd64.whl.

File metadata

  • Download URL: recordclass-0.13-cp27-cp27m-win_amd64.whl
  • Upload date:
  • Size: 110.8 kB
  • Tags: CPython 2.7m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp27-cp27m-win_amd64.whl
Algorithm Hash digest
SHA256 d832963c7e632d87cef81903e46eb6ac75e4a6891ce53dbd081f295fb009d578
MD5 e40e08077e76759ad34253ee456ea0e6
BLAKE2b-256 cbcd2e6fda1f2c342ad75b85350be63dd8eb0edcf37be484f9924c2e0db10a5d

See more details on using hashes here.

File details

Details for the file recordclass-0.13-cp27-cp27m-win32.whl.

File metadata

  • Download URL: recordclass-0.13-cp27-cp27m-win32.whl
  • Upload date:
  • Size: 101.8 kB
  • Tags: CPython 2.7m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.22.0 setuptools/44.0.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.7.6

File hashes

Hashes for recordclass-0.13-cp27-cp27m-win32.whl
Algorithm Hash digest
SHA256 11f94d2a032e127a5306d0c816990ca67d6fb28f2ec2ed3ae5caa32c278cf5fe
MD5 f7f7df7c529030ed9dc14af1261482c5
BLAKE2b-256 ec23455e6356048eb8b8efb2bf3b092909c6c1276b6a8d956b5177d00e7c79b8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page