なぜPythonはレコードタイプをネイティブにサポートしないのですか?namedtupleの変更可能なバージョンを持つことの問題です。
namedtuple._replace
を使用できます。しかし、これらのレコードをコレクションに含める必要があり、namedtuple._replace
が別のインスタンスを作成するため、コレクションを変更する必要があります。
背景:TCP/IPを介してポーリングすることで属性を取得する必要があるデバイスがあります。つまり、その表現は可変オブジェクトです。
編集:ポーリングする必要があるデバイスのセットがあります。
編集:PyQtを使用して属性を表示するオブジェクトを反復処理する必要があります。 __getitem__
や__iter__
などの特別なメソッドを追加できることは知っていますが、もっと簡単な方法があるかどうか知りたいです。
編集:属性が固定されているタイプ(デバイスにあるのと同じ)を好みますが、可変です。
こんな感じ?
_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)
_
提供されるメソッドは、可能なメソッドのサンプルにすぎないことに注意してください。
_types.SimpleNamespace
_ を使用できます:
_>>> import types
>>> r= types.SimpleNamespace()
>>> r.attribute1= "hello"
>>> r.attribute2= "there"
>>> r.attribute3= 3.14
_
dir(r)
は属性名を提供します(もちろんすべての.startswith("__")
を除外します)。
通常の辞書を使用できない理由はありますか?特定の状況では、属性に特定の順序がないようです。
または、クラスインスタンス(Nice属性アクセス構文を使用)を使用することもできます。インスタンスごとに__slots__
を作成しないようにする場合は、__dict__
を使用できます。
また、 "records"のレシピ を見つけました。これは、変更可能な名前付きタプルとして記述されています。それらはクラスを使用して実装されます。
更新:
シナリオでは順序が重要であると言うので(そして、すべての属性を反復処理したいので)、OrderedDict
が道のりのようです。これは、Python 2.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']
これは、次のように空のクラスとそのインスタンスを使用して実行できます。
>>> class a(): pass
...
>>> ainstance = a()
>>> ainstance.b = 'We want Moshiach Now'
>>> ainstance.b
'We want Moshiach Now'
>>>
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)
この回答は 別の1つ と重複しています。 collections.namedtuple
- recordclass の代わりに変更可能なものがあります。
namedtuple
と同じ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)
より完全な 例 があります(パフォーマンスの比較も含まれます)。
密接に関連する PythonでのTupleという名前のmutableの存在? 質問13のテストは、namedtuple
の6つのmutableの代替を比較するために使用されます。
最新の namedlist 1.7 passes Python 2.7およびPython 3.52016年1月11日現在。これは純粋なpython実装です。
これらのテストによる次善の候補は、C拡張である recordclass
です。もちろん、C拡張が優先されるかどうかは要件によって異なります。
特にテストの詳細については、 PythonでのTupleという名前の可変の存在? を参照してください。
時間をかけて集められたいくつかの有用なトリックに基づいて、この「frozenclass」デコレーターは必要なほとんどすべてを実行します。 http://Pastebin.com/fsuVyM45
そのコードは70%を超えるドキュメントとテストであるため、ここではこれ以上述べません。
これは、リストのように動作し、完全に互換性がある、私が作成した完全な変更可能な名前付きタプルです。
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})
独自の__dict__
であるthis dict
subclassのようなことができます。基本的な概念はActiveState AttrDict レシピの概念と同じですが、実装は簡単です。インスタンスの属性と値の両方が変更可能であるため、結果は必要以上に変更可能なものになります。属性は順序付けられていませんが、現在の属性やその値を反復処理できます。
class Record(dict):
def __init__(self, *args, **kwargs):
super(Record, self).__init__(*args, **kwargs)
self.__dict__ = self