首頁 後端開發 Python教學 詳解python裝飾器的實例教程

詳解python裝飾器的實例教程

Jun 17, 2017 am 11:17 AM
python 實例 教學 詳解

Python中的裝飾器是你進入Python大門的一道坎,不管你跨不跨過去它都在那裡。 Python中的裝飾器的概念常常會讓人搞得一頭霧水,所以今天就好好來分析一下python中的裝飾器

1.作用域

 在python中,作用域分為兩種:全域作用域和局部作用域。

 全域作用域是定義在檔案層級的變數,函數名稱。而局部作用域,則是定義函數內部。

 關於作用域,我要理解兩點:a.在全域不能存取到局部定義的變數b.在局部能夠存取到全域定義的變量,但是不能修改全域定義的變數(當然有方法可以修改)

 下面我們來看看下面實例:


x = 1
def funx():
  x = 10
  print(x) # 打印出10

funx()
print(x) # 打印出1
登入後複製

  如果局部沒有定義變數x,那麼函數內部會從內往外開始查找x,如果沒有找到,就會報錯


x = 1
def funx():
  print(x) # 打印出1

funx()
print(x) # 打印出1

x = 1
def funx():
  def func1():
    print(x) # 打印出1
  func1()

funx()
print(x) # 打印出1
登入後複製

  因此,關於作用域的問題,只需要記住兩點就行:全域變數能夠被文件任何地方引用,但修改只能在全域進行操作;如果局部沒有找到所需的變量,就會往外進行查找,沒有找到就會報錯。

2.高階函數

 我們知道,函數名其實就是指向一段記憶體空間的位址,既然是位址,那麼我們可以利用這個特性來。

 a函數名稱可以作為一個值


def delete(ps):
  import os
  filename = ps[-1]
  delelemetns = ps[1]
  with open(filename, encoding='utf-8') as f_read,\
    open('tmp.txt', 'w', encoding='utf-8') as f_write:
    for line in iter(f_read.readline, ''):
      if line != '\n': # 处理非空行
        if delelemetns in line:
          line = line.replace(delelemetns,'')
        f_write.write(line)
  os.remove(filename)
  os.rename('tmp.txt',filename)

def add(ps):
  filename = ps[-1]
  addelemetns = ps[1]
  with open(filename, 'a', encoding='utf-8') as fp:
    fp.write("\n", addelemetns)

def modify(ps):
  import os
  filename = ps[-1]
  modify_elemetns = ps[1]
  with open(filename, encoding='utf-8') as f_read, \
      open('tmp.txt', 'w', encoding='utf-8') as f_write:
    for line in iter(f_read.readline, ''):
      if line != '\n': # 处理非空行
        if modify_elemetns in line:
          line = line.replace(modify_elemetns, '')
        f_write.write(line)
  os.remove(filename)
  os.rename('tmp.txt', filename)


def search(cmd):
  filename = cmd[-1]
  pattern = cmd[1]
  with open(filename, 'r', encoding="utf-8") as f:
    for line in f:
      if pattern in line:
        print(line, end="")
    else:
      print("没有找到")

dic_func ={'delete': delete, 'add': add, 'modify': modify, 'search': search}

while True:
  inp = input("请输入您要进行的操作:").strip()
  if not inp:
    continue
  cmd_1 = inp.split()
  cmd = cmd_1[0]
  if cmd in dic_func:
    dic_func[cmd](cmd_1)
  else:
    print("Error")
登入後複製

 b.函數名稱可以作為一個值


def outer():
  def inner():
    pass
  return inner

s = outer()
print(s)

######输出结果为#######
<function outer.<locals>.inner at 0x000000D22D8AB8C8>
登入後複製

 b.函數名稱可以作為傳回值


def index():
  print("index func")

def outer(index):
  s = index
  s()
  
outer(index)

######输出结果#########

index func
登入後複製

 c..函數名稱可以作為一個參數

def outer():
  def inner():
    print("inner func excuted")
  inner() # 调用执行inner()函数
  print("outer func excuted")
outer() # 调用执行outer函数

####输出结果为##########
inner func excuted
outer func excuted
登入後複製

 所以滿足上面兩個條件中的一個,都可以稱為高級函數.

3.閉包函數

  閉包函數必須滿足兩個條件:1.函數內部定義的函數2.包含對外部作用域而非全域作用域的參考


  下面透過一些實例來說明閉包函數:

  實例一:以下僅在函數內部定義了一個函數,但並非閉包函數.


#
x = 1
def outer():
  def inner():
    print("x=%s" %x) # 引用了一个非inner函数内部的变量
    print("inner func excuted")
  inner() # 执行inner函数
  print("outer func excuted")

outer()
#####输出结果########
x=1
inner func excuted
outer func excuted
登入後複製

  實例二:以下在函數內部定義了一個函數,而且還引用了一個外部變數x,那麼這個是閉包函數麼?答案:不是


def outer():
  x = 1
  def inner():
    print("x=%s" %x)
    print("inner func excuted")
  inner()
  print("outer func excuted")

outer()

#####输出结果#########
x=1
inner func excuted
outer func excuted
登入後複製

  在回頭來看看對閉包函數的定義,是不是兩條都滿足?聰明的你,一定發現不滿足第二條.對,這裡的變數x,是屬於全局變數,而非外部作用於域的變數。再來看看下面範例:


def outer():
  x = 1
  def inner():
    print("x=%s" %x)
    print("inner func excuted")
  print("outer func excuted")
  return inner # 返回内部函数名
  
outer()
登入後複製

  顯然,上面實例滿足閉包函數的條件。現在,你應該清楚,作為一個閉包函數,必須得滿足上述的兩個條件,缺一不可。但是,一般情況下,我們都會給閉包函數回傳一個值.這裡先不說為什麼.在接下來的內容中,你會看到這個回傳值的用途.

def outer():
  x = 1
  y = 2

  def inner():
    print("x= %s" %x)
    print("y= %s" %y)

  print(inner.closure)
  return inner

outer()

######输出结果#######
(<cell at 0x000000DF9EA965B8: int object at 0x000000006FC2B440>, <cell at 0x000000DF9EA965E8: int object at 0x000000006FC2B460>)
登入後複製

  現在我們來抽象的定義一下閉包函數。它是函數和與其相關的引用環境組合而成的實體。在實現深層約束時,需要創建一個能明確表示引用環境的東西,並將它與相關的子程序捆綁在一起,這樣捆綁起成為閉包。在上面實例中,我們可以發現,閉包函數,它必須包含自己的函數以及一個外部變數才能真正稱得上是一個閉包函數。如果沒有一個外部變數與其綁定,那麼這個函數不能算是閉包函數。

  那麼怎麼知道一個閉包函數有多少個外部引用變數?看看下面程式碼.

from urllib.request import urlopen

def index(url)
  def get()
    return urlopen(url).read()
  return get

python = index("http://www.python.org") # 返回的是get函数的地址
print(python()) # 执行get函数《并且将返回的结果打印出来
baidu = index("http://www.baidu.com")
print(baidu())
登入後複製

  結果顯示,在inner內部,引用了兩個外部局部變數。如果引用的是非局部變量,那麼這裡輸出的為None.

  閉包函數的特點:

1.自帶作用域2.延遲計算


  那麼閉包函數有什麼作用呢?我們清楚的知道,閉包函數在定義時,一定會綁定一個外在環境。這個整體才能算的上是一個閉包函數,那麼我們可以利用這個綁定特性,來完成某些特殊的功能。

  實例三:根據傳入的URL,來下載頁面原始碼

import time, random

def index():
  time.sleep(random.randrange(1, 5))
  print("welcome to index page")
登入後複製
登入後複製

  有人可以會說,這個不滿足閉包函數的條件啊!我沒有引用非全域的外部變數啊。其實並非如此,給,我們之前說過,只要在函數內部的變數都屬於函數。那我在index(url),這個url也屬於函數內部,只不過我們省略一步而已,所以上面那個函數也是閉包函數。

4.裝飾器

  有了以上基礎,對於裝飾器就好理解了.

  裝飾器:外部函數傳入被裝飾函數名,內部函數傳回裝飾函數名。

  特點:1.不修改被裝飾函數的呼叫方式2.不修改被裝飾函數的原始碼######  a.無參裝飾者#######  有以下實例,我們需要計算一下程式碼執行的時間。 ###


import time, random

def index():
  time.sleep(random.randrange(1, 5))
  print("welcome to index page")
登入後複製
登入後複製

  根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.


import time, random

def outer(func): # 将index的地址传递给func
  def inner():
    start_time = time.time()
    func()  # fun = index 即func保存了外部index函数的地址
    end_time = time.time()
    print("运行时间为%s"%(end_time - start_time))
  return inner # 返回inner的地址

def index():
  time.sleep(random.randrange(1, 5))
  print("welcome to index page")

index = outer(index) # 这里返回的是inner的地址,并重新赋值给index

index()
登入後複製

  但是,有些情况,被装饰的函数需要传递参数进去,有些函数又不需要参数,那么如何来处理这种变参数函数呢?下面来看看有参数装饰器的使用情况.

  b.有参装饰器


def outer(func): # 将index的地址传递给func
  def inner(*args, **kwargs):
    start_time = time.time()
    func(*args, **kwargs)  # fun = index 即func保存了外部index函数的地址
    end_time = time.time()
    print("运行时间为%s"%(end_time - start_time))
  return inner # 返回inner的地址
登入後複製

  下面来说说一些其他情况的实例。

  如果被装饰的函数有返回值


def timmer(func):
  def wrapper(*args,**kwargs):
    start_time = time.time()
    res=func(*args,**kwargs) #res来接收home函数的返回值
    stop_time=time.time()
    print(&#39;run time is %s&#39; %(stop_time-start_time))
    return res 
  return wrapper

def home(name):
  time.sleep(random.randrange(1,3))
  print(&#39;welecome to %s HOME page&#39; %name)
  return 123123123123123123123123123123123123123123
登入後複製

  这里补充一点,加入我们要执行被装饰后的函数,那么应该是如下调用方式:

  home = timmer(home) # 等式右边返回的是wrapper的内存地址,再将其赋值给home,这里的home不在是原来的的那个函数,而是被装饰以后的函数了。像home = timmer(home)这样的写法,python给我们提供了一个便捷的方式------语法糖@.以后我们再要在被装饰的函数之前写上@timmer,它的效果就和home = timmer(home)是一样的。

  如果一个函数被多个装饰器装饰,那么执行顺序是怎样的。


import time
import random

def timmer(func):
  def wrapper():
    start_time = time.time()
    func()
    stop_time=time.time()
    print(&#39;run time is %s&#39; %(stop_time-start_time))
  return wrapper
def auth(func):
  def deco():
    name=input(&#39;name: &#39;)
    password=input(&#39;password: &#39;)
    if name == &#39;egon&#39; and password == &#39;123&#39;:
      print(&#39;login successful&#39;)
      func() #wrapper()
    else:
      print(&#39;login err&#39;)
  return deco

@auth  # index = auth(timmer(index))         
@timmer # index = timmer(index)
def index():
 
  time.sleep(3)
  print(&#39;welecome to index page&#39;)

index()
登入後複製

  实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。

  关于装饰器,还有一些高级用法,有兴趣的可以自己研究研究。

以上是詳解python裝飾器的實例教程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

vs code 可以在 Windows 8 中運行嗎 vs code 可以在 Windows 8 中運行嗎 Apr 15, 2025 pm 07:24 PM

VS Code可以在Windows 8上運行,但體驗可能不佳。首先確保系統已更新到最新補丁,然後下載與系統架構匹配的VS Code安裝包,按照提示安裝。安裝後,注意某些擴展程序可能與Windows 8不兼容,需要尋找替代擴展或在虛擬機中使用更新的Windows系統。安裝必要的擴展,檢查是否正常工作。儘管VS Code在Windows 8上可行,但建議升級到更新的Windows系統以獲得更好的開發體驗和安全保障。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

visual studio code 可以用於 python 嗎 visual studio code 可以用於 python 嗎 Apr 15, 2025 pm 08:18 PM

VS Code 可用於編寫 Python,並提供許多功能,使其成為開發 Python 應用程序的理想工具。它允許用戶:安裝 Python 擴展,以獲得代碼補全、語法高亮和調試等功能。使用調試器逐步跟踪代碼,查找和修復錯誤。集成 Git,進行版本控制。使用代碼格式化工具,保持代碼一致性。使用 Linting 工具,提前發現潛在問題。

vscode怎麼在終端運行程序 vscode怎麼在終端運行程序 Apr 15, 2025 pm 06:42 PM

在 VS Code 中,可以通過以下步驟在終端運行程序:準備代碼和打開集成終端確保代碼目錄與終端工作目錄一致根據編程語言選擇運行命令(如 Python 的 python your_file_name.py)檢查是否成功運行並解決錯誤利用調試器提升調試效率

vscode 擴展是否是惡意的 vscode 擴展是否是惡意的 Apr 15, 2025 pm 07:57 PM

VS Code 擴展存在惡意風險,例如隱藏惡意代碼、利用漏洞、偽裝成合法擴展。識別惡意擴展的方法包括:檢查發布者、閱讀評論、檢查代碼、謹慎安裝。安全措施還包括:安全意識、良好習慣、定期更新和殺毒軟件。

See all articles