簡體   English   中英

你能否輕松地在python中創建一個類似於列表的對象,它使用類似描述符的項目?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM