我不确定这项技术的用语。我想创建一个列表,如果我尝试访问列表范围之外的元素,列表将自行循环"。我想要实现的行为示例:
>>> musical_scale = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
>>> musical_scale[2]
E
>>> musical_scale[7]
C
>>> musical_scale[-1]
B
我想我可以编写一个这样做的类,但我认为可能有更正确的方法。
答案 0 :(得分:4)
创建List的子类将是一种非常有用的方法。这样的事情,也许是:
class modList(list):
def __getitem__(self, i):
if len(self) == 0:
raise IndexError # Or do something else if you want, like return []
i = i % len(self) # self.__len__() works also
return super(modList, self).__getitem__(i) # In Python 3, super().__getitem__(i)
如果你想做切片,它有点复杂,但相似。通过StackOverflow查找此内容:
def __getitem__(self, i):
if isinstance(i, int):
if len(self) == 0:
raise IndexError
i = i % len(self)
return super(modList, self).__getitem__(i) # list.__getitem__(i) works too
elif isinstance(i, slice):
if len(self) == 0:
return []
start = i.start % len(self)
stop = i.stop % len(self)
step = i.step
return super(modList, self).__getItem__(slice(start, stop, step))
else:
raise TypeError("invalid index")
虽然这个切片修改可能会给你一个类似[3:2]
的情况,它将返回一个空列表。基本上切片很难,你需要决定如何实现它,但希望这是一个开始。
(感谢@JonClements提供聊天中的所有建议。)
编辑:现在我们有一些处理程序,如果你有一个空列表。 @wim建议为单次访问引发错误,并为切片返回[]
。真的,这取决于你想要做什么,但这对我来说似乎是明智的,所以这就是我在这个答案中所包含的内容。
编辑编辑:如果您使用的是Python 2.x,我相信您还需要覆盖__getslice__
。
答案 1 :(得分:3)
使用模数运算符%
来"循环返回"索引超过列表末尾
>>> musical_scale = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
def getValue(x, l):
return l[x % len(l)]
>>> getValue(0, musical_scale)
'C'
>>> getValue(9, musical_scale)
'E'
答案 2 :(得分:0)
如果您想扩展列表类以处理获取和设置...
class UnsatList(list):
def __getitem__(self, index):
try:
return list.__getitem__(self, index % len(self))
except ZeroDivisionError:
raise IndexError('list assignment index out of range')
def __setitem__(self, index, value):
try:
return list.__setitem__(self, index % len(self), value)
except ZeroDivisionError:
raise IndexError('list assignment index out of range')
if __name__ == '__main__':
test_list = UnsatList([1,2,3,4])
assert test_list[0] == 1
assert test_list[4] == 1
assert test_list[8] == 1
assert test_list[-8] == 1
test_list[4] = 5
assert test_list[0] == 5
assert test_list[4] == 5
assert test_list[8] == 5
assert test_list[-8] == 5
答案 3 :(得分:0)
这是一个解决方案,但请记住The Zen of Python: Flat优于嵌套。
您可以编写例如:
a = [1, [2]]
a[1].append(a)
然后你的列表a具有“无限深度”,你可以通过反复执行以下来循环这两个元素:
a[0]
a = a[1]
如果不玩深度,你不能这样做,因为不像Lisp,Python列表是变长数组,而不是链表。
以下是自动执行整个操作的小功能:
def loopify(a):
r = b = []
for x in a:
b.append(x)
b.append([])
c = b
b = b[1]
c[1] = r
return r
def loopgen(a):
while True:
yield a[0]
a = a[1]
然后用你的例子:
musical_scale = loopify(['C', 'D', 'E', 'F', 'G', 'A', 'B'])
g = loopgen(musical_scale)
[next(g) for i in range(20)]
这会产生:
['C','D','E','F','G','A','B','C','D','E','F','G ','A','B','C','D','E','F','G','A']
当然,也可以使用mod或跟踪当前索引,并在溢出列表时将其重置为0。其他答案告诉你如何做到这一点。
另请注意,链表方法不允许随机访问,只允许顺序访问(因此上面的生成器)。如果您需要随机访问列表,模块化算法是最合适的方法。