Example #1
0
 def f0():
     x=_testnames_g1 + a 
     x="string x"
     y="string y"
     def f1():
         i= _testnames_g1 + a + x
         i="string i"
         j='string j'
     
     from gpylib.misc import printfuncodeinfo #printfuncodeinfo也会存在于 co_names,没空深究
     printfuncodeinfo(colorama,testnames)
     assert set(['_testnames_g1', 'testdummy1', 'testdummy2']) == set(testnames.func_code.co_names)
     assert set(['b', 'C', 'D', 'f0bak', 'Dbak']) == set(testnames.func_code.co_varnames)
     assert len(testnames.func_code.co_freevars)==0
     assert set(['a', 'f0']) == set(testnames.func_code.co_cellvars)
     
     printfuncodeinfo(colorama,f0)#这里打印的也只是父block中的f0名字而已。
     assert set(['y', 'f1', 'printfuncodeinfo', 'z']) == set(f0.func_code.co_varnames)
     assert set(['a', 'f0']) == set(f0.func_code.co_freevars)
     assert set(['x']) == set(f0.func_code.co_cellvars)
     
     printfuncodeinfo(colorama,f1)
     assert set(['i', 'j']) == set(f1.func_code.co_varnames)
     assert set(['a', 'x']) == set(f1.func_code.co_freevars)
     assert len(f1.func_code.co_cellvars)==0
     
     z=re.compile("abcd")
Example #2
0
def testdynamicfunc7():
    '''
    原理:Python没有局部对象的概念,只有局部name的概念。所有对象都是全局的。
    原理1:一个函数F如果用了局部名称,无论局部变量是在哪一级函数中定义的,则函数在调用的时候,
    会使用最新的这个局部名称对应的对象。也就是说,回去locals()、globals()里面去找这个对象。
    这个locals()是函数定义的地方的locals()和globals()
    推理1:如果定义这些局部名称的父函数已经返回,而外部还引用了F,则如果外部调用F,则F中的局部
    名称对应的对象是父函数返回的时刻的那个对象。
    引用了一个已经结束调用的函数f里面定义的函数f1,而f1还使用了f0的局部变量,看起来很有陷阱,但是
    python却保证这个f0指向的函数对象能够被调用。


    初始理解:函数引用的name,在该block运行过程中变化,该name总能实时反应
    在执行的时候,才按照命名空间去找该name所引用的对象。
    python的object,无论怎么创建都没有区别。程序中只是和name打交道。
    name只是指向哪个object。但是程序中的代码只指向name。每次用这个
    name的时候,才去找这个name所对应的object。这个和C完全相反。
    
    而通过观察f.func_closure[0].cell_contents,能够看到,只要freevariable
    被赋值,cell_contents就会立刻更新。而不是调用的时候才更新。
    
    继续看cpython的实现中 PyCodeObject的代码,可以看到python把函数block中定义的
    被子孙block使用的变量单独放置一个List里面。猜测这样的话,这些变量被赋值的时候
    python回去修改这些子孙的func_closure里面的值。
    
    python中的name是静态的,是代码写好后就确定的了。每个名字在什么地方定义,是在
    编译的时候就确定的了。推理而言,每使用一个名字,在执行的时候去哪个BLOCK去找那个
    被绑定的Object,也是静态的。
    
    看二进制代码,对于这些name,在binding的时候使用了:STORE_DEREF
    Stores TOS into the cell contained in slot i of the cell and free variable storage.
    
    '''
    def checkfuncclosure(f,n,i):
        assert f.__code__.co_freevars[0] == n
        assert f.func_closure[0].cell_contents is i
        
    def ff(i):
        def f0():
            return b
        
        try:       
            checkfuncclosure(f0,'b',None)#空
        except ValueError as e:
            print GREEN+str(e)
        
        for x in (11,22):
            b=[x*i]
            checkfuncclosure(f0,'b',b) 
            #f.func_closure[0].cell_contents立刻变化了,所以函数无论何时被调用,
            #都会使用最新的free variable对象
            yield b,f0 
            #后面的多次Yield,f0是不变化的,b变化了
        
        for x in (33,44,55):
            b=[x*i]
            checkfuncclosure(f0,'b',b) 
            yield b,None

        
    print "\n[Position 1]"
    bf=[]
    for b,f in ff(1):
        bf.append([b, f])
        checkfuncclosure(bf[0][1],'b',b)
        #只检查第一个返回的函数对象即可
        
    assert bf[0][1] is bf[1][1] #f
    assert bf[0][1]() is bf[-1][0] is not bf[0][0] #b 
    
        
    '''第二部分,证明了不同的调用其返回的函数真正执行的时候,其环境已经不同了
    第二次环境里面的局部变量,已经不会对第一次的函数结果造成影响。
    说明,如果局部函数引用的局部变量具有镜像功能。
    '''
    print "\n[Position 3]"
    bf2=[]
    for b,f in ff(1):
        bf2.append([b, f])
        checkfuncclosure(bf2[0][1],'b',b)
        #只检查第一个返回的函数对象即可
        
    assert bf2[0][1] is bf2[1][1] #f
    assert bf2[0][1]() is bf2[-1][0] is not bf2[0][0] #b 

    print "\n[Position 5]"
    assert bf[0][1] is not bf2[0][1]() #f
    
    bf[-1][0][0]="me"
    
    assert bf[0][1]()[0] is bf[1][1]()[0] is bf[-1][0][0] is not bf2[0][1]()[0]
    
    print "\n[Position 6"
    bf2[-1][0][0]="you"
    assert bf2[0][1]()[0] is bf2[1][1]()[0] is bf2[-1][0][0] is not bf[0][1]()[0]
    
    global _ghc_f #用于在命令行调试
    _ghc_f=bf[0][1]
    
    from gpylib.misc import printfuncodeinfo
    printfuncodeinfo(colorama, _ghc_f)
    printfuncodeinfo(colorama, testdynamicfunc7)
    printfuncodeinfo(colorama, ff)