您可以在python中轻松创建一个类似列表的对象,该对象使用类似描述符的内容吗?

Spo*_*ser 5 python python-2.7

我正在尝试编写一个抽象一些接口的接口。

底部界面在某些方面与要求不一致:有时是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)
Run Code Online (Sandbox Code Playgroud)

该文档建议为行为像可变序列的对象定义17种方法(不包括构造函数)。我认为子类化根本无法list帮助我。感觉我应该能够仅在某个地方定义一个getter和setter来实现这一目标。

UserList 显然已贬值(尽管在python3中?我使用的是2.7)。

有没有一种方法可以实现此目的或类似的方法,而不必重新定义太多功能?

jsb*_*eno 3

您不需要重写所有列表方法 - __setitem__、__init__ 和 \append 应该足够了 - 您可能还需要 insert 和其他一些方法。您可以编写 __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))
Run Code Online (Sandbox Code Playgroud)

例子:

>>> 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
>>> 
Run Code Online (Sandbox Code Playgroud)

- 编辑 -

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)
Run Code Online (Sandbox Code Playgroud)

它的工作原理如下:

>>> 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]
>>> 
Run Code Online (Sandbox Code Playgroud)