我一直在使用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
这相当令人困惑,因为我没有在字符串中插入任何换行符 - 相反,我实际上直接从timeit模块文档中粘贴了示例。
在玩这个时,我尝试测试没有任何空格的片段,因为错误标记了它们之前的字符。即使现在没有异常,模块也会报告相同的执行时间,就像我传递了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命令提示符,而不是。
那么,为什么这种奇怪的行为会完全发生,我该如何解决?
答案 0 :(得分:11)
对传递给timeit模块的语句使用双引号。
例如:
C:\Users\Me>python -m timeit "'-'.join(map(str, range(100)))"
10 loops, best of 3: 28.9 usec per loop
与诸如 bash 和 tcsh 之类的Unix shell相比,单引号在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中搜索了一下,我找到了this great description of argument tokenization by cmd:
从命令窗口调用命令时,标记化 命令行参数不是由
cmd.exe
(a.k.a。“shell”)完成的。 大多数情况下,标记化是由新形成的过程完成的 C / C ++运行时,但不一定如此 - 例如,如果 新进程不是用C / C ++编写的,或者是新进程选择的 忽略argv
并为自己处理原始命令行(例如,使用 [GetCommandLine()] [1])。在操作系统级别,Windows传递命令行 未加工为新进程的单个字符串。这是相反的 大多数* nix shell,其中shell标记了a中的参数 在将它们传递给新形成之前,一致,可预测的方式 处理。所有这些意味着您可能会遇到极端分歧 Windows上不同程序的参数标记化行为, 因为个别程序经常将参数标记化为自己的 手。如果它听起来像无政府状态,那就是。但是,既然很大 Windows程序做的数量使用Microsoft C / C ++运行时
argv
,了解MSVCRT的方法通常很有用 标记参数。这是一段摘录:
- 参数由空格分隔,可以是空格或制表符。
- 由双引号括起的字符串被解释为单个参数,而不管其中包含的空格。引用 string可以嵌入到参数中。请注意,插入符号(^)不是 被识别为转义字符或分隔符。
考虑到上述情况,让我们首先解释第二个奇怪的行为(作为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模块记录的那样:
通过将每一行指定为a,可以给出多行语句 单独的陈述参数;
所以,在致电:
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工作,这样我就可以简单地粘贴时间码片段。如果有人知道一种快速而肮脏的方式(如果可能的话),为了完成答案,这将是非常棒的。)