国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python裝飾器用法實例總結

2020-01-04 15:56:09
字體:
來源:轉載
供稿:網友

本文實例講述了Python裝飾器用法。分享給大家供大家參考,具體如下:

一、裝飾器是什么

python的裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。簡單的說裝飾器就是一個用來返回函數的函數。

它經常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼并繼續重用。

概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。

二、為什么需要裝飾器

1、先來看一個簡單例子:

def foo():print('i am foo')

2、增加需求

現在有一個新的需求,希望可以記錄下函數的執行日志,于是在代碼中添加日志代碼:

def foo():  print('i am foo')  print("foo is running")

3、又有需求

假設現在有100個函數需要增加這個需求,并且后續可能還要對這一百個函數都增加執行前打印日志的需求,怎么辦?還一個個改嗎?

當然不了,這樣會造成大量雷同的代碼,為了減少重復寫代碼,我們可以這樣做,重新定義一個函數:專門處理日志 ,日志處理完之后再執行真正的業務代碼。

def use_logging(func):  print("%s is running" % func.__name__)  func()def bar():  print('i am bar')use_logging(bar)#result:#bar is running#i am bar

通過以上use_logging函數我們增加了日志功能,不管以后有多少函數需要增加日志或者修改日志的格式我們只需要修改use_logging函數,并執行use_logging(被裝飾的函數)就達到了我們想要的效果。

def use_logging(func):  print("%s is running" % func.__name__)  return func@use_loggingdef bar():  print('i am bar')bar()

三、基礎裝飾器入門

1、裝飾器語法糖

python提供了@符號作為裝飾器的語法糖,使我們更方便的應用裝飾函數。但使用語法糖要求裝飾函數必須return一個函數對象。因此我們將上面的func函數使用內嵌函數包裹并return。

裝飾器相當于執行了裝飾函數use_loggin后又返回被裝飾函數bar,因此bar()被調用的時候相當于執行了兩個函數。等價于use_logging(bar)()

def use_logging(func):  def _deco():    print("%s is running" % func.__name__)    func()  return _deco@use_loggingdef bar():  print('i am bar')bar()

2、對帶參數的函數進行裝飾

現在我們的參數需要傳入兩個參數并計算值,因此我們需要對內層函數進行改動傳入我們的兩個參數a和b,等價于use_logging(bar)(1,2)

def use_logging(func):  def _deco(a,b):    print("%s is running" % func.__name__)    func(a,b)  return _deco@use_loggingdef bar(a,b):  print('i am bar:%s'%(a+b))bar(1,2)

我們裝飾的函數可能參數的個數和類型都不一樣,每一次我們都需要對裝飾器做修改嗎?這樣做當然是不科學的,因此我們使用python的變長參數*args和**kwargs來解決我們的參數問題。

3、函數參數數量不確定

不帶參數裝飾器版本,這個格式適用于不帶參數的裝飾器。

經過以下修改我們已經適應了各種長度和類型的參數。這個版本的裝飾器已經可以任意類型的無參數函數。

def use_logging(func):  def _deco(*args,**kwargs):    print("%s is running" % func.__name__)    func(*args,**kwargs)  return _deco@use_loggingdef bar(a,b):  print('i am bar:%s'%(a+b))@use_loggingdef foo(a,b,c):  print('i am bar:%s'%(a+b+c))bar(1,2)foo(1,2,3)

4、裝飾器帶參數

帶參數的裝飾器,這個格式適用于帶參數的裝飾器。

某些情況我們需要讓裝飾器帶上參數,那就需要編寫一個返回一個裝飾器的高階函數,寫出來會更復雜。比如:

#! /usr/bin/env python# -*- coding:utf-8 -*-# __author__ = "TKQ"def use_logging(level):  def _deco(func):    def __deco(*args, **kwargs):      if level == "warn":        print "%s is running" % func.__name__      return func(*args, **kwargs)    return __deco  return _deco@use_logging(level="warn")def bar(a,b):  print('i am bar:%s'%(a+b))bar(1,3)# 等價于use_logging(level="warn")(bar)(1,3)

5、functools.wraps

使用裝飾器極大地復用了代碼,但是他有一個缺點就是原函數的元信息不見了,比如函數的docstring、__name__、參數列表,先看例子:

def use_logging(func):  def _deco(*args,**kwargs):    print("%s is running" % func.__name__)    func(*args,**kwargs)  return _deco@use_loggingdef bar():  print('i am bar')  print(bar.__name__)bar()#bar is running#i am bar#_deco#函數名變為_deco而不是bar,這個情況在使用反射的特性的時候就會造成問題。因此引入了functools.wraps解決這個問題。

使用functools.wraps:

import functoolsdef use_logging(func):  @functools.wraps(func)  def _deco(*args,**kwargs):    print("%s is running" % func.__name__)    func(*args,**kwargs)  return _deco@use_loggingdef bar():  print('i am bar')  print(bar.__name__)bar()#result:#bar is running#i am bar#bar ,這個結果是我們想要的。OK啦!

6、實現帶參數和不帶參數的裝飾器自適應

import functoolsdef use_logging(arg):  if callable(arg):#判斷參入的參數是否是函數,不帶參數的裝飾器調用這個分支    @functools.wraps(arg)    def _deco(*args,**kwargs):      print("%s is running" % arg.__name__)      arg(*args,**kwargs)    return _deco  else:#帶參數的裝飾器調用這個分支    def _deco(func):      @functools.wraps(func)      def __deco(*args, **kwargs):        if arg == "warn":          print "warn%s is running" % func.__name__        return func(*args, **kwargs)      return __deco    return _deco@use_logging("warn")# @use_loggingdef bar():  print('i am bar')  print(bar.__name__)bar()

三、類裝飾器

使用類裝飾器可以實現帶參數裝飾器的效果,但實現的更加優雅簡潔,而且可以通過繼承來靈活的擴展.

1、類裝飾器

class loging(object):  def __init__(self,level="warn"):    self.level = level  def __call__(self,func):    @functools.wraps(func)    def _deco(*args, **kwargs):      if self.level == "warn":        self.notify(func)      return func(*args, **kwargs)    return _deco  def notify(self,func):    # logit只打日志,不做別的    print "%s is running" % func.__name__@loging(level="warn")#執行__call__方法def bar(a,b):  print('i am bar:%s'%(a+b))bar(1,3)

2、繼承擴展類裝飾器

class email_loging(Loging):  '''  一個loging的實現版本,可以在函數調用時發送email給管理員  '''  def __init__(self, email='admin@myproject.com', *args, **kwargs):    self.email = email    super(email_loging, self).__init__(*args, **kwargs)  def notify(self,func):    # 發送一封email到self.email    print "%s is running" % func.__name__    print "sending email to %s" %self.email@email_loging(level="warn")def bar(a,b):  print('i am bar:%s'%(a+b))bar(1,3)

希望本文所述對大家Python程序設計有所幫助。


注:相關教程知識閱讀請移步到python教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 七台河市| 桂东县| 康乐县| 恩平市| 金寨县| 塘沽区| 江城| 平利县| 天津市| 蒙山县| 洞头县| 肇州县| 韶关市| 宽甸| 丹江口市| 澜沧| 衡南县| 綦江县| 永宁县| 阳曲县| 西和县| 称多县| 泸定县| 什邡市| 靖西县| 进贤县| 永安市| 葵青区| 崇义县| 聊城市| 哈密市| 海晏县| 敖汉旗| 连平县| 乌什县| 曲阜市| 桃源县| 沙河市| 梅州市| 高平市| 辉县市|