[奇用]嵌入式函式

沈崴 <[email protected]>
reply-to        [email protected]
to      python-cn`CPyUG`华蟒用户组 <[email protected]>
date    Wed, Oct 15, 2008 at 16:37
subject [CPyUG:68306] 新手提问, 为什么有中间变量和没有中

现象

   1 import sys
   2 def test():
   3        m = type(sys)('<string>')
   4        exec '''\
   5 a = 'hello world!'
   6 def print_a():
   7        print a''' in m.__dict__
   8        return m
   9 
  10 test().print_a() # -> None
  11 
  12 temp = test()
  13 temp.print_a() # -> hello world!

理解

ZoomQuiet
  • 这又是魔术之一,在函式中制造函式,没有运行函式前,名称空间中是没有那个 print_a() 的;这不是中间变量,根本就是 运行时 mix-in

字节码

onenew <[email protected]>
reply-to        [email protected]
to      [email protected]
date    Wed, Oct 15, 2008 at 17:37
subject [CPyUG:68319] Re: 新手提问, 为什么有中间变量和没有中间变量的结果是不一样的

>>> from dis import dis
>>> print dis(test()[1].print_a)
test()

 3           0 LOAD_NAME                0 (type)
             3 LOAD_NAME                1 (sys)
             6 CALL_FUNCTION            1
             9 LOAD_CONST               1 ('<string>')
            12 CALL_FUNCTION            1
            15 STORE_FAST               0 (m)

 6          18 LOAD_CONST               2 ("\na = 'hello world!';\ndef print_a():print a ")
            21 LOAD_FAST                0 (m)
            24 LOAD_ATTR                2 (__dict__)
            27 DUP_TOP            
            28 EXEC_STMT          

 7          29 LOAD_FAST                0 (m)
            32 RETURN_VALUE        

temp.print_a() & test().print_a()

 3           0 LOAD_GLOBAL              0 (a)
             3 PRINT_ITEM          
             4 PRINT_NEWLINE       
             5 LOAD_CONST               0 (None)
             8 RETURN_VALUE       

以上是两个函数对应的字节码 问题关键就在 LOAD_GLOBAL 的解释

猜想
  • 两种执行方式的GLOBAL 是不同的
  • temp = test() 这样temp成为一个模块对象 有了自己的名字空间 temp.print_a的GLOBAL 是这个空间
  • test().print_a() 本身是一个表达式python可能内部做了优化,在查找GLOBAL 的时候直接使用了表达式所在的名字空间

抛砖引玉了:)


-- 知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得。

继续

又测试了一下 看来不是对 global查找策略的优化(ps:如果是直接查找顶层名字空间会出异常的), 而是对初始化时机的优化.细节只有去抠源码了.

>>> import sys
>>> def test():
...     #print "calling test()"
...     m = type(sys)('<string>')
...     d = m.__dict__
...     exec '''\
>>> a = 'hello world!';
>>> def print_a():print a''' in d
...     #print d['a']# -> hello world!
...     #print m.a# -> hello world!
...     #print "test() end"
...     return d,m

----------------------------------------

>>> pprint (test()[0])

{'__builtins__': {},
 '__doc__': None,
 '__name__': None,
 'a': None,
 'print_a': None} #模块的字典尚未初始化
----------------------------------------

>>> pprint (test()[1].__dict__ == test()[0] ) #模块的字典尚未初始化

True

>>> print test()[1].__dict__['a'] # -> hello world!#模块的字典完成初始化

None

>>> print test()[1].a # -> hello world!#模块的字典完成初始化

hello world!
----------------------------------------

>>> d,m=test()
>>> pprint (d)

{'__builtins__': {},
 '__doc__': None,
 '__name__': '<string>', #
 'a': 'hello world!',
 'print_a': <function print_a at 0x01D2FF70>}#模块的字典完成初始化

>>> pprint (m.__dict__['a'])#模块的字典完成初始化

'hello world!'

>>> print m.a  # -> hello world!#模块的字典完成初始化

hello world!
----------------------------------------

}看起来, 模块创建的时候字典里只有键,值都是none (注意看 __name__) 被使用,或者被命名的时候才初始化字典的值. 以前倒是没注意过,python的手册有提过这种优化?

BUG

沈崴 <[email protected]>
reply-to        [email protected]
to      python-cn`CPyUG`华蟒用户组 <[email protected]>
date    Thu, Oct 16, 2008 at 08:50
subject [CPyUG:68360] Re: 新手提问, 为什么有中间变量和没有中间变量的结果是不一样的

On Oct 15, 8:16 pm, 杨晨醒 <[email protected]> wrote:

> 感觉还是没有解释heroboy的那段代码里的现象啊......
> 按照那段代码,f['a']已经被初始化了,但是返回出去之后却取不出['a']

cpython 的这种情况应该可以归入 bug 的范畴了。

另外使用 PyPy 来执行这个代码, 能得到正确的结果。


反馈

创建 by -- ZoomQuiet [2008-10-15 09:18:32]

MiscItems/2008-10-15 (last edited 2009-12-25 07:12:50 by localhost)