有时可以使用“MixIn”类修补元组和列表包装问题(超过“[-1:0]”边界)。
例如代码:
#!/usr/bin/env python
def wrapslice(*sl):
if len(sl)==1: sl=sl[0]
if isinstance(sl, int): return sl
else:
if not isinstance(sl,slice): sl=slice(*sl)
if None in (sl.start,sl.stop): return sl
elif sl.start>=0 or sl.stop<0: return sl
else: return slice(sl.start, None, sl.step)
class WrapMixin(object):
# Not sure how to use a decorator on a MixIn, so I use some "boilerplate" code...
def __getitem__(self,sl):
return super(WrapMixin,self).__getitem__(wrapslice(sl))
def __setitem__(self,sl, value):
return super(WrapMixin,self).__setitem__(wrapslice(sl), value)
def __delitem__(self,sl):
return super(WrapMixin,self).__delitem__(wrapslice(sl))
# Note: for __getslice__ [-1:2] converts to [-1+len:2]...
# i.e. add len to any -ve numbers! Ouch...
def __getslice__(self,start,stop):
if start>stop: stop+=len(self)
sl=wrapslice(start,stop)
return super(WrapMixin,self).__getslice__(sl.start, sl.stop)
def __setslice__(self,start,stop, value):
if start>stop: stop+=len(self)
sl=wrapslice(start,stop)
return super(WrapMixin,self).__setslice__(sl.start, sl.stop, value)
def __delslice__(self,start,stop):
if start>stop: stop+=len(self)
sl=wrapslice(start,stop)
return super(WrapMixin,self).__delslice__(sl.start, sl.stop)
# Todo: def append/insert/append/iadd/radd ...
#class WrapString(WrapMixin, string): pass # FAIL
class WrapTuple(WrapMixin, tuple): pass # GOOD
class WrapList(WrapMixin, list): pass # GOOD
if __name__=="__main__": # test case
aeiou=list("aeiou")
AEIOU=WrapList("AEIOU")
len_slice=3
for i in range(-5,5,1):
vanilla=slice(i,i+len_slice)
fixed=wrapslice(vanilla)
print AEIOU[i]+"...","Vanilla:",vanilla,"vs Patched:",fixed
print " get_slice VANILLA: %15r -> PATCHED: %15r"%(aeiou[vanilla.start:vanilla.stop], AEIOU[vanilla.start:vanilla.stop])
print " get_item VANILLA: %15r -> PATCHED: %15r"%(aeiou[vanilla], AEIOU[vanilla])
输出:
A... Vanilla: slice(-5, -2, None) vs Patched: slice(-5, -2, None)
get_slice VANILLA: ['a', 'e', 'i'] -> PATCHED: ['A', 'E', 'I']
get_item VANILLA: ['a', 'e', 'i'] -> PATCHED: ['A', 'E', 'I']
E... Vanilla: slice(-4, -1, None) vs Patched: slice(-4, -1, None)
get_slice VANILLA: ['e', 'i', 'o'] -> PATCHED: ['E', 'I', 'O']
get_item VANILLA: ['e', 'i', 'o'] -> PATCHED: ['E', 'I', 'O']
I... Vanilla: slice(-3, 0, None) vs Patched: slice(-3, None, None)
get_slice VANILLA: [] -> PATCHED: ['I', 'O', 'U']
get_item VANILLA: [] -> PATCHED: ['I', 'O', 'U']
O... Vanilla: slice(-2, 1, None) vs Patched: slice(-2, None, None)
get_slice VANILLA: [] -> PATCHED: ['O', 'U']
get_item VANILLA: [] -> PATCHED: ['O', 'U']
U... Vanilla: slice(-1, 2, None) vs Patched: slice(-1, None, None)
get_slice VANILLA: [] -> PATCHED: ['U'] <= Found U!
get_item VANILLA: [] -> PATCHED: ['U']
A... Vanilla: slice(0, 3, None) vs Patched: slice(0, 3, None)
get_slice VANILLA: ['a', 'e', 'i'] -> PATCHED: ['A', 'E', 'I']
get_item VANILLA: ['a', 'e', 'i'] -> PATCHED: ['A', 'E', 'I']
E... Vanilla: slice(1, 4, None) vs Patched: slice(1, 4, None)
get_slice VANILLA: ['e', 'i', 'o'] -> PATCHED: ['E', 'I', 'O']
get_item VANILLA: ['e', 'i', 'o'] -> PATCHED: ['E', 'I', 'O']
I... Vanilla: slice(2, 5, None) vs Patched: slice(2, 5, None)
get_slice VANILLA: ['i', 'o', 'u'] -> PATCHED: ['I', 'O', 'U']
get_item VANILLA: ['i', 'o', 'u'] -> PATCHED: ['I', 'O', 'U']
O... Vanilla: slice(3, 6, None) vs Patched: slice(3, 6, None)
get_slice VANILLA: ['o', 'u'] -> PATCHED: ['O', 'U']
get_item VANILLA: ['o', 'u'] -> PATCHED: ['O', 'U']
U... Vanilla: slice(4, 7, None) vs Patched: slice(4, 7, None)
get_slice VANILLA: ['u'] -> PATCHED: ['U']
get_item VANILLA: ['u'] -> PATCHED: ['U']
注意:字符串类型不能被子类化,因此不能被修补。
另外:使用负 start:stop 参数调用成员 __getslice__ 和 __setslice__ 很棘手,因为这些参数会被解释器“摆弄”......因此需要额外的补丁代码......