天天看點

Python修飾符 詳解

  修飾符(有的也翻譯成裝飾符、裝飾器,英文叫decorator)是Python中一個很有意思的feature,它可以向已經寫好的代碼中添加功能。這其實也叫元程式設計,因為程式的一部分在編譯的時候嘗試修改程式的另一部分。

高階函數

  在學習Python的修飾符前,我們要知道幾個a概念,首先是Python中的所有東西都是對象,所有我們定義的變量、類甚至與于函數,都是對象。函數是對象是個什麼概念呢?就是函數之間可以互相指派:

def first(msg):
	pritn(msg)
	
first("Hello")
>>> Hello

second = first
second("Hello")
>>> Hello
           

  上面的

first

second

都是指向同一個函數對象的。這種直接函數名之間的直接指派,在C++中是不支援的。

  當然,函數也可以當作參數傳遞給其他函數,最常見的就是

map

filter

reduce

這三個函數了。

def func(x):
	return x*2
list(map(func, [1,2,3]))
>>>[2,4,6]
           
list(map(lambda x: x * 2, range(1,4)))
>>>[2,4,6]
           

下面我們自定義一個函數,其參數也是函數:

def add(x):
    return x + 1

def test(func, x):
    return add(x)
           

執行

test(add, 2)
>>> 3
           

在Python中,把其他函數當做參數的函數,叫做高階函數。

嵌套函數

  就是nested function,在函數中定義函數,這個我們之前寫過,直接放上之前的連結:Python嵌套函數 閉包 詳解

Python修飾符

  下面回歸正題,來講Python修飾符。在Python中,使用

@

來表示修飾符,但這裡我們先看下不使用

@

來完成一個修飾函數。

def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner

def ordinary():
    print("I am ordinary")
    
pretty = make_pretty(ordinary)
pretty()
>>> I got decorated
    I am ordinary
           

  在這個例子中,

make_pretty()

就是一個修飾器,在

pretty = make_pretty(ordinary)

語句中,

ordinary

函數被修飾,傳回的函數指派給了

pretty

。是以修飾器就像對函數又進行了一層的包裝,下面來看使用修飾符@來對函數進行修飾,隻需在定義函數的上一行加上

@func

即可。

def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner
    
@make_pretty
def ordinary():
    print("I am ordinary")
    
ordinary()
>>> I got decorated
    I am ordinary
           

  修飾符必須出現在函數定義前一行,不允許和函數定義在同一行。隻可以在子產品或類定義層内對函數進行修飾,不允許修飾一個類。一個修飾符就是一個函數,它将被修飾的函數做為參數,并傳回修飾後的同名函數或其它可調用的東西。

  本質上講,修飾符@類似于回調函數,把其它的函數作為自己的入口參數,在目的函數執行前,執行一些自己的操作,然後傳回目的函數。當Python解釋器讀取到修飾符時,會調用修飾符的函數,來看下面的例子(這個例子隻是為了解釋Python讀取到修飾符時會直接調用,這個修飾函數并沒有傳回目的函數)

# test.py
def test(func):
    print('This is test function step1')
    func()
    print('This is test function step2')
    
@test
def func():
    print('This is func...')
           

代碼列印如下:

This is test function step1
This is func...
This is test function step2
           

這段代碼中隻是定義了兩個函數,并沒有調用它們,但仍然會有結果列印出來。

  當被修飾的函數中含有參數時,應該怎麼來寫呢?

  我們定義一個除法函數,參數a和b,傳回a/b的結果。

def divide(a,b)
	return a / b
           

我們知道除法的除數不能是0,是以當我們令b=0時,就會報錯。

>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
           

  是以我們需要增加判斷機制,當除數為0時,需要告訴使用者不能執行。但

divide()

函數中我們就隻想完成除法的功能,判斷機制就可以通過修飾符來完成。

def smart_divide(func):
    def inner(a, b):
        print("I am going to divide", a, "and", b)
        if b == 0:
            print("Whoops! cannot divide")
            return

        return func(a, b)
    return inner

@smart_divide
def divide(a, b):
    print(a/b)
           

可以看到,在修飾符函數内容的嵌套函數

inner()

中,嵌套函數的參數依然的(a,b),傳回值也同樣是

func(a,b)

,這就是包含參數的函數被修飾時的寫法。

divide(2,5)
>>> I am going to divide 2 and 5
    0.4

divide(2,0)
>>> I am going to divide 2 and 0
    Whoops! cannot divide
           

一般的,我們定義函數的參數可以設為

*args, **kwargs

,其中*args表示位置參數,位置很重要,是以清單形式呈現;**kwargs:關鍵字參數,位置不重要。字典形式呈現。

def works_for_all(func):
    def inner(*args, **kwargs):
        print("I can decorate any function")
        return func(*args, **kwargs)
    return inner

@works_for_all
def ff(*args, **kwargs):
    print(args)
    print(kwargs)
           

調用示例如下:

ff(1,2,a=3,b=4)
>>> I can decorate any function
    (1, 2)
    {'a': 3, 'b': 4}
           

一個函數的多個修飾符

  Python中,一個函數可以有多個修飾符。

def star(func):
    def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
    return inner

def percent(func):
    def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
    return inner

@star
@percent
def printer(msg):
    print(msg)

printer("Hello")
           

列印結果如下:

******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
           

其實上面的:

@star
@percent
def printer(msg):
    print(msg)
           

就等效于下面的寫法:

def printer(msg):
    print(msg)
printer = star(percent(printer))
           

是以執行的步驟為:

  • printer

    函數傳入到

    percent

    函數中,

    percent

    函數直接傳回

    inner

    函數給

    star

    函數
  • star

    函數中,傳入的參數

    func

    percent

    的内嵌函數

    inner

    ,此時在

    star

    inner

    中,先執行

    print("*" * 30)

    ,再執行

    func()

    ,也就是會進入到

    percent->inner

    中執行
  • 此時将執行

    print("%" * 30)

    ,再執行

    func()

    ,此時的

    func

    printer

  • 再執行

    percent->inner

    中的

    print("%" * 30)

    ,傳回後又到了

    star->inner

    中,執行

    print("*" * 30)

寫的有點繞,看下面的圖會更加清晰,從左到右執行。

Python修飾符 詳解

總結

  下面來總結一下修飾符的要點:

  1. 修飾符必須出現在函數定義前一行,不允許和函數定義在同一行;
  2. 修飾符的内嵌函數的參數跟被修飾的函數參數相同,内嵌函數的傳回值跟被修飾函數的定義表達式一樣
  3. 一個函數可以用多個修飾符,執行順序是參考上圖。

微信公衆号:

Python修飾符 詳解