如何扩展,模仿或模拟范围功能?

Tig*_*kT3 22 python

我为字符范围做了一个小生成器函数:

>>> 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中的"最终"类(这里有更多信息) - 在这两种情况下都不是你可以子类的东西.您将需要创建一个crangeobject作为基本类型扩展的类.

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)


Ari*_*nom 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])
Run Code Online (Sandbox Code Playgroud)

换句话说:如果我们不能将它子类化,我们可以使用对象组合.