日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python:装饰器

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

前言

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

閉包

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

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

在inner函數里面調用了outer函數的局部變量string,打印出來的結果是:

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

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

通常不是在outer函數內部直接操作inner,而是通過返回inner的地址:

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

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

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

那為什么要使用閉包呢?

看下面這段代碼:

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

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

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

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的函數內,可以直接引用外部變量,但不能改寫外部變量。閉包也如此。如果你寫出了這樣的代碼,將會報錯:

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

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

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

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

裝飾器

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

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

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

那就換個寫法:

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

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

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()

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

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

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

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

裝飾器進階:帶參數的裝飾器

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):"""遞歸斐波拉契數列"""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)

裝飾器進階:類裝飾器

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

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

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

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本身也是一個裝飾器,它能把原函數的元信息拷貝到裝飾器里面的 func 函數中,這使得原函數的元信息不丟失。

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))

裝飾器進階:多裝飾器的執行順序

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

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

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

某網站有三個頁面

  • 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":#如果賬號密碼正確,則調用原函數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:装饰器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。