日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

python:装饰器

發(fā)布時間:2025/6/17 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python:装饰器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
  • 前言
  • 閉包
    • nonlocal 語句
  • 裝飾器
  • 裝飾器進階:裝飾器流程詳解
  • 裝飾器進階:帶參數(shù)的裝飾器
  • 裝飾器進階:類裝飾器
  • 裝飾器進階:functools.wraps
  • 裝飾器進階:多裝飾器的執(zhí)行順序
  • 裝飾器進階:裝飾器應用示例

前言

連裝飾器都弄不明白,別說會python。

閉包

要理解裝飾器,先要理解閉包。閉包并不是什么很高深的概念,簡單說就是函數(shù)嵌套,內(nèi)部函數(shù)調(diào)用外部函數(shù)的變量。內(nèi)部函數(shù)沒有調(diào)用變量不構成閉包。
先來看一段代碼:

def outer():string = "hello world"def inner():print(string)inner()print(inner.__closure__)outer() print(outer.__closure__)

在inner函數(shù)里面調(diào)用了outer函數(shù)的局部變量string,打印出來的結果是:

(<cell at 0x00000259ACCBD768: str object at 0x00000259ACD307F0>,) None

注意:inner是閉包,outer不是。

通常不是在outer函數(shù)內(nèi)部直接操作inner,而是通過返回inner的地址:

def outer():string = "hello world"def inner():print(string)return innerres = outer() res()

上面這段代碼就是閉包的基本結構。

閉包的優(yōu)點就在于,函數(shù)調(diào)用完之后,函數(shù)內(nèi)部的局部變量依然在內(nèi)存中。以上面這段代碼為例,一般我們認為outer函數(shù)執(zhí)行完后,其內(nèi)部變量string將不再可用,然而這里我們發(fā)現(xiàn)outer執(zhí)行完之后調(diào)用res,string輸出了。其原理就是把函數(shù)和內(nèi)部變量打包到一起,而沒有“用完則釋放局部變量”。

那為什么要使用閉包呢?

看下面這段代碼:

def outer(x):def inner(y):print("%d+%d=%d"%(x,y,x+y))return innerres = outer(1) res(2) res(3)

閉包避免了使用全局變量。

此外,閉包的思想和面向對象編程有相似之處,面向對象編程是將一類對象的屬性封裝起來,閉包是將變量和函數(shù)關聯(lián)起來。
下面是用閉包和類兩種思想實現(xiàn)的同一功能:

class animal():def __init__(self,name):self.name = namedef sound(self,voice):print("%s -> %s"%(self.name,voice))d = animal("dog") d.sound("wangwang")def animal(name):def sound(voice):print("%s -> %s"%(name,voice))return soundc = animal("cat") c("miaomiao")

nonlocal 語句

在python的函數(shù)內(nèi),可以直接引用外部變量,但不能改寫外部變量。閉包也如此。如果你寫出了這樣的代碼,將會報錯:

def outer():count = 0def inner():count += 1inner()outer()

在python 2中可以在函數(shù)內(nèi)使用global語句,但全局變量在任何語言中都不被提倡,因為它很難控制,python 3中引入了nonlocal語句解決了這個問題:

def outer():count = 0def inner():nonlocal countcount += 1inner()outer()

Nonlocal 與 global 的區(qū)別在于 nonlocal 語句會去搜尋本地變量與全局變量之間的變量,其會優(yōu)先尋找層級關系與閉包作用域最近的外部變量。

裝飾器

所謂裝飾器,就是給函數(shù)添加功能,起到裝飾的作用,所以裝飾器本身就是函數(shù)。
最簡單的思路想要實現(xiàn)給函數(shù)增加功能,代碼如下:

def add_func(f):f()print("add func")def func():print("func")# func() add_func(func)

但是這種實現(xiàn)方式有一個缺點:改變了函數(shù)調(diào)用的方式。從func()變成了add_func()。如果要加功能的函數(shù)有很多的話,修改所有的調(diào)用方式無疑工作量太大了。

那就換個寫法:

def add_func():print("add func")def func(f):print("func")return ffunc = func(add_func) func()

這樣寫好像可以不用改變函數(shù)原本的調(diào)用方式,又增加了功能。能解決問題,但是這種寫法避不開前面談到的一個問題:局部變量在函數(shù)調(diào)用后被銷毀的問題。所以用閉包來實現(xiàn)裝飾器簡直不能再合適。代碼如下:

def deco(f):def add_func():f()print("add func")return add_funcdef func():print("func")func = deco(func) func()

本來裝飾器寫到這里應該就結束了,但python的語法力求簡潔。
上面這段代碼還可以寫成這個樣子:

def deco(f):def add_func():f()print("add func")return add_func@deco def func():print("func")func()

這種寫法大大簡化了代碼重構的工作量。不需要修改函數(shù)的調(diào)用方式,只需要在被裝飾函數(shù)的前面加上一個 @decoration_name 即可。對比一下兩段代碼,會發(fā)現(xiàn) @deco 其實就等同于 func = deco(func)

裝飾器進階:裝飾器流程詳解

裝飾器在裝飾函數(shù)與被裝飾函數(shù)之間跳來跳去,下面來研究一下具體流程。還是以前面那段代碼為例:

def deco(f): #1.定義deco函數(shù)def add_func(): #4.定義add_func函數(shù)f() #8.執(zhí)行f,即執(zhí)行被裝飾函數(shù)funcprint("add func") #9.執(zhí)行新添加的功能return add_func #5.返回add_func函數(shù)地址 # @deco <==> func=deco(func)知道這一點就好理解了 #3.調(diào)用deco函數(shù) 6.把返回回來的函數(shù)地址賦值給func @deco def func(): #2.定義func函數(shù)print("func")func() #7.使用裝飾器跳到add_func 10.回到這里,繼續(xù)下面的代碼

裝飾器進階:帶參數(shù)的裝飾器

import timedef printTime(f):def wrapper(*args,**kwargs):print("++++++++++++++++++++++++++++++")start = time.time()f(*args,**kwargs)stop = time.time()print("%s 用時 %s"%(f.__doc__,stop-start))return wrapper@printTime def func1():"""1加到100"""sum = 0;for i in range(1,10001):sum = sum + i;print("sum:%s"%sum)@printTime def func2(name,age):"""print name and age"""print("name:%s\nage:%d"%(name,age))@printTime def func3(num):"""遞歸斐波拉契數(shù)列"""def fib(num):if num == 1 or num == 2:return 1else:return fib(num-1)+fib(num-2)res = fib(num)print(res)func1() func2("hex",22) func3(20)

裝飾器進階:類裝飾器

類也可以作為函數(shù)的裝飾器。類作為裝飾器的時候,要構造一個func屬性,用來作為原函數(shù)的載體。

class beforeSleep:def __init__(self,func):self.func = funcdef __call__(self, *args, **kwargs):print("before sleep,i want to read some books.")self.func()print("after sleep,i want to drink some water.")@beforeSleep def sleep():print("i am sleeping.")sleep()

裝飾器進階:functools.wraps

使用裝飾器能很好的復用代碼,但是會丟失原函數(shù)的元信息。

def logged(func):def with_logging(*args, **kwargs):print(func.__name__) # 輸出 'with_logging'print(func.__doc__) # 輸出 Nonereturn func(*args, **kwargs)return with_logging@logged def f(x):"""does some math"""return x + x * xprint(logged(f))

wraps本身也是一個裝飾器,它能把原函數(shù)的元信息拷貝到裝飾器里面的 func 函數(shù)中,這使得原函數(shù)的元信息不丟失。

from functools import wraps def logged(func):@wraps(func)def with_logging(*args, **kwargs):print(func.__name__) # 輸出 'f'print(func.__doc__) # 輸出 'does some math'return func(*args, **kwargs)return with_logging@logged def f(x):"""does some math"""return x + x * xprint(logged(f))

裝飾器進階:多裝飾器的執(zhí)行順序

@a @b @c def f():pass

執(zhí)行順序:f = a(b(c(f)))

裝飾器進階:裝飾器應用示例

某網(wǎng)站有三個頁面

  • home 所有人都可以訪問
  • user 只有用戶本人可以訪問
  • message 只有用戶本人可以訪問
def home():"""home 4 everyone"""print("home")def user():"""user 4 user"""print("user")def message():"""message 4 user"""print("message")

給它增加一個功能:訪問限制

import getpassdef auth(f):def func(*args,**kwargs):username = input("Please input username:").strip()#getpass庫使得密碼輸入不會顯示在終端。PyCharm不支持,需要在命令行使用python運行當前腳本password = getpass.getpass("Please input password:").strip()if username=="hex" and password=="a-123456":#如果賬號密碼正確,則調(diào)用原函數(shù)f(*args,**kwargs)else:print("invalid input")return funcdef home():"""home 4 everyone"""print("home")@auth def user():"""user 4 user"""print("user")@auth def message():"""message 4 user"""print("message")home() user() message()

轉載于:https://www.cnblogs.com/hextech/p/10073613.html

總結

以上是生活随笔為你收集整理的python:装饰器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。