2012-01-08 101 views
5

我认为,有一个类似的函数定义一个项目:locals()中不可访问的`.0`变量是否会影响内存或性能?

def f(a, (b1, b2), c): 
    print locals() 

在调试我发现,.1键出现在locals(),与价值(b1, b2)的代码。快速检查表明,一个函数定义如下所示:

def f((a1, a2)): 
    print locals() 

将在locals()与价值(a1, a2)一个.0关键。我对这种行为感到惊讶,但是在Python文档中找不到任何信息。

我的问题是:这些否则无法访问的位置变量影响内存或性能?他们记录在任何地方?他们的服务目的是什么?

有问题的项目是feedparser,这是基于SAX和可能有几十或几百将通过这种行为来影响函数调用。

+4

自动元组参数拆包的这种风格已经在Python 3中被明确地删除了,如果我当时记得正确的话被描述为语法事故。这当然不是我喜欢看的风格。 – 2012-01-08 08:19:52

+0

很高兴知道!你是对的,'2to3'工具会自动用'xxx_todo_changeme'替换'(b1,b2)'语法。不过,您是否有链接可以帮助解释这种行为,或者有关明确移除的链接? – 2012-01-08 08:42:13

+3

@Kurt McKee [PEP 3113 - 删除元组参数拆包](http://www.python.org/dev/peps/pep-3113/)。 – 2012-01-08 09:03:20

回答

5

所以pep 3113,如阿图尔·加斯帕指出,包含一个完整的答案。它还列出了一大堆理由,为什么这可能不是一个很好的模式。其中一个您发现在调试的恼人的副作用。更大的一个是我认为你的代码会中断转换到python3,但我不确定/仍然在2.7上。

我想玩什么。寻找一些拆解字节码,我们可以看到这三种功能会发生什么(扰流板:foo和bar具有相同的字节码):

from dis import dis 

def foo(a, (b, c) ,d): 
    return a + b + c + d 

def bar(a, b_c, d): 
    b, c = b_c 
    return a + b + c + d 

def baz(a, b, c, d): 
    return a + b + c + d 

print '\nfoo:' 
dis(foo) 
print '\nbar:' 
dis(bar) 
print '\nbaz:' 
dis(baz) 

产量:

foo: 
    3   0 LOAD_FAST    1 (.1) 
       3 UNPACK_SEQUENCE   2 
       6 STORE_FAST    3 (b) 
       9 STORE_FAST    4 (c) 

    4   12 LOAD_FAST    0 (a) 
      15 LOAD_FAST    3 (b) 
      18 BINARY_ADD   
      19 LOAD_FAST    4 (c) 
      22 BINARY_ADD   
      23 LOAD_FAST    2 (d) 
      26 BINARY_ADD   
      27 RETURN_VALUE   


bar: 
    7   0 LOAD_FAST    1 (b_c) 
       3 UNPACK_SEQUENCE   2 
       6 STORE_FAST    3 (b) 
       9 STORE_FAST    4 (c) 

    8   12 LOAD_FAST    0 (a) 
      15 LOAD_FAST    3 (b) 
      18 BINARY_ADD   
      19 LOAD_FAST    4 (c) 
      22 BINARY_ADD   
      23 LOAD_FAST    2 (d) 
      26 BINARY_ADD   
      27 RETURN_VALUE   


baz: 
11   0 LOAD_FAST    0 (a) 
       3 LOAD_FAST    1 (b) 
       6 BINARY_ADD   
       7 LOAD_FAST    2 (c) 
      10 BINARY_ADD   
      11 LOAD_FAST    3 (d) 
      14 BINARY_ADD   
      15 RETURN_VALUE   

正如你所看到的。 foobar是相同的,而baz跳过了拆箱。所以,是的,这会影响性能一点,但只要只能作为元组拆包发生,这应该是在一切都可以忽略不计,除了非常小的功能和玩具的例子(像这样的; P)

+0

我以前从未使用过'dis'模块,感谢您展示了这段代码的工作原理!不幸的是,无法从解开元组中解脱出来,因​​为所有的SAX解析器都使用传递元组的API,但是这个(和Artur提供的链接)是我正在寻找的那种分析。 – 2012-01-08 18:37:12

3

是的,它们会影响性能。

>>> import timeit 
>>> 
>>> 
>>> def function_1(a, (b1, b2), c): 
...  locals() 
... 
>>> def function_2(a, b1, b2, c): 
...  locals() 
... 
>>> 
>>> object_1 = object() 
>>> object_2 = object() 
>>> object_3 = object() 
>>> tuple_of_objects_2_and_3 = (object_2, object_3) 
>>> object_4 = object() 
>>> 
>>> n = 100000000 
>>> 
>>> time_1 = timeit.timeit(lambda: function_1(object_1, tuple_of_objects_2_and_3, 
...           object_4), 
...      number=n) 
>>> time_2 = timeit.timeit(lambda: function_2(object_1, object_2, object_3, 
...           object_4), 
...      number=n) 
>>> 
>>> print(time_1, time_2) 
(170.2440218925476, 151.92010402679443) 

关于他们的文档或目的,我不知道。

+2

该测试还包括元组打包以及元组解包。在第一个lambda中使用'tuple_23 =(object_2,object_3)'并引用它可能会更公平。 – Robin 2012-01-08 09:36:11

+0

@Robin你是对的,谢谢。我编辑了我的答案。 – 2012-01-08 11:09:59

+0

感谢您的时间测试,这有助于知道。 – 2012-01-08 18:15:14

相关问题