Python裝飾器,遞歸,子產品
先看一個Python執行過程
>>> def foo(): #定義函數
... print 'foo' #執行函數列印字元串foo
...
>>> foo
<function foo at 0x7fd3a06f77d0> #表示foo是一個函數
>>> foo() #執行函數輸出
foo
重新定義foo
>>>foo = lambda x:x + 1
<function <lambda> at 0x7fd3a06f75f0>
>>> foo(1)
2 #foo函數被重新定義了,執行的是+1的函數
在看一個列子
>>> def f1(arg):
... arg()
>>> def func():
... print '12'
>>> f1(func)
12
執行步驟為
1,定義函數f1放入記憶體改函數傳遞一個參數arg 執行arg()代表執行一個函數
2,定義函數func 該函數執行列印數字12
3,執行函數f1調用func為參數,然後在f1内部加()執行func函數,相當于func(),結果就是列印出12
根據這個原理提出以下需求
假設公司有一個基礎平台,基礎平台提供底層的功能函數(比如調用資料庫,監控API等)這裡為了簡化就定義為輸出一個字元串
(也隻定義了一個函數,實際工作肯定有多個函數)
vim day5-1.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
print 'f1'
f1()
業務部門調用基礎平台提供的函數輸出為f1
假如業務有需求要在調用前加一個驗證(為了簡化也用輸出一個字元串代替),有幾種方案
1,業務部門調用的時候加驗證
修改代碼
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
print 'f1'
print 'before'
f1()
每個部門在調用函數的時候自己加驗證,很明顯不行
2,修改底層函數
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
print 'before'
print 'f1'
f1()
業務部門不需要修改代碼,但是底層函數有很多個,一個個修改不現實
3,重新定義一個函數,在底層函數一個個插入
#!/usr/bin/python
# -*- coding:utf-8 -*-
def before():
print 'before'
def f1():
before()
print 'f1'
f1()
隻需要在每個底層函數調用一次新的函數即可,好像可以了
但是
寫代碼要遵循開發封閉原則,雖然在這個原則是用的面向對象開發,但是也适用于函數式程式設計,簡單來說,它規定已經實作的功能代碼不允許被修改,但可以被擴充,即:
- 封閉:已實作的功能代碼塊
- 開放:對擴充開發
4,終極解決方案,使用裝飾器
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func): #把函數auth寫入記憶體
def inner(): #函數内部函數
print 'before' #實作類似于驗證的功能
func() #執行func()其實這裡就是執行了f1()
return inner #傳回内部函數inner
@auth #裝飾器方法,調用auth函數
def f1(): #定義底層函數這裡底層函數遵守封閉性原則不做任何改動
print 'f1'
f1() #模拟各個部門調用底層函數,也未做改動
定義裝飾器的執行過程如下
一,把auth函數定義到記憶體
二,定義底層函數f1
三,使用@加函數名調用裝飾器把函數f1作為參數傳遞給函數auth
四,裝飾器内部先執行print 'before' 在執行f1() 傳回函數inner相當于把函數f1作為參數傳遞給函數auth然後把傳回值在指派給f1函數
五,調用函數f1此時的輸出為執行完驗證以後的輸出
完美實作了功能,底層定義函數代碼及各個部門調用函數代碼沒有變化
裝飾器其實就是函數加Python的文法糖
PS:裝飾器傳回的是一個函數體,不是函數執行後的結果,需要執行才能出結果
如下
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func):
def inner():
print 'before'
func()
return inner
def f1():
print 'f1'
f1=auth(f1)
f1()
把f1作為函數auth的參數然後在把傳回的函數值指派給f1,最後在執行一次f1函數出結果(比較low)
以上例子調用的函數f1是沒有參數的
假如有函數是有參數的呢
重新定義一個帶參數的裝飾器即可
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func):
def inner():
print 'before'
func()
return inner
def auth_arg(func):
def inner(arg):
print 'before'
func(arg)
return inner
@auth
def f1():
print 'f1'
#f1()
@auth_arg
def f5(arg):
print 'f5',arg
如果有多個參數使用上面的方式需要定義多個函數
Python提供一種通用的裝飾器方法無論提供多少個參數均可
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func):
def inner(*args,**kwargs):
print 'before'
func(*args,**kwargs)
return inner
@auth
def f1():
print 'f1'
#f1()
@auth
def f5(arg):
print 'f5',arg
小結
1,裝飾器是一個函數,至少兩層
2,執行auth函數,被裝飾的函數作為參數auth(foo)
auth函數的傳回值,指派給被裝飾的函數的函數名
3,動态參數,可以裝飾含有n個參數的函數
4,函數的傳回值
5,多裝飾器
以上調用的函數的沒有傳回值的假如調用的基礎函數有傳回值呢
vim basic.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
def login():
name = 'alex'
if name == 'alex':
return True
else:
return False
def auth(func):
def inner(*args,**kwargs):
is_login = login()
if not is_login:
return '非法使用者'
print 'before'
temp = func(*args,**kwargs)
return temp
return inner
@auth
def f1():
print 'f1'
#f1()
@auth
def f5(arg):
print 'f5',arg
@auth
def fetch_server_list(arg):
server_list = ['c1','c2','c3']
return server_list
在auth裡面傳回了原函數的傳回值,并且模拟了一個驗證的過程
vim day5-3.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
import basic
ret_list = basic.fetch_server_list('test')
print ret_list
調用輸出

如果使用者名不是alex則會輸出非法使用者的提示
PS:在寫web項目的時候都有使用裝飾器來做驗證的作用.
再次模拟使用以後key來驗證的過程
#!/usr/bin/python
# -*- coding:utf-8 -*-
def login(token):
local = 'askjdhkjahsdkjahsakjsd'
if local == token:
return True
else:
return False
def auth(func):
#fetch_server_list('test',token=key)
def inner(*args,**kwargs):
# key = kwargs["token"] #因為原函數fetch_server_list隻接受一個參數
# del kwargs['token'] #是以把傳遞的字典的一個參數去掉,該參數在驗證的時候已經使用過一次
key = kwargs.pop('token') #這句等同于以上兩句
is_login = login(key)
if not is_login:
return '非法使用者'
print 'before'
temp = func(*args,**kwargs)
return temp
return inner
@auth
def f1():
print 'f1'
#f1()
@auth
def f5(arg):
print 'f5',arg
@auth
def fetch_server_list(arg):
server_list = ['c1','c2','c3']
return server_list
#!/usr/bin/python
# -*- coding:utf-8 -*-
import basic
key = 'askjdhkjahsdkjahsakjsd'
ret_list = basic.fetch_server_list('test',token=key)
print ret_list
模拟傳遞一個key進行驗證,輸出結果不變
多裝飾器
vim day5-5.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
def w1(func):
def inner():
print 'w1,before'
func()
print 'w1,after'
return inner
def w2(func):
def inner():
print 'w2,before'
func()
print 'w2,after'
return inner
#@w2
@w1
def foo():
print 'foo'
foo()
單裝飾器和多裝飾器的運作結果如下,一層裝飾器就是套一層盒子最上面的就是最外面的那層盒子
多裝飾器的用途,使用者登陸後的權限不同,一般用不上.