[英]Can you easily create a list-like object in python that uses something like a descriptor for its items?
我正在嘗試編寫一個在某種程度上抽象出另一個接口的接口。
底層界面在需要時有些不一致:有時是id,有時是名字。 我試圖隱藏這些細節。
我想創建一個類似列表的對象,允許您為其添加名稱,但內部存儲與這些名稱相關聯的ID。
最好是,我想對類屬性使用描述符之類的東西,除了它們在列表項上工作。 也就是說,一個函數(如__get__
)被調用,用於添加到列表中的所有內容,以將其轉換為我想在內部存儲的id,以及另一個函數(如__set__
)返回對象(提供便利方法)而不是實際的id嘗試從列表中檢索項目時。
這樣我就可以這樣做:
def get_thing_id_from_name(name):
# assume that this is more complicated
return other_api.get_id_from_name_or_whatever(name)
class Thing(object)
def __init__(self, thing_id):
self.id = thing_id
self.name = other_api.get_name_somehow(id)
def __eq__(self, other):
if isinstance(other, basestring):
return self.name == other
if isinstance(other, Thing):
return self.thing_id == other.thing_id
return NotImplemented
tl = ThingList()
tl.append('thing_one')
tl.append('thing_two')
tl[1] = 'thing_three'
print tl[0].id
print tl[0] == 'thing_one'
print tl[1] == Thing(3)
文檔建議為一個像可變序列一樣的對象定義17個方法 (不包括構造函數)。 我認為子類化list
根本不會幫助我。 感覺我應該能夠實現這個,只是在某處定義一個getter和setter。
UserList
顯然已經折舊了(雖然在python3中?雖然我使用的是2.7)。
有沒有辦法實現這個或類似的東西,而不必重新定義這么多的功能?
喲不需要覆蓋所有列表方法 - __setitem __,__ init__和\\ append應該足夠了 - 你可能想要插入和其他一些。 您可以編寫__setitem__和__getitem__來在特殊的“Thing”類中調用__set__和__get__方法,就像描述符一樣。
這是一個簡短的例子 - 可能是你想要的東西:
class Thing(object):
def __init__(self, thing):
self.value = thing
self.name = str(thing)
id = property(lambda s: id(s))
#...
def __repr__(self):
return "I am a %s" %self.name
class ThingList(list):
def __init__(self, items):
for item in items:
self.append(item)
def append(self, value):
list.append(self, Thing(value))
def __setitem__(self, index, value):
list.__setitem__(self, index, Thing(value))
例:
>>> a = ThingList(range(3))
>>> a.append("three")
>>> a
[I am a 0, I am a 1, I am a 2, I am a three]
>>> a[0].id
35242896
>>>
- 編輯 -
OP評論說:“我真的希望有一種方法可以獲得列表中的所有功能 - 添加,擴展,切片等等,只需重新定義獲取/設置項目行為。”
因此,我們必須以這種方式覆蓋所有相關方法。 但是如果我們想要避免的只是很多鍋爐板代碼與很多函數幾乎相同,那么新的,重寫方法可以動態生成 - 我們需要的是一個裝飾器來將普通對象改為所有的Things
設置值的操作:
class Thing(object):
# Prevents duplicating the wrapping of objects:
def __new__(cls, thing):
if isinstance(thing, cls):
return thing
return object.__new__(cls, thing)
def __init__(self, thing):
self.value = thing
self.name = str(thing)
id = property(lambda s: id(s))
#...
def __repr__(self):
return "I am a %s" %self.name
def converter(func, cardinality=1):
def new_func(*args):
# Pick the last item in the argument list, which
# for all item setter methods on a list is the one
# which actually contains the values
if cardinality == 1:
args = args[:-1] + (Thing(args[-1] ),)
else:
args = args[:-1] + ([Thing(item) for item in args[-1]],)
return func(*args)
new_func.func_name = func.__name__
return new_func
my_list_dict = {}
for single_setter in ("__setitem__", "append", "insert"):
my_list_dict[single_setter] = converter(getattr(list, single_setter), cardinality=1)
for many_setter in ("__setslice__", "__add__", "__iadd__", "__init__", "extend"):
my_list_dict[many_setter] = converter(getattr(list, many_setter), cardinality="many")
MyList = type("MyList", (list,), my_list_dict)
它起作用:
>>> a = MyList()
>>> a
[]
>>> a.append(5)
>>> a
[I am a 5]
>>> a + [2,3,4]
[I am a 5, I am a 2, I am a 3, I am a 4]
>>> a.extend(range(4))
>>> a
[I am a 5, I am a 0, I am a 1, I am a 2, I am a 3]
>>> a[1:2] = range(10,12)
>>> a
[I am a 5, I am a 10, I am a 11, I am a 1, I am a 2, I am a 3]
>>>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.