天天看點

python學習手冊17 作用域

點選(此處)折疊或打開

  1. #!/usr/bin/env python
  2. #-*- coding:utf8 -*-
  3. #python對變量的操作都是在命名空間中(作用域),變量名被指派的位置決定了這個變量名能被通路到的範圍。
  4. #變量指派的地方決定了命名空間,即語義作用域。
  5. #一個函數所有變量名都與函數的命名空間相關。
  6. '''
  7. def内定義的變量名隻能在def内使用。
  8. def内外的變量名不沖突。
  9. 變量對應的作用域: 作用于可以防止程式變量名沖突。
  10.     def内指派就是def内。
  11.     嵌套def,對于嵌套函數是非本地。
  12.     def外指派,是全局的。
  13. X=99#全局變量
  14. def func():
  15.     X=88 #函數本地變量
  16. #函數定義了本地作用哉,子產品定義了全局作用域。
  17. 内嵌的子產品是全局作用域。
  18. 全局作用域的範圍僅限單個檔案。(全局是相對一個子產品或檔案而言。)
  19. 每次對函數的調用都建立了一個新的本地作用域。
  20. 指派的變量名除非聲明為全局變量或本地變量,否則均為本地變量。
  21. 所有其它的變量名都可以歸納為本地、全局、或都内置的。
  22. 此外,原處改變對象并不會把變量劃分為本地變量,隻有對變量名指派才可以。即修改一個對象并不是對一個名稱指派。
  23. print('變量名解析:LEGB原則')
  24. 變量名按:本地(L)->上層函數(E)->全局(G)->内置(B)的順序查找
  25. 預設變量名指派會建立或改變本地變量。(指派總是建立或改變變量名,除非聲明過類型。)
  26. 全局聲明與非本地聲明将指派變量名映射到子產品内部的作用域。
  27. #全局變量
  28. X=99 #X與func在本子產品中是:全局變量
  29. def func(Y): #Y與Z在函數中是本地變量
  30.     #本地變量
  31.     Z=X+Y #X是全局變量
  32.     return Z
  33. print(func(1))
  34. #内置作用域 __builtin__ 内置子產品
  35. #import __builtin__
  36. #print(dir(__builtin__))
  37. #global語句
  38. 全局變量是位于子產品檔案内部頂層的變量
  39. 全局變量在函數内必須經過聲明
  40. 全局變量在函數内不用聲明可以引用
  41. X=88 #全局變量
  42.     global X #全局變量聲明
  43.     X=99
  44.     return X
  45. print(func())
  46. print(X)
  47. a,b=1,2
  48. def all_global():
  49.     global x
  50.     x=a+b
  51.     print(x)
  52. all_global()
  53. print(x)
  54. #最小化全局變量
  55. #原因是:流程控制比較難,儲存狀态資訊過于複雜。
  56. #最小化檔案間的修改
  57. #隐性的跨檔案依賴性,在最好的情況下會導緻代碼不靈活,最壞的情況會引發bug。
  58. import t17mod
  59. t17mod.test()
  60. #這個例子表明全局變量與子產品的屬性是等效的。但比global要多寫許多語句。
  61. 作用域和嵌套函數
  62. #按照LEGB法測,如果嵌套函數将一個變量聲明為全局變量,它将改變整個子產品作用域。如果我們隻想想必被嵌套函數同為此變量名的變量的作用域,可以使用nonlocal聲明,指派會修改最近的嵌套函數中的變量的作用域
  63. print('作用域和嵌套函數')
  64. TT=99 #全局作用
  65. def f1():
  66.     TT=88 #本地作用
  67.     def f2(): #f2是f1函數的本地變量
  68.         print(TT) #根據LEGB法則,X=88
  69.     f2()
  70. f1()
  71. #工廠函數,一個能記住嵌套作用域的變量值的函數,雖然有可能那個作用域已經不存在了。但類是最适合做記憶狀态的。
  72. #本地作用域中N被作為執行的狀态資訊保留下來。
  73. def maker(N):
  74.     def action(M):
  75.         return N*M
  76.     return action
  77. f = maker(5) #N=5
  78. print(f(2)) #M=2,N是被記憶的為5
  79. #新建立的函數不影響原來的
  80. g=maker(9)
  81. print(g(2))
  82. print(f(2))
  83. 盡量避免在def中内嵌套def。隻要第二個函數定義在第一個函數調用前就可行。
  84. 如此可以避免使用嵌套
  85.     x=100
  86.     f2(x)
  87. def f2(x):
  88. 嵌套作用域和lambda
  89.     lambda是一個表達式,但類似def,會生成新的作用域,可以使用在def不能使用的地方,如一個清單或是字典中
  90. def func(N):
  91.     action=(lambda M : N ** M)
  92. A=func(5)
  93. print(A(2))
  94. print(func(5)(3))
  95. 作用域與帶有循環變量的預設參數相比較
  96.     lambda或def函數在一個函數中定義,嵌套在一個循環中,并引用了上層函數的一個變量,變量在循環中被改變,但lambda或def函數最後值是最後一次循環後的值,不會受其它循環值影響
  97. def makeAction():
  98.     acts = []
  99.     for i in range(5):
  100.         acts.append(lambda n : i ** n )
  101.     return acts
  102. actss=makeAction()
  103. print(actss[2](2))
  104. #是以必須把嵌套函數的值傳遞給嵌套作用域的變量
  105. def makeAction2():
  106.         acts.append(lambda n, i=i: i ** n)
  107. actsss=makeAction2()
  108. print(actsss[3](2))
  109. #actsss[i]i最大不能超過range(5)的最大值。
  110. print(actsss[4](2))
  111. 作用域可以被任意嵌套,但是隻有内嵌的函數會被搜尋。
  112.     在python中,平坦優于嵌套。
  113. nonlocal允許對嵌套函數作用域中的名稱指派,并且把這樣的名稱作用域查找限制在嵌套def.
  114. def tester(start):
  115.     state=start
  116.     def nester(label):
  117.         #預設不允許修改嵌套的def作用域中的名稱。
  118.         #state +=1 #UnboundLocalError: local variable 'state' referenced before assignment
  119.         #python3.0中使用nonlocal修改,前提是nonlocal的變量在上層函數中已經指派過。
  120.         #nonlocal隻在上層函數的作用域中查找變量,不會去其它作用域查找。
  121.         #nonlocal state
  122.         #nonlocal nostate #SyntaxError: no binding for nonlocal 'nostate' found
  123.         print(label,state)
  124.         #state +=50
  125.     return nester
  126. F=tester(100)
  127. F(111) #111 100
  128. F(112) #112 150
  129. #如果建立一個新的副本,不會影響原來的state
  130. G=tester(10)
  131. G('egg') #egg 10
  132. G('egg2') #egg2 60
  133. F('old') #old 200
  134. states2 = 9
  135. def tester2(start):
  136.     def nester2(lable):
  137.         global states2
  138.         states2 = 99
  139.         print(lable,states2)
  140.     return nester2
  141. H=tester2(1000)
  142. H('new') #new 99
  143. #nonlocal語句允許在内在保持可變狀态的多個副本,并且解決了在類無法保證的情況下的簡單的狀态保持。
  144. #下面使用類實作存狀态保持。
  145. class ctest:
  146.     def __init__(self,A):
  147.         self.state = A
  148.     def funcA(self,label):
  149.         print(label,self.state)
  150.         self.state +=1
  151. CL=ctest(88)
  152. CL.funcA('egg') #('egg', 88)
  153. CL.funcA('egg2') #('egg2', 89)
  154. print(CL.state) #90
  155. #使用__call__運算符重載工具擷取一個執行個體上的直接調用
  156. class ct:
  157.     def __call__(self,label):
  158.         self.state +=11
  159. CL1=ct(77)
  160. CL1('python') #('python', 77)
  161. CL1('ADD11') #('ADD11', 88)
  162. #下面這個沒搞通,再研究了。
  163. def Z(start):
  164.     def Y(lable):
  165.     print(lable,Y.state)
  166.     Y.state += 1
  167.     Y.state = start
  168.     return Y
  169. X=Z(188)
  170. X('egg')
  171. 全局、非本地、類、函數屬性都提供了狀态保持選項。
  172. 全局隻支援共享資料,類要用OOP知識,類和函數屬性都允許嵌套自身之外通路狀态。最好的工具取決于程式的目的。

t17mod.py

點選(此處)折疊或打開

  1. #!/usr/bin/env python
  2. #coding:utf8
  3. var=99 #全局變量=子產品屬性
  4. def local():
  5.     var = 0 #本地變量,不影響
  6. def glob1():
  7.     global var #全局變量
  8.     var +=1 #修改全局var=100
  9. def glob2():
  10.     var =0
  11.     import t17mod #導入子產品,變成子產品屬性
  12.     t17mod.var +=1
  13. def glob3():
  14.     var = 0
  15.     import sys
  16.     glob = sys.modules['t17mod'] #對子產品屬性生新指派
  17.     glob.var +=1
  18. def test():
  19.     print(var)
  20.     local();glob1();glob2();glob3() #按照執行順序,第一個函數不影響全局變量

結果

  1. /usr/bin/python2.7 /home/talen/PycharmProjects/untitled/t17.py
  2. 變量名解析:LEGB原則
  3. 100
  4. 99
  5. 3
  6. 102
  7. 88
  8. 10
  9. 18
  10. 25
  11. 125
  12. 16
  13. 9
  14. (111, 100)
  15. (112, 100)
  16. ('egg', 10)
  17. ('egg2', 10)
  18. ('old', 100)
  19. ('new', 99)
  20. ('egg', 88)
  21. ('egg2', 89)
  22. 90
  23. ('python', 77)
  24. ('ADD11', 88)
  25. Process finished with exit code 0