我正在尝试编写一个在某种程度上抽象出另一个界面的界面。
底层界面在需要时有些不一致:有时是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)。
有没有办法实现这个或类似的东西,而不必重新定义这么多的功能?
答案 0 :(得分:3)
哟不需要覆盖所有列表方法 - __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
>>>
- 编辑 -
O.P.评论说:“我真的希望有一种方法可以获得列表中的所有功能 - 添加,扩展,切片等等,只需重新定义获取/设置项目行为。”
因此,我们必须以这种方式覆盖所有相关方法。但是,如果我们想要避免的只是很多锅炉板代码与许多函数几乎相同,那么新的,重写方法可以动态生成 - 我们需要的是一个装饰器来将普通对象更改为{{1对于设置值的所有操作:
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)