为 Python 列表指定默认偏移量
Specify a Default Offset for Python List
python 中有没有一种方法可以为列表指定默认偏移量?
喜欢:
a = [0, 1, 2, 3, 4, 5, 6]
a.offset = 2
这样每当access/modify使用索引时,索引都会先加上偏移量:
a[0] == 2
a[4] == 6
Python 或我所知道的任何其他语言都没有这样的功能。假设您可以获得批准的功能,您建议的语法是合理的。但是,它有几个缺点。
直到并且除非此功能成为普遍使用,否则任何试图阅读此类代码的人都会感到困惑。零基和一基索引是“规则”;任意索引违反了长期以来的假设。
您会严重限制 Python 的右端索引:语义不明确。如果有人写 a[-1]
来访问最后一个元素,他们应该得到那个元素(这是语言定义的习语),原始 a[1]
元素(根据您的定义),“反射” a[-3]
,或 index out of bounds
试图向右移动两个元素?
请注意,Python 可以让您定义自己的功能:
class
任何时候您不喜欢给定的数据类型,您都可以创建自己的数据类型。您不能更改内置类型,但您可以通过继承 list
并编写自己的 get
和其他方法来做您喜欢的事情。
如果您只是从列表中读取数据,您可能会使用原始数据的下标副本:
a = [0, 1, 2, 3, 4, 5, 6]
a = a[2:]
a[0] == 2 # True
a[4] == 6 # True
请记住,这会使用相同的变量名创建列表的副本,因此您将丢失原始内容(索引 0 和 1)。如果你确实需要它,你可以将它保存在一个单独的变量中:
a = [0, 1, 2, 3, 4, 5, 6]
a0,a = a,a[2:]
a[0] == 2 # True
a[4] == 6 # True
a0[0] == 0 # True
a0[4] == 4 # True
如果你真的需要一个具有读写能力的原始数组视图,那么我建议使用 numpy 数组:
import numpy as np
a = np.array([0, 1, 2, 3, 4, 5, 6])
b = a[2:].view()
b[0] == 2 # True
b[4] == 4 # True
b[1] = 99
print(a) # [ 0 1 2 99 4 5 6]
a[3] == 99 # True
如果你想自己实现类似于 numpy 的东西,你可以创建一个 class 代表列表上的一个“视图”,内部切片 属性(开始、停止、步骤) :
class ListView:
def __init__(self,aList,start=None,stop=None,step=1):
self.data = aList
self.slice = slice(start,stop,step)
@property
def indices(self): return range(len(self.data))[self.slice]
def offset(self,index=None):
if not isinstance(index,slice): return self.indices[index]
first = self.indices[index][0]
last = self.indices[index][-1]
step = (index.step or 1)*(self.slice.step or 1)
return slice(first,last+1-2*(step<0),step)
def __len__(self): return len(self.indices)
def __getitem__(self,index): return self.data[self.offset(index)]
def __repr__(self): return self[:].__repr__()
def __iter__(self): return self[:].__iter__()
def __setitem__(self,index,value): self.data[self.offset(index)] = value
def __delitem__(self,index): del self.data[self.offset(index)]
用法:
a = list(range(1,21))
v = ListView(a,3,-2,2)
len(v) # 8
print(a)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
print(v)
# [4, 6, 8, 10, 12, 14, 16, 18]
v[2] += 80
print(a)
# [1, 2, 3, 4, 5, 6, 7, 88, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
v.slice = slice(-4,None,-3)
print(v)
# [17, 14, 11, 88, 5, 2]
没有实现此目的的内置方法。但是,您可以通过扩展触发 list
to get this behaviour. When you do my_list[n]
, internally __getitem__()
函数来创建自定义 class。您可以通过将 offset
添加到索引来将此函数覆盖为 return 值。
同理,list contains other magic functions which you can override to further modify the behaviour of your custom class. For example, __setitem__()
is triggered when you assign any value to list, __delitem__()
删除item时触发
这是创建 OffsetList
class 的示例代码,它在创建 list
时将附加参数作为 offset
,并对 [=40= 执行基于索引的操作]index
+offset
值.
class OffsetList(list):
def __init__(self, offset, *args, **kwargs):
super(OffsetList, self).__init__(*args, **kwargs)
self.offset = offset
def _get_offset_index(self, key):
if isinstance(key, slice):
key = slice(
None if key.start is None else key.start + self.offset,
None if key.stop is None else key.stop + self.offset,
key.step
)
elif isinstance(key, int):
key += self.offset
return key
def __getitem__(self, key):
key = self._get_offset_index(key)
return super(OffsetList, self).__getitem__(key)
def __setitem__(self, key, value):
key = self._get_offset_index(key)
return super(OffsetList, self).__setitem__(key, value)
def __delitem__(self, key):
key = self._get_offset_index(key)
return super(OffsetList, self).__delitem__(key)
样本运行:
# With offset as `0`, behaves as normal list
>>> offset_list = OffsetList(0, [10,20,30,40,50,60])
>>> offset_list[0]
10
# With offset as `1`, returns index+1
>>> offset_list = OffsetList(1, [10,20,30,40,50,60])
>>> offset_list[0]
20
# With offset as `2`, returns index+2
>>> offset_list = OffsetList(2, [10,20,30,40,50,60])
>>> offset_list[0]
30
# Slicing support, with `start` as start+offset and `end` as end+offset
>>> offset_list[1:]
[40, 50, 60]
# Assigning new value, based on index+offset
>>> offset_list[0] = 123
>>> offset_list
[10, 20, 123, 40, 50, 60]
# Deleting value based on index+offset
>>> del offset_list[0]
>>> offset_list
[10, 20, 40, 50, 60]
同样,您可以根据需要修改 __len__()
, __iter__()
, __repr__()
, __str__()
等其他魔法函数的行为。
python 中有没有一种方法可以为列表指定默认偏移量? 喜欢:
a = [0, 1, 2, 3, 4, 5, 6]
a.offset = 2
这样每当access/modify使用索引时,索引都会先加上偏移量:
a[0] == 2
a[4] == 6
Python 或我所知道的任何其他语言都没有这样的功能。假设您可以获得批准的功能,您建议的语法是合理的。但是,它有几个缺点。
直到并且除非此功能成为普遍使用,否则任何试图阅读此类代码的人都会感到困惑。零基和一基索引是“规则”;任意索引违反了长期以来的假设。
您会严重限制 Python 的右端索引:语义不明确。如果有人写 a[-1]
来访问最后一个元素,他们应该得到那个元素(这是语言定义的习语),原始 a[1]
元素(根据您的定义),“反射” a[-3]
,或 index out of bounds
试图向右移动两个元素?
请注意,Python 可以让您定义自己的功能:
class
任何时候您不喜欢给定的数据类型,您都可以创建自己的数据类型。您不能更改内置类型,但您可以通过继承 list
并编写自己的 get
和其他方法来做您喜欢的事情。
如果您只是从列表中读取数据,您可能会使用原始数据的下标副本:
a = [0, 1, 2, 3, 4, 5, 6]
a = a[2:]
a[0] == 2 # True
a[4] == 6 # True
请记住,这会使用相同的变量名创建列表的副本,因此您将丢失原始内容(索引 0 和 1)。如果你确实需要它,你可以将它保存在一个单独的变量中:
a = [0, 1, 2, 3, 4, 5, 6]
a0,a = a,a[2:]
a[0] == 2 # True
a[4] == 6 # True
a0[0] == 0 # True
a0[4] == 4 # True
如果你真的需要一个具有读写能力的原始数组视图,那么我建议使用 numpy 数组:
import numpy as np
a = np.array([0, 1, 2, 3, 4, 5, 6])
b = a[2:].view()
b[0] == 2 # True
b[4] == 4 # True
b[1] = 99
print(a) # [ 0 1 2 99 4 5 6]
a[3] == 99 # True
如果你想自己实现类似于 numpy 的东西,你可以创建一个 class 代表列表上的一个“视图”,内部切片 属性(开始、停止、步骤) :
class ListView:
def __init__(self,aList,start=None,stop=None,step=1):
self.data = aList
self.slice = slice(start,stop,step)
@property
def indices(self): return range(len(self.data))[self.slice]
def offset(self,index=None):
if not isinstance(index,slice): return self.indices[index]
first = self.indices[index][0]
last = self.indices[index][-1]
step = (index.step or 1)*(self.slice.step or 1)
return slice(first,last+1-2*(step<0),step)
def __len__(self): return len(self.indices)
def __getitem__(self,index): return self.data[self.offset(index)]
def __repr__(self): return self[:].__repr__()
def __iter__(self): return self[:].__iter__()
def __setitem__(self,index,value): self.data[self.offset(index)] = value
def __delitem__(self,index): del self.data[self.offset(index)]
用法:
a = list(range(1,21))
v = ListView(a,3,-2,2)
len(v) # 8
print(a)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
print(v)
# [4, 6, 8, 10, 12, 14, 16, 18]
v[2] += 80
print(a)
# [1, 2, 3, 4, 5, 6, 7, 88, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
v.slice = slice(-4,None,-3)
print(v)
# [17, 14, 11, 88, 5, 2]
没有实现此目的的内置方法。但是,您可以通过扩展触发 list
to get this behaviour. When you do my_list[n]
, internally __getitem__()
函数来创建自定义 class。您可以通过将 offset
添加到索引来将此函数覆盖为 return 值。
同理,list contains other magic functions which you can override to further modify the behaviour of your custom class. For example, __setitem__()
is triggered when you assign any value to list, __delitem__()
删除item时触发
这是创建 OffsetList
class 的示例代码,它在创建 list
时将附加参数作为 offset
,并对 [=40= 执行基于索引的操作]index
+offset
值.
class OffsetList(list):
def __init__(self, offset, *args, **kwargs):
super(OffsetList, self).__init__(*args, **kwargs)
self.offset = offset
def _get_offset_index(self, key):
if isinstance(key, slice):
key = slice(
None if key.start is None else key.start + self.offset,
None if key.stop is None else key.stop + self.offset,
key.step
)
elif isinstance(key, int):
key += self.offset
return key
def __getitem__(self, key):
key = self._get_offset_index(key)
return super(OffsetList, self).__getitem__(key)
def __setitem__(self, key, value):
key = self._get_offset_index(key)
return super(OffsetList, self).__setitem__(key, value)
def __delitem__(self, key):
key = self._get_offset_index(key)
return super(OffsetList, self).__delitem__(key)
样本运行:
# With offset as `0`, behaves as normal list
>>> offset_list = OffsetList(0, [10,20,30,40,50,60])
>>> offset_list[0]
10
# With offset as `1`, returns index+1
>>> offset_list = OffsetList(1, [10,20,30,40,50,60])
>>> offset_list[0]
20
# With offset as `2`, returns index+2
>>> offset_list = OffsetList(2, [10,20,30,40,50,60])
>>> offset_list[0]
30
# Slicing support, with `start` as start+offset and `end` as end+offset
>>> offset_list[1:]
[40, 50, 60]
# Assigning new value, based on index+offset
>>> offset_list[0] = 123
>>> offset_list
[10, 20, 123, 40, 50, 60]
# Deleting value based on index+offset
>>> del offset_list[0]
>>> offset_list
[10, 20, 40, 50, 60]
同样,您可以根据需要修改 __len__()
, __iter__()
, __repr__()
, __str__()
等其他魔法函数的行为。