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")
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)