我为字符范围做了一个小生成器函数:
>>> def crange(start, end):
... for i in range(ord(start), ord(end)+1):
... yield chr(i)
...
Run Code Online (Sandbox Code Playgroud)
然后我可以这样做:
>>> print(*crange('a','e'))
a b c d e
Run Code Online (Sandbox Code Playgroud)
好极了!但这不起作用:
>>> crange('a','e')[::2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'generator' object is not subscriptable
Run Code Online (Sandbox Code Playgroud)
这是有效的,但是O(n),不像range's(1):
>>> 'y' in crange('a','z')
True
Run Code Online (Sandbox Code Playgroud)
这意味着在最大值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
Run Code Online (Sandbox Code Playgroud)
所以我想我必须以某种方式模仿它,允许我将字符作为参数传递,像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
Run Code Online (Sandbox Code Playgroud)
这让我看到了那里的功能,但我不确定他们在做什么,或者如何range使用它们.我找了源代码range,但它在C中,我不知道在哪里可以找到它的Python包装器(它确实有一个,对吧?).
我从哪里开始,我是否应该去那里?
Mar*_*cny 17
那时,我的第一个想法是简单地将范围子类化.
range是Python2中的一个函数,Python3中的"最终"类(这里有更多信息) - 在这两种情况下都不是你可以子类的东西.您将需要创建一个crange从object作为基本类型扩展的类.
class crange(object):
Run Code Online (Sandbox Code Playgroud)
这是有效的,但是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)
Run Code Online (Sandbox Code Playgroud)
添加到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])
Run Code Online (Sandbox Code Playgroud)
换句话说:如果我们不能将它子类化,我们可以使用对象组合.