1. 程式人生 > >Python學習之==>裝飾器

Python學習之==>裝飾器

在Python中,裝飾器和迭代器、生成器都是非常重要的高階函式。

在講裝飾器之前,我們先要學習以下三個內容:

一、函式的作用域

1、作用域介紹

Python中的作用域分為四種情況:

  • L:local,區域性作用域,即函式中定義的變數;
  • E:enclosing,巢狀的父級函式的區域性作用域,即包含此函式的上級函式的區域性作用域,但不是全域性的;
  • G:globa,全域性變數,就是模組級別定義的變數;
  • B:built-in,系統固定模組裡面的變數,比如int, bytearray等。 

搜尋變數的優先順序順序依次是:L –> E –> G –>B,即:區域性作用域>外層作用域>當前模組中的全域性>Python內建作用域。 

 1 x = int(2.9)  # int built-in
 2 
 3 g_count = 0   # global
 4 
 5 def outer():
 6     o_count = 1  # enclosing
 7     def inner():
 8         i_count = 2  # local
 9         print(o_count)
10     print(i_count)   # 找不到
11     inner()
12 outer()
13 
14 print(o_count) #找不到

當然,local和enclosing是相對的,enclosing變數相對上層來說也是local。

2、作用域的產生

在Python中,只有函式(def、lambda)、類(class)以及模組(module)才會引入新的作用域,其它的程式碼塊(如if、try、for等)是不會引入新的作用域的,如下程式碼:

1 if 2>1:
2     x = 1
3 print(x)  # 1

if並沒有引入一個新的作用域,x仍處在當前作用域中,後面程式碼可以使用。

1 def test():
2     x = 2
3 print(x) # NameError: name 'x2' is not defined

上面這段程式碼則會報錯,因為def、class、lambda是可以引入新作用域的。

3、變數的修改

 1 x = 6
 2 def f():
 3     print(x)
 4     x = 5
 5 f()
 6 
 7 # 錯誤的原因在於print(x)時,直譯器會在區域性作用域找,會找到x=5(函式已經載入到記憶體),但x使用在宣告前了,所以報錯:
 8 # local variable 'x' referenced before assignment.如何證明找到了x=5呢?簡單:註釋掉x=5,x=6,報錯為:name 'x' is not defined
 9 
10 # 同理
11 x = 6
12 def f():
13     x += 1  # local variable 'x' referenced before assignment.
14 f()

4、global關鍵字

當內部作用域想修改外部作用域的變數時,就要用到global和nonlocal關鍵字了,當修改的變數是在全域性作用域(global作用域)上的,就要使用global先宣告一下,程式碼如下:

1 count = 10
2 def outer():
3     global count
4     print(count)   #10
5     count = 100
6     print(count)   #100
7 outer()
8 print(count)       #100

5、nonlocal關鍵字

global關鍵字宣告的變數必須在全域性作用域上,不能巢狀作用域上,當要修改巢狀作用域(enclosing作用域,外層非全域性作用域)中的變數怎麼辦呢,這時就需要nonlocal關鍵字了,程式碼如下:

1 def outer():
2     count = 10
3     def inner():
4         nonlocal count
5         count = 20
6         print(count)  #20
7     inner()
8     print(count)      #20
9 outer()

6、作用域小結

  • 變數查詢順序:LEGB,作用域區域性>外層作用域>當前模組中的全域性>python內建作用域;
  • 只有函式、類以及模組才能引入新的作用域;
  • 對於一個變數,內部作用域先宣告就會覆蓋外部變數,不宣告直接使用,就會使用外部作用域的變數;
  • 內部作用域要修改外部作用域變數的值時,全域性變數要使用global關鍵字,巢狀作用域變數要使用nonlocal關鍵字。nonlocal是python3新增的關鍵字,有了這個 關鍵字,就能完美的實現閉包了;

二、函式即物件

  在Python中,函式和之前學過的字串、整型、列表等一樣都是物件,而且函式是最高階的物件(物件是類的例項化,可以呼叫相應的方法,函式是包含變數的物件)。如下:

1 def foo():
2     print('i am the foo')
3     bar()
4 
5 def bar():
6     print('i am the bar')
7 
8 foo()

接著,我們再聊一下函式在記憶體的儲存情況:

函式物件的呼叫僅僅比其它物件多了一個()而已!foo,bar與a,b一樣都是個變數名。

既然函式是物件,那麼自然滿足下面兩個條件:

1、函式可以被賦值給其他變數

1 def foo():
2     print('foo')
3 bar=foo
4 bar()
5 foo()
6 print(id(foo),id(bar))  #1386464801520 1386464801520

2、函式可以被定義在另外一個函式內(作為引數或者返回值),類似於整型、字串等物件

 1 # *******函式名作為引數**********
 2 def foo(func):
 3     print('foo')
 4     func()
 5 
 6 def bar():
 7     print('bar')
 8 
 9 foo(bar)
10 
11 # *******函式名作為返回值*********
12 def foo():
13     print('foo')
14     return bar
15 
16 def bar():
17     print('bar')
18 
19 b = foo()
20 b()

 

 

 

三、函式的巢狀及閉包