Why does Python not support record type? (i.e. mutable namedtuple)

后端 未结 11 1556
眼角桃花
眼角桃花 2020-12-12 22:10

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

相关标签:
11条回答
  • 2020-12-12 22:16

    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.

    0 讨论(0)
  • 2020-12-12 22:26

    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:

    Positional constructor arguments

    >>> 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)
    

    Pretty repr

    >>> loc_1
    Location(lat=49.4, long=8.7)
    

    Mutable

    >>> loc_2 = Location()
    >>> loc_2
    Location(lat=0, long=0)
    >>> loc_2.lat = 49.4
    >>> loc_2
    Location(lat=49.4, long=0)
    

    Value semantics for equality

    >>> loc_2 == loc_1
    False
    >>> loc_2.long = 8.7
    >>> loc_2 == loc_1
    True
    

    Can add attributes at runtime

    >>> loc_2.city = 'Heidelberg'
    >>> loc_2
    
    0 讨论(0)
  • 2020-12-12 22:31

    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'
    >>> 
    
    0 讨论(0)
  • 2020-12-12 22:31

    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)
    
    0 讨论(0)
  • 2020-12-12 22:31

    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})
    
    0 讨论(0)
  • 2020-12-12 22:32

    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).

    0 讨论(0)
提交回复
热议问题