RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 567735
Accepted
pynista
pynista
Asked:2020-09-19 03:47:44 +0000 UTC2020-09-19 03:47:44 +0000 UTC 2020-09-19 03:47:44 +0000 UTC

为什么一种检查列表中数字的方法比第二种方法慢?

  • 772
导入时间

lst = [-3, 3, 7, 0, -10, 23, -9, -8, -5, -10, 9, 3,
 -2, 8, -3, 6, -1, 0, 10, -1, -6, -6, 10, -7, 3, 8,
 0, 7, 1, 5, -3, -6, 4, 6, -6, -4, -3, 10, 10, -5,
 -7, 0, -4, -8, 2, 9, 0, -10, -3, 3, -4, 9, -7, -8,
 0, -1, 1, 7, 2, -1, 3, 0, 9, -9, 4, 7, 6, 10, 8,
 -6, 3, 1, 1, 9, -8, -8, 2, 4, 10, 1, 5, -1, -1, 5,
 -9, 9, -3, 3, 0, -6, 2, 5, 10, 10, 5, -6, -10, -2, -9, '字符串']


start1 = time.clock()
a = all(isinstance(item, (int, float, complex)) for item in lst)
finish1 = 时间.时钟()
print("结果: {}, 时间:{:.2}ms".format(a, (finish1 - start1) * 1000))

start2 = time.clock()
def is_numbers(可迭代):
    对于可迭代的项目:
        如果不是 isinstance(item, (int, float, complex)):
            返回假
    返回真
b = is_numbers(lst)
finish2 = 时间.时钟()
print("结果: {}, 时间:{:.2}ms".format(b, (finish2 - start2) * 1000))

结果:

>>>
====== 重启:/home/dzmitry/adasdsadasdasdsad.py ======
结果:假,时间:0.081ms
结果:假,时间:0.042ms
>>>
====== 重启:/home/dzmitry/adasdsadasdasdsad.py ======
结果:假,时间:0.085ms
结果:假,时间:0.043ms
>>>

all() 函数等效于:

全部定义(可迭代):
    对于可迭代的元素:
        如果不是元素:
            返回假
    返回真

事实证明,第一种和第二种方法是等效的,除了使用生成器表达式。为什么第一种方法慢 1.5-2 倍?

python
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    jfs
    2020-09-20T11:38:49Z2020-09-20T11:38:49Z

    这与类型检查无关——即使您将isinstance(item, ..)调用替换为 ,时间差异仍然存在f(item),其中f该函数什么都不做,只是简单地返回True值:(f = lambda item: True以便它扫描all()整个lst列表而不会提前退出)。此外,如果完全删除函数调用,差异仍然存在:

    In [1]: %timeit all(True for _ in range(1000))
    10000 loops, best of 3: 69.7 µs per loop
    
    In [2]: def loop():
       ...:     for _ in range(1000):
       ...:         if not True:
       ...:             return False
       ...:     return True
       ...: 
    
    In [3]: %timeit loop()
    10000 loops, best of 3: 30.3 µs per loop
    

    显式 for 循环比 CPython 中的生成器更有效(结果也可能取决于平台)。

    all_(),手动实现,比all() 的内置版本稍慢,后者在 C 中实现了类似的循环:

    def all_(iterable):
        for item in iterable:
            if not item:
                return False
        return True
    

    也就是说,重要的是使用生成器,而不是如何all()实现它:

    In [4]: %timeit all_(range(1, 1000))
    10000 loops, best of 3: 41.1 µs per loop
    In [5]: %timeit all_(i  for i in range(1, 1000))
    10000 loops, best of 3: 93.9 µs per loop
    

    即使是列表生成器(listcomp 是一个列表理解)也可以比等效的 genexpr 更有效(尽管在 Jython 中恰恰相反):

    In [6]: %timeit [True for _ in range(1000)]
    10000 loops, best of 3: 44.4 µs per loop
    
    In [7]: %timeit list(True for _ in range(1000))
    10000 loops, best of 3: 83.2 µs per loop
    

    虽然在 Python 3 中这些表达式之间应该没有太大区别:Why is this list comprehension is faster than equivalent generator expression?

    字节码没有显示出任何特别可疑的东西:

    >>> import dis
    >>> dis.dis(lambda: [True for _ in range(1000000)])
      1           0 LOAD_CONST               1 (<code object <listcomp> at 0x610efc445eea, file "<stdin>", line 1>)
                  3 LOAD_CONST               2 ('<lambda>.<locals>.<listcomp>')
                  6 MAKE_FUNCTION            0
                  9 LOAD_GLOBAL              0 (range)
                 12 LOAD_CONST               3 (1000000)
                 15 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
                 18 GET_ITER
                 19 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
                 22 RETURN_VALUE
    >>> dis.dis(lambda: list(True for _ in range(1000000)))
      1           0 LOAD_GLOBAL              0 (list)
                  3 LOAD_CONST               1 (<code object <genexpr> at 0x610efc4bc0b2, file "<stdin>", line 1>)
                  6 LOAD_CONST               2 ('<lambda>.<locals>.<genexpr>')
                  9 MAKE_FUNCTION            0
                 12 LOAD_GLOBAL              1 (range)
                 15 LOAD_CONST               3 (1000000)
                 18 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
                 21 GET_ITER
                 22 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
                 25 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
                 28 RETURN_VALUE
    

    第一个片段:从 listcomp 对象创建一个函数并用结果调用它iter(range(1000000))。第二个片段:从 genexpr 创建一个函数并使用相同的参数调用它,另外list()用结果调用该函数。

    listcomp 非常简单:

    >>> code = compile('[True for _ in range(1000000)]', '<string>', 'eval')
    >>> code.co_consts[0]
    <code object <listcomp> at 0x610efc445f2c, file "<string>", line 1>
    >>> dis.dis(code.co_consts[0])
      1           0 BUILD_LIST               0
                  3 LOAD_FAST                0 (.0)
            >>    6 FOR_ITER                12 (to 21)
                  9 STORE_FAST               1 (_)
                 12 LOAD_CONST               0 (True)
                 15 LIST_APPEND              2
                 18 JUMP_ABSOLUTE            6
            >>   21 RETURN_VALUE
    

    靠近什么:

    def listcomp(it):
        L = []
        for _ in it:
            L.append(True)
        return L
    

    其中it = iter(range(1000000)), 较早获得。

    genexpr 看起来像:

    >>> code = compile('list(True for _ in range(1000000))', '<string>', 'eval')
    >>> code.co_consts[0]
    <code object <genexpr> at 0x610efc44145d, file "<string>", line 1>
    >>> dis.dis(code.co_consts[0])
      1           0 LOAD_FAST                0 (.0)
            >>    3 FOR_ITER                11 (to 17)
                  6 STORE_FAST               1 (_)
                  9 LOAD_CONST               0 (True)
                 12 YIELD_VALUE
                 13 POP_TOP
                 14 JUMP_ABSOLUTE            3
            >>   17 LOAD_CONST               1 (None)
                 20 RETURN_VALUE
    

    靠近什么:

    def genexpr(it):
        for _ in it:
            yield True
    

    将创建的生成器(对象)传递给内置list()函数,该函数通过调用 实现listextend(),执行与上述代码接近的代码listcomp(it)。同样,性能上的差异不在于使用list(),而在于将生成器传递给它(list(range(1000))并且也list(iter(range(1000))可以更快)。list(i for i in range(1000))[True for _ in range(1000)]

    这不再令人惊讶,即使list([True for _ in range(1000)])看起来做额外的工作也可以更快list(True for _ in range(1000))(CPython/Pypy 2/3,Ubuntu)。

    不要对微优化太着迷——编写最简单、最易理解的代码来解决手头的问题。在测量结果表明在您的特定情况下有必要更改代码之前,请勿更改代码。

    • 7
  2. vadim vaduxa
    2020-09-19T15:46:32Z2020-09-19T15:46:32Z

    您可以使用profile查看它。

    可以看到,时间除了isinstance

    98902 0.125 0.000 0.125 0.000 :0(isinstance)

    转到 genexpr te 生成器

    98903 0.406 0.000 0.531 0.000 C:/Scripts/python/2016/4/123.py:21(<genexpr>)

    # -*- coding: UTF-8 -*-
    import timeit
    import profile, pstats
    p = 'profile.txt'
    
    def execTime(target_: list, repeat=1):
        target_[:] = [(fn.__name__, timeit.Timer(fn).timeit(repeat)) for fn in target_]
        for e, (n, tmt) in enumerate(sorted(target_, key=lambda r: r[1]), start=1):
            print("{}'time {} {}".format(e, n, tmt))
    
    lst = [-3, 3, 7, 0, -10, 23, -9, -8, -5, -10, 9, 3,
     -2, 8, -3, 6, -1, 0, 10, -1, -6, -6, 10, -7, 3, 8,
     0, 7, 1, 5, -3, -6, 4, 6, -6, -4, -3, 10, 10, -5,
     -7, 0, -4, -8, 2, 9, 0, -10, -3, 3, -4, 9, -7, -8,
     0, -1, 1, 7, 2, -1, 3, 0, 9, -9, 4, 7, 6, 10, 8,
     -6, 3, 1, 1, 9, -8, -8, 2, 4, 10, 1, 5, -1, -1, 5,
     -9, 9, -3, 3, 0, -6, 2, 5, 10, 10, 5, -6, -10,  -2, -9]*999 + ['СТРОКА']
    
    
    def test1():
        return all(isinstance(item, (int, float, complex)) for item in lst)
    
    def test2():
        for a in lst:
            if not isinstance(a, (int, float, complex)): return
    
    if __name__ == '__main__':
        print('-'*20, 1)
        profile.run('test1()', p)
        print(pstats.Stats(p).sort_stats('cumtime').print_stats())
    
        print('-'*20, 2)
        profile.run('test2()', p)
        print(pstats.Stats(p).sort_stats('cumtime').print_stats())
    
        print('-'*20, 3)
        execTime([test1, test2], 100)
    

    出去:

    -------------------- 1
    Mon Sep 19 10:45:57 2016    profile.txt
    
             197811 function calls in 0.672 seconds
    
       Ordered by: cumulative time
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000    0.672    0.672 :0(exec)
            1    0.141    0.141    0.672    0.672 :0(all)
            1    0.000    0.000    0.672    0.672 C:/Scripts/python/2016/4/123.py:20(test1)
            1    0.000    0.000    0.672    0.672 <string>:1(<module>)
            1    0.000    0.000    0.672    0.672 profile:0(test1())
        98903    0.406    0.000    0.531    0.000 C:/Scripts/python/2016/4/123.py:21(<genexpr>)
        98902    0.125    0.000    0.125    0.000 :0(isinstance)
            1    0.000    0.000    0.000    0.000 :0(setprofile)
            0    0.000             0.000          profile:0(profiler)
    
    
    <pstats.Stats object at 0x025A9410>
    -------------------- 2
    Mon Sep 19 10:45:59 2016    profile.txt
    
             98907 function calls in 0.203 seconds
    
       Ordered by: cumulative time
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000    0.203    0.203 :0(exec)
            1    0.000    0.000    0.203    0.203 <string>:1(<module>)
            1    0.094    0.094    0.203    0.203 C:/Scripts/python/2016/4/123.py:23(test2)
            1    0.000    0.000    0.203    0.203 profile:0(test2())
        98902    0.109    0.000    0.109    0.000 :0(isinstance)
            1    0.000    0.000    0.000    0.000 :0(setprofile)
            0    0.000             0.000          profile:0(profiler)
    
    
    <pstats.Stats object at 0x025ACFB0>
    -------------------- 3
    1'time test2 3.4469346536397873
    2'time test1 4.2089339634381275
    
    • 3

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5