Why does Python not support a record type natively? It\'s a matter of having a mutable version of namedtuple.
I could use namedtuple._replace
. But I nee
This question is old, but just for the sake of completeness, Python 3.7 has dataclasses which are pretty much records.
>>> 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)
The attrs third party library provides more functionality for both Python 2 and Python 3. Nothing wrong with vendoring dependencies either if the requirement is more around things you can't keep locally rather than specifically only using the stdlib. dephell has a nice helper for doing that.
As tzot stated, since Python ≥3.3, Python does have a mutable version of namedtuple: types.SimpleNamespace.
These things are very similar to the new C# 9 Records.
Here are some usage examples:
>>> 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
This can be done using an empty class and instances of it, like this:
>>> class a(): pass
...
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>>
There's a library similar to namedtuple, but mutable, called recordtype.
Package home: http://pypi.python.org/pypi/recordtype
Simple example:
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)
Simple default value example:
Basis = recordtype('Basis', [('x', 1), ('y', 0)])
Iterate through the fields of person1
in order:
map(person1.__getattribute__, Person._fields)
Here is a complete mutable namedtuple I made, which behaves like a list and is totally compatible with it.
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})
This answer duplicates another one.
There is a mutable alternative to collections.namedtuple
- recordclass.
It has same API and memory footprint as namedtuple
(actually it also faster). It support assignments. For example:
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)
There is more complete example (it also include performance comparisons).