如何创建一个类似List的类,它允许在与py2和py3一起使用的片上调用包含对象的方法
问题描述:
下面的代码来自SCons的代码库。我们正在努力移植代码,以便它可以在Python 2.7.x和3.x上使用。如何创建一个类似List的类,它允许在与py2和py3一起使用的片上调用包含对象的方法
下面的代码工作正常的python 2.7.x,但当下运行的Python 3.5如下失败:
python3.5 ~/tmp/blah123.py Traceback (most recent call last):
File "/home/bdbaddog/tmp/blah123.py", line 73, in print("stuff:%s"%nl[0:2].bar) AttributeError: 'list' object has no attribute 'bar'
此代码是有点核心给SCons的功能。任何帮助将是最受欢迎的。 (见这里的原代码:src/engine/SCons/Util.py)
from __future__ import print_function
try:
from UserList import UserList
except ImportError as e:
from collections import UserList
class NodeList(UserList):
"""This class is almost exactly like a regular list of Nodes
(actually it can hold any object), with one important difference.
If you try to get an attribute from this list, it will return that
attribute from every item in the list. For example:
>>> someList = NodeList([ ' foo ', ' bar ' ])
>>> someList.strip()
[ 'foo', 'bar' ]
"""
def __nonzero__(self):
return len(self.data) != 0
def __bool__(self):
return self.__nonzero__()
def __str__(self):
return ' '.join(map(str, self.data))
def __iter__(self):
return iter(self.data)
def __call__(self, *args, **kwargs):
result = [x(*args, **kwargs) for x in self.data]
return self.__class__(result)
def __getattr__(self, name):
result = [getattr(x, name) for x in self.data]
return self.__class__(result)
# def __getitem__(self, index):
# return self.__class__(self.data[index])
# return self.data[index]
def __getitem__(self, index):
"""
This comes for free on py2,
but py3 slices of NodeList are returning a list
breaking slicing nodelist and refering to
properties and methods on contained object
"""
# return self.__class__(self.data[index])
if isinstance(index, slice):
# Expand the slice object using range()
# to a maximum of eight items.
return [self[x] for x in
range(*index.indices(8))]
else:
# Return one item of the tart
return self.data[index]
class TestClass(object):
def __init__(self, name, child=None):
self.child = child
self.bar = name
t1 = TestClass('t1', TestClass('t1child'))
t2 = TestClass('t2', TestClass('t2child'))
t3 = TestClass('t3')
nl = NodeList([t1, t2, t3])
print("stuff:%s"%nl[0:2].bar)
print("another:%s"%nl[1:].bar)
assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
nl[0:2].child.bar
for f in nl:
print("->%s"%f.bar)
答
你__getitem__
调用一个slice
或许应该再次回到同一类的新实例。例如:
def __getitem__(self, index):
if isinstance(index, slice):
return self.__class__(self[x] for x in
range(*index.indices(len(self.data)))
else:
return self.data[index]
那么你的测试用例打印:
stuff:t1 t2
->t1
->t2
->t3
谢谢!这涵盖了完整测试套件中的几乎所有测试。一个单元测试仍然失败。如果我可以追踪并简化,我将添加到上面的示例代码。 – bdbaddog
在上面添加更新测试请参阅“另一个”。 Fix实际上是删除上面index.indices(8)中的最大值8。并用index.indices(len(self.data))替换。如果你在上面编辑,我会标记为答案。 – bdbaddog
@bdbaddog太好了!我很高兴它的工作:) – MSeifert