Why is “1000000000000000 in range(1000000000000001)” so fast in Python 3?

前端 未结 11 1400
梦毁少年i
梦毁少年i 2020-11-22 03:46

It is my understanding that the range() function, which is actually an object type in Python 3, generates its contents on the fly, similar to a generator.

11条回答
  •  悲&欢浪女
    2020-11-22 04:24

    To add to Martijn’s answer, this is the relevant part of the source (in C, as the range object is written in native code):

    static int
    range_contains(rangeobject *r, PyObject *ob)
    {
        if (PyLong_CheckExact(ob) || PyBool_Check(ob))
            return range_contains_long(r, ob);
    
        return (int)_PySequence_IterSearch((PyObject*)r, ob,
                                           PY_ITERSEARCH_CONTAINS);
    }
    

    So for PyLong objects (which is int in Python 3), it will use the range_contains_long function to determine the result. And that function essentially checks if ob is in the specified range (although it looks a bit more complex in C).

    If it’s not an int object, it falls back to iterating until it finds the value (or not).

    The whole logic could be translated to pseudo-Python like this:

    def range_contains (rangeObj, obj):
        if isinstance(obj, int):
            return range_contains_long(rangeObj, obj)
    
        # default logic by iterating
        return any(obj == x for x in rangeObj)
    
    def range_contains_long (r, num):
        if r.step > 0:
            # positive step: r.start <= num < r.stop
            cmp2 = r.start <= num
            cmp3 = num < r.stop
        else:
            # negative step: r.start >= num > r.stop
            cmp2 = num <= r.start
            cmp3 = r.stop < num
    
        # outside of the range boundaries
        if not cmp2 or not cmp3:
            return False
    
        # num must be on a valid step inside the boundaries
        return (num - r.start) % r.step == 0
    

提交回复
热议问题