我为角色范围制作了一个小的生成器函数:
>>> def crange(start, end):
... for i in range(ord(start), ord(end)+1):
... yield chr(i)
...
然后我可以这样做:
>>> print(*crange('a','e'))
a b c d e
耶!但这不起作用:
>>> crange('a','e')[::2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'generator' object is not subscriptable
这是有效的,但是O(n),不像range
&#39; O(1):
>>> 'y' in crange('a','z')
True
这意味着从最大值110,000中搜索字符编号109,999大约需要0.35秒。 109999 in range(110000)
当然是快速的。
那时,我的第一个想法是简单地将范围子类化。不幸的是:
>>> class A(range):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type 'range' is not an acceptable base type
所以我想我必须以某种方式模仿它,允许我将字符作为参数传递,在内部像range
一样工作,并产生字符。不幸的是,我不确定如何继续。我尝试了dir()
:
>>> print(*dir(range), sep='\n')
__class__
__contains__
__delattr__
__dir__
__doc__
__eq__
__format__
__ge__
__getattribute__
__getitem__
__gt__
__hash__
__init__
__iter__
__le__
__len__
__lt__
__ne__
__new__
__reduce__
__reduce_ex__
__repr__
__reversed__
__setattr__
__sizeof__
__str__
__subclasshook__
count
index
start
step
stop
让我可以看到其中有哪些功能,但我不确定他们正在做什么,或range
如何使用它们。我查找了range
的来源,但它在C中,我不知道在哪里找到它的Python包装器(它确实有一个,对吧?)。
我从哪里出发,我是否应该去那里?
答案 0 :(得分:17)
那时,我的第一个想法是简单地将范围子类化。
range
是Python2中的一个函数,是#34; final&#34; Python3中的类(more info here) - 在这两种情况下都不是你可以子类的东西。您需要创建一个从crange
扩展为基类的类object
。
class crange(object):
这是有效的,但是O(n),与范围的O(1)
不同
在Python 3中,您将为对象定义__contains__
方法。
对于未定义
__contains__()
的对象,成员资格测试首先通过__iter__()
尝试迭代,然后通过__getitem__()
尝试旧的序列迭代协议,请参阅语言参考中的此部分。
这允许Python确定值是否在您的范围内而不实际枚举范围。
举一个简单的例子,如果您的范围是1到1,000,000,那么确定23546是否在该范围内(1 < 23546 < 1000000
)是微不足道的。当然,实际的实现有点复杂,并且增加了处理步长增量等的能力。
关于:
耶!但这不起作用:
>>> crange('a','e')[::2]
在这种情况下,您需要在对象上定义__getitem__
。以下是一些所需方法的示例:
class crange(object):
def __init__(self, start, end, step=1):
# initialize your range object
self.start = start
self.end = end
self.step = step
def __iter__(self):
# enable iteration over your object
# (assume step size is 1)
for i in range(ord(self.start), ord(self.end)+1):
yield chr(i)
def __getitem__(self, i):
# enable accessing items in your range by index
# also enable crange('a','e')[::2]
# (assuming step size of 1)
if isinstance( i, slice ):
# implement slicing
else:
return chr(ord(self.start) + i)
def __contains__(self, char):
# enable O(1) determination of whether a value is in your range
# (assume step size is 1)
return ord(self.start) <= ord(char) < ord(self.end)
def __len__(self):
# return length (assuming step size of 1)
return ord(self.end) - ord(self.start)
答案 1 :(得分:9)
添加到Martin Konecny的回答中。您可能希望对所有内容使用内部范围,并在chr和ord之间进行转换。
class crange:
def __init__(self, *args, **kwargs):
args = [ord(arg) for arg in args]
kwargs = {key: ord(val) for key, val in kwargs.items()}
self.range = range(*args, **kwargs)
def __iter__(self):
for n in self.range:
yield chr(n)
def __contains__(self, c):
return ord(c) in self.range
def __getitem__(self, i):
if isinstance(i, slice):
ret = crange('\x00')
ret.range = self.range[i]
return ret
else:
return chr(self.range[i])
def __repr__(self):
return "crange({}, {})".format(
repr(chr(self.range.start)), repr(chr(self.range.stop)))
r = crange('a', 'f')
print(list(r))
print('b' in r)
print('f' in r)
print(r[:2])
换句话说:如果我们不能将其子类化,我们可以使用object composition。