How do I extend, mimic, or emulate the range function?

后端 未结 2 1663
天涯浪人
天涯浪人 2020-12-28 15:27

I made a little generator function for character ranges:

>>> def crange(start, end):
...     for i in range(ord(start), ord(end)+1):
...                     


        
相关标签:
2条回答
  • 2020-12-28 16:12

    To add to Martin Konecny's answer. You probably want to use an internal range for everything and convert between chr and 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])
    

    In other words: if we can't subclass it we can use object composition.

    0 讨论(0)
  • 2020-12-28 16:13

    At that point, my first thought was to simply subclass range.

    range was a function in Python2 and a "final" class in Python3 (more info here) - in both cases not something you can sub-class. You will need to create a class crange that extends from an object as the base type.

    class crange(object):
    

    And this works, but is O(n), unlike range's O(1)

    In Python 3, there is a __contains__ method that you will define for your object.

    For objects that don’t define __contains__(), the membership test first tries iteration via __iter__(), then the old sequence iteration protocol via __getitem__(), see this section in the language reference.

    This allows Python to determine if the value is in your range without actually enumerating the range.

    For a simple example, if your range is 1 to 1,000,000, it is trivial to determine whether 23546 is in that range (1 < 23546 < 1000000). Of course the actual implementation is a bit more complex and adds ability to handle step increments etc.

    Regarding:

    Yay! But this doesn't work: >>> crange('a','e')[::2]

    In this case you need to define __getitem__ on your object. Here's an example of some of the methods required:

    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)
    
    0 讨论(0)
提交回复
热议问题