55

Python がレコード型をネイティブにサポートしていないのはなぜですか? これは、namedtuple の変更可能なバージョンを持つことの問題です。

使えnamedtuple._replaceました。しかし、これらのレコードをコレクションにnamedtuple._replace含める必要があり、別のインスタンスを作成するため、コレクションを変更する必要があり、すぐに面倒になります。

背景: TCP/IP を介してポーリングして属性を取得する必要があるデバイスがあります。つまり、その表現は変更可能なオブジェクトです。

編集: ポーリングする必要がある一連のデバイスがあります。

編集: PyQt を使用して属性を表示するオブジェクトを反復処理する必要があります。__getitem__やのような特別なメソッドを追加できることは__iter__わかっていますが、もっと簡単な方法があるかどうか知りたいです。

編集:属性が固定されているタイプ(デバイスにあるのと同じように)を好むが、変更可能です。

4

11 に答える 11

53

Python <3.3

このようなことを意味しますか?

class Record(object):
    __slots__= "attribute1", "attribute2", "attribute3",

    def items(self):
        "dict style items"
        return [
            (field_name, getattr(self, field_name))
            for field_name in self.__slots__]

    def __iter__(self):
        "iterate over fields tuple/list style"
        for field_name in self.__slots__:
            yield getattr(self, field_name)

    def __getitem__(self, index):
        "tuple/list style getitem"
        return getattr(self, self.__slots__[index])

>>> r= Record()
>>> r.attribute1= "hello"
>>> r.attribute2= "there"
>>> r.attribute3= 3.14

>>> print r.items()
[('attribute1', 'hello'), ('attribute2', 'there'), ('attribute3', 3.1400000000000001)]
>>> print tuple(r)
('hello', 'there', 3.1400000000000001)

提供されているメソッドは、可能なメソッドのサンプルにすぎないことに注意してください。

Python ≥3.3 の更新

使用できますtypes.SimpleNamespace

>>> import types
>>> r= types.SimpleNamespace()
>>> r.attribute1= "hello"
>>> r.attribute2= "there"
>>> r.attribute3= 3.14

dir(r)属性名を提供します(もちろん、すべてを除外します.startswith("__"))。

于 2011-03-30T20:08:34.557 に答える
18

通常の辞書が使えない理由はありますか?特定の状況では、属性に特定の順序がないようです。

または、クラスインスタンス(優れた属性アクセス構文を備えている)を使用することもできます。インスタンスごとに作成され__slots__ないようにする場合に使用できます。__dict__

また、変更可能な名前付きタプルとして記述されている「レコード」のレシピも見つけました。それらはクラスを使用して実装されます。

アップデート:

シナリオでは順序が重要であると言うので(そしてすべての属性を反復処理したいので)、それOrderedDictが進むべき道のようです。これは、Python2.7以降の標準collectionsモジュールの一部です。Python <2.7の場合、インターネット上に浮かんでいる他の実装があります。

属性スタイルのアクセスを追加するには、次のようにサブクラス化できます。

from collections import OrderedDict

class MutableNamedTuple(OrderedDict):
    def __init__(self, *args, **kwargs):
        super(MutableNamedTuple, self).__init__(*args, **kwargs)
        self._initialized = True

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        if hasattr(self, '_initialized'):
            super(MutableNamedTuple, self).__setitem__(name, value)
        else:
            super(MutableNamedTuple, self).__setattr__(name, value)

次に、次のことができます。

>>> t = MutableNamedTuple()
>>> t.foo = u'Crazy camels!'
>>> t.bar = u'Yay, attribute access'
>>> t.foo
u'Crazy camels!'
>>> t.values()
[u'Crazy camels!', u'Yay, attribute access']
于 2011-03-08T03:05:12.930 に答える
11

これは、次のように、空のクラスとそのインスタンスを使用して実行できます。

>>> class a(): pass
... 
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>> 
于 2011-03-08T03:19:23.570 に答える
10

namedtuple に似ているが変更可能な、recordtype というライブラリがあります。

パッケージのホーム: http://pypi.python.org/pypi/recordtype

簡単な例:

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)

簡単なデフォルト値の例:

Basis = recordtype('Basis', [('x', 1), ('y', 0)])

のフィールドをperson1順番に繰り返します。

map(person1.__getattribute__, Person._fields)
于 2013-01-15T13:08:55.720 に答える
4

この回答は別の回答と重複しています。collections.namedtuple-recordclassの変更可能な代替手段があります

同じ API と最小限のメモリ フットプリントを備えています (実際には高速でもあります)。課題をサポートします。例えば:

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)

より完全ながあります (パフォーマンスの比較も含まれています)。

于 2015-04-30T17:41:44.387 に答える
0

dict独自のこのサブクラスのようなことを行うことができます__dict__基本的な考え方は ActiveState AttrDictレシピと同じですが、実装はよりシンプルです。インスタンスの属性とその値の両方が変更可能であるため、結果は必要以上に可変なものになります。属性は順序付けされていませんが、現在の属性やその値を反復処理できます。

class Record(dict):
    def __init__(self, *args, **kwargs):
        super(Record, self).__init__(*args, **kwargs)
        self.__dict__ = self
于 2013-03-16T07:52:22.297 に答える
0

これは、私が作成した完全に可変な namedtuple です。これは、リストのように動作し、リストと完全に互換性があります。

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})
于 2015-05-10T13:59:15.447 に答える