我一直在使用Python timeit模块,但只能通过交互式的Python会话或Unix shell。 现在,我试图在Windows命令提示符( cmd.exe )中测量一些代码片段,但是它显示了这个错误:
C:\Users\Me>python -m timeit '"-".join(map(str, range(100)))' Traceback (most recent call last): File "C:\Python33\lib\runpy.py", line 160, in _run_module_as_main "__main__", fname, loader, pkg_name) File "C:\Python33\lib\runpy.py", line 73, in _run_code exec(code, run_globals) File "C:\Python33\lib\timeit.py", line 334, in <module> sys.exit(main()) File "C:\Python33\lib\timeit.py", line 298, in main t = Timer(stmt, setup, timer) File "C:\Python33\lib\timeit.py", line 131, in __init__ code = compile(src, dummy_src_name, "exec") File "<timeit-src>", line 6 '-.join(map(str, ^ SyntaxError: EOL while scanning string literal
这是相当混乱的,因为我没有在string中插入任何换行符 – 而是直接从timeit模块文档中直接粘贴了这个示例。
在玩这个游戏的时候,我尝试了没有任何空格的testing代码片段,因为错误在它们之前标记了字符。 即使现在没有发生exception,模块会报告相同的执行时间,就像我已经传递了一个pass
语句一样,如下所示:
C:\Users\Me>python -m timeit 100000000 loops, best of 3: 0.013 usec per loop C:\Users\Me>python -m timeit 'map(str,range(100))' 100000000 loops, best of 3: 0.013 usec per loop C:\Users\Me>python -m timeit 'map(str,range(1000000000000000))' 100000000 loops, best of 3: 0.013 usec per loop
我确定我正确地调用了模块,因为我在Unix shell上粘贴了相同的行,并且按照预期工作。
因为我用Python 2.7和3.3获得了完全相同的结果(另外,模块是用纯Python编写的,而且已经存在很长时间了),我相信这与Python无关,但是Windows命令提示符,代替。
那么,为什么这种奇怪的行为发生了,我该如何解决呢?
对传递给timeit模块的语句使用双引号。
例:
C:\Users\Me>python -m timeit "'-'.join(map(str, range(100)))" 10 loops, best of 3: 28.9 usec per loop
与Unix shell(如bash和tcsh)相比 ,单引号在Windows命令行中的处理方式不同。
这里是一个很小的python程序来演示这个:
import sys print(sys.argv[1:])
运行这个(让我们调用文件cmdtest.py ),我们观察以下内容:
C:\Users\Me\Desktop>python cmdtest.py 1 2 3 ['1', '2', '3'] C:\Users\Me\Desktop>python cmdtest.py "1 2 3" ['1 2 3'] C:\Users\Me\Desktop>python cmdtest.py '1 2 3' ["'1", '2', "3'"]
所以,单引号是字面处理(即不作为特殊字符)。 在SO中搜索了一下,我发现了这个由cmd描述的参数标记化的很好的描述 :
从命令窗口调用命令时,命令行参数的标记化不是由
cmd.exe
(又名“shell”)完成的。 大多数情况下,标记是由新形成的进程的C / C ++运行时完成的,但这不一定是这样 – 例如,如果新进程不是用C / C ++编写的,或者新进程选择忽略argv
和处理原始命令行(例如[GetCommandLine()] [1])。 在操作系统级别,Windows将未被指定为单个字符串的命令行传递给新进程。 这与大多数* nix shell相反,shell在将参数传递给新形成的进程之前,以一致的,可预测的方式将参数标记为参数。 所有这一切意味着你可能会在Windows上的不同程序中经历非常不同的参数标记化行为,因为个别程序通常会将参数标记化转化为自己的手。如果听起来像无政府状态,那就是。 但是,由于大量的Windows程序确实使用Microsoft C / C ++运行库的
argv
,所以理解MSVCRT如何标记参数可能通常很有用。 这是一个摘录:
- 参数是由空格分隔的,空格是一个空格或一个制表符。
- 由双引号包围的字符串被解释为单个参数,而不管其中包含的空白。 带引号的字符串可以嵌入到参数中。 请注意,脱字符(^)不被识别为转义字符或分隔符。
考虑到上述情况,让我们首先解释第二个奇怪的行为(作为pass
语句的行为),因为它更简单一些。 由于单引号字面解释,当调用:
C:\Users\Me>python -m timeit 'map(str,range(100))'
确切的字符串文字'map(str,range(100))'
(带引号)作为语句传递给时间。
所以,Python会看到
"'map(str,range(100))'"
代替
'map(str,range(100))'
它作为一个字符串,并没有真正做任何事情,并给出一个非常接近pass
的测量。
现在为第一个错误:
正如它为python timeit模块所记录的:
可以通过将每行指定为单独的语句参数来给出多行语句;
所以,打电话时:
C:\Users\Me>python -m timeit '"-".join(map(str, range(100)))'
Python将["'-.join(map(str,", "range(100)))'"]
作为语句传递给timeit,模块将其解释为多行语句:
'"-".join(map(str, range(100)))'
这有一个字符串,用单引号打开,但从不关闭,因此,(最后)解释奇怪的EOL错误。
使用双引号的语句来解决问题。
我也尝试过Windows PowerShell ,它比cmd.exe更先进,并且表现出与Unix shell类似的行为,但是对于我所测试的所有语句都没有这么做。
例如,这是有效的(注意声明中的空间):
PS C:\Users\Me> python -m timeit 'map(str, range(100))' 1000000 loops, best of 3: 0.688 usec per loop
而最初的例子不是:
PS C:\Users\Me\Desktop> python -m timeit '"-".join(map(str, range(100)))' option -. not recognized use -h/--help for command line help
(我还没有真正满意,我宁愿做的就是让cmd或者PowerShell作为一个Unix shell来工作,这样我就可以简单的粘贴和计时代码片断了,如果有人知道这样做的快捷方式, (如果甚至可能的话),为了完成答案,这将是非常棒的。)