List slicing with dynamic index on [:index]

前端 未结 4 1152
慢半拍i
慢半拍i 2020-12-06 17:34

I need to slice a list using negative dynamic indexes ([:-index]). This was easy until I realized that if the value of my dynamic index was 0, no items were returned, instea

相关标签:
4条回答
  • 2020-12-06 18:15

    You can use None rather than 0 to get the full slice:

    >>> arr = [1, 2, 3]
    >>> index = 1
    >>> arr[:-index if index else None]
    [1, 2]
    >>> index = 0
    >>> arr[:-index if index else None]
    [1, 2, 3]
    

    My testing:

    import timeit
    
    def jonrsharpe(seq, index):
        return seq[:-index if index else None]
    
    def Cyber(seq, index):
        return seq[:len(arr) - index]
    
    def shashank(seq, index):
        return seq[:-index or None]
    
    if __name__ == '__main__':
        funcs = ('jonrsharpe', 'Cyber', 'shashank')
        arr = range(1000)
        setup = 'from __main__ import arr, {}'.format(', '.join(funcs))
        for func in funcs:
            print func
            for x in (0, 10, 100, 1000):
                print x,
                print timeit.timeit('{}(arr, {})'.format(func, x), setup=setup)
    

    and results:

    jonrsharpe
    0 2.9769377505
    10 3.10071766781
    100 2.83629358793
    1000 0.252808797871
    Cyber
    0 3.11828875501
    10 3.10177615276
    100 2.82515282642
    1000 0.283648679403
    shashank
    0 2.99515364824
    10 3.11204965989
    100 2.85491723351
    1000 0.201558213116
    
    0 讨论(0)
  • 2020-12-06 18:16

    It kind of takes away from the cleanness of the slice notation, but you could do

    >>> arr[: len(arr) - 2]
    'test te'
    >>> arr[: len(arr) - 1]
    'test tex'
    >>> arr[: len(arr) - 0]
    'test text'
    
    0 讨论(0)
  • 2020-12-06 18:22

    Another potential solution for fun.

    >>> arr = [1, 2, 3]
    >>> index = 0
    >>> arr[:-index or None]
    [1, 2, 3]
    >>> index = 1
    >>> arr[:-index or None]
    [1, 2]
    

    For higher performance on immutable sequence types like strings, you can avoid slicing the sequence entirely in the case that index is 0 by checking the value of index before the slice operation.

    Here's three functions to test in terms of performance:

    def shashank1(seq, index):
        return seq[:-index or None]
    
    def shashank2(seq, index):
        return index and seq[:-index] or seq
    
    def shashank3(seq, index):
        return seq[:-index] if index else seq
    

    The latter two should be much faster in the case where index is 0, but may be slower (or faster) in other cases.


    Updated benchmark code: http://repl.it/oA5

    Note: The results depend quite a bit on the Python implementation.

    0 讨论(0)
  • 2020-12-06 18:30

    Since I wouldn't be able to sleep until I chose the best right answer, I tested the performance of each answer using two different scripts in addition to the one provided by @jonrsharpe.

    This is the code I used to compare performance between the three different solutions using profile:

    import profile
    
    arr='test 123456789014'
    
    def jonrsharpe(index):
        global arr
        for c in range(1,100000,1):
            a=arr[:-index if index else None]
    
    def Cyber(index):
        global arr
        for c in range(1,100000,1):
            a=arr[:len(arr)-index]
    
    def shashank(index):
        global arr
        for c in range(1,100000,1):
            a=arr[:-index or None]
    
    def testf():
        for index in (0,3,6,9):
            jonrsharpe(index)
            Cyber(index)
            shashank(index)
    
    if __name__ == '__main__':
        profile.run("testf()")
    

    Here is the output:

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    799992    1.629    0.000    1.629    0.000 :0(len)
       12    0.021    0.002    0.021    0.002 :0(range)
        1    0.006    0.006    0.006    0.006 :0(setprofile)
        1    0.000    0.000    4.390    4.390 <string>:1(<module>)
        0    0.000             0.000          profile:0(profiler)
        1    0.000    0.000    4.396    4.396 profile:0(testf())
        4    2.114    0.529    3.750    0.937 test.py:12(Cyber)
        4    0.307    0.077    0.313    0.078 test.py:19(shashank)
        1    0.000    0.000    4.390    4.390 test.py:26(testf)
        4    0.319    0.080    0.328    0.082 test.py:5(jonrsharpe)
    

    Another method:

    import time
    
    if __name__ == '__main__':
        arr = '01234567890123456789012345678901234567890123456789'#range(1000)
        for x in (0, 10, 20, 30,40,49):
            print 'index=',x
            start=time.clock()
            for count in range(1000000):
                a=arr[:-x if x else None]
    
            print 'jonrsharpe=',round(time.clock()-start,4)
    
            start=time.clock()
            for count in range(1000000):
                a=arr[:len(arr)-x]
            print 'Cyber     =',round(time.clock()-start,4)
    
            start=time.clock()
            for count in range(1000000):
                a=arr[:-x or None]
            print 'shashank  =',round(time.clock()-start,4)
    

    Output:

    index= 0
    jonrsharpe= 0.4918
    Cyber     = 0.5341
    shashank  = 0.4269
    index= 10
    jonrsharpe= 0.4617
    Cyber     = 0.5334
    shashank  = 0.4105
    index= 20
    jonrsharpe= 0.4271
    Cyber     = 0.4562
    shashank  = 0.3493
    index= 30
    jonrsharpe= 0.4217
    Cyber     = 0.4548
    shashank  = 0.3264
    index= 40
    jonrsharpe= 0.4713
    Cyber     = 0.8488
    shashank  = 0.6458
    index= 49
    jonrsharpe= 0.6159
    Cyber     = 0.5663
    shashank  = 0.4312
    

    Since I will be using this line of code a gazillion times, performance is very important, and @Shashank's solution was the winner in most cases, even if it was just by a little.

    0 讨论(0)
提交回复
热议问题