Python timeit命令行错误:"语法错误:EOL扫描字符串文字"

时间:2014-06-02 04:07:10

标签: python windows command-line cmd timeit

我一直在使用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命令提示符,而不是。

那么,为什么这种奇怪的行为会完全发生,我该如何解决?

1 个答案:

答案 0 :(得分:11)

TL;博士

对传递给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可以嵌入到参数中。请注意,插入符号(^)不是   被识别为转义字符或分隔符。
  •   

错误#2

考虑到上述情况,让我们首先解释第二个奇怪的行为(作为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语句的度量。


错误#1

现在出现第一个错误:
正如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工作,这样我就可以简单地粘贴时间码片段。如果有人知道一种快速而肮脏的方式(如果可能的话),为了完成答案,这将是非常棒的。)