最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python的裝飾器使用詳解

 更新時間:2017年06月26日 08:47:36   作者:0xFEE1C001  
最近在學習python,下面是在Python學習小組上介紹的內容,現學現賣、多練習是好的學習方式,希望大家能夠喜歡

Python有大量強大又貼心的特性,如果要列個最受歡迎排行榜,那么裝飾器絕對會在其中。

初識裝飾器,會感覺到優(yōu)雅且神奇,想親手實現時卻總有距離感,就像深閨的冰美人一般。這往往是因為理解裝飾器時把其他的一些概念混雜在一起了。待我撫去層層面紗,你會看到純粹的裝飾器其實蠻簡單直率的。

裝飾器的原理

在解釋器下跑個裝飾器的例子,直觀地感受一下。
# make_bold就是裝飾器,實現方式這里略去

>>> @make_bold
... def get_content():
...  return 'hello world'
...
>>> get_content()
'<b>hello world</b>'

被 make_bold 裝飾的 get_content ,調用后返回結果會自動被 b 標簽包住。怎么做到的呢,簡單4步就能明白了。

1. 函數是對象

我們定義個 get_content 函數。這時 get_content 也是個對象,它能做所有對象的操作。

def get_content():
  return 'hello world'

它有 id ,有 type ,有值。

>>> id(get_content)
140090200473112
>>> type(get_content)
<class 'function'>
>>> get_content
<function get_content at 0x7f694aa2be18>

跟其他對象一樣可以被賦值給其它變量。

>>> func_name = get_content
>>> func_name()
'hello world'

它可以當參數傳遞,也可以當返回值

>>> def foo(bar):
...   print(bar())
...   return bar
...
>>> func = foo(get_content)
hello world
>>> func()
'hello world'

2. 自定義函數對象

我們可以用 class 來構造函數對象。有成員函數 __call__ 的就是函數對象了,函數對象被調用時正是調用的 __call__ 。

class FuncObj(object):
  def __init__(self, name):
    print('Initialize')
    self.name= name

  def __call__(self):
    print('Hi', self.name)

我們來調用看看。可以看到, 函數對象的使用分兩步:構造和調用 (同學們注意了,這是考點)。

>>> fo = FuncObj('python')
Initialize
>>> fo()
Hi python

3. @ 是個語法糖

裝飾器的 @ 沒有做什么特別的事,不用它也可以實現一樣的功能,只不過需要更多的代碼。

@make_bold
def get_content():
  return 'hello world'

# 上面的代碼等價于下面的

def get_content():
  return 'hello world'
get_content = make_bold(get_content)

make_bold 是個函數,要求入參是函數對象,返回值是函數對象。 @ 的語法糖其實是省去了上面最后一行代碼,使可讀性更好。用了裝飾器后,每次調用 get_content ,真正調用的是 make_bold 返回的函數對象。

4. 用類實現裝飾器

入參是函數對象,返回是函數對象,如果第2步里的類的構造函數改成入參是個函數對象,不就正好符合要求嗎?我們來試試實現 make_bold 。

class make_bold(object):
  def __init__(self, func):
    print('Initialize')
    self.func = func

  def __call__(self):
    print('Call')
    return '<b>{}</b>'.format(self.func())

大功告成,看看能不能用。

>>> @make_bold
... def get_content():
...   return 'hello world'
...
Initialize
>>> get_content()
Call
'<b>hello world</b>'

成功實現裝飾器!是不是很簡單?

這里分析一下之前強調的 構造 和 調用 兩個過程。我們去掉 @ 語法糖好理解一些。
# 構造,使用裝飾器時構造函數對象,調用了__init__

>>> get_content = make_bold(get_content)
Initialize

# 調用,實際上直接調用的是make_bold構造出來的函數對象
>>> get_content()
Call
'<b>hello world</b>'

到這里就徹底清楚了,完結撒花,可以關掉網頁了~~~(如果只是想知道裝飾器原理的話)

函數版裝飾器

閱讀源碼時,經常見到用嵌套函數實現的裝飾器,怎么理解?同樣僅需4步。

1. def 的函數對象初始化

用 class 實現的函數對象很容易看到什么時候 構造 的,那 def 定義的函數對象什么時候 構造 的呢?
# 這里的全局變量刪去了無關的內容

>>> globals()
{}
>>> def func():
...   pass
...
>>> globals()
{'func': <function func at 0x10f5baf28>}

不像一些編譯型語言,程序在啟動時函數已經構造那好了。上面的例子可以看到,執(zhí)行到 def 會才構造出一個函數對象,并賦值給變量 make_bold 。

這段代碼和下面的代碼效果是很像的。

class NoName(object):
  def __call__(self):
    pass

func = NoName()

2. 嵌套函數

Python的函數可以嵌套定義。

def outer():
  print('Before def:', locals())
  def inner():
    pass
  print('After def:', locals())
  return inner

inner 是在 outer 內定義的,所以算 outer 的局部變量。執(zhí)行到 def inner 時函數對象才創(chuàng)建,因此每次調用 outer 都會創(chuàng)建一個新的 inner 。下面可以看出,每次返回的 inner 是不同的。

>>> outer()
Before def: {}
After def: {'inner': <function outer.<locals>.inner at 0x7f0b18fa0048>}
<function outer.<locals>.inner at 0x7f0b18fa0048>
>>> outer()
Before def: {}
After def: {'inner': <function outer.<locals>.inner at 0x7f0b18fa00d0>}
<function outer.<locals>.inner at 0x7f0b18fa00d0>

3. 閉包

嵌套函數有什么特別之處?因為有閉包。

def outer():
  msg = 'hello world'
  def inner():
    print(msg)
  return inner

下面的試驗表明, inner 可以訪問到 outer 的局部變量 msg 。

>>> func = outer()
>>> func()
hello world

閉包有2個特點
1. inner 能訪問 outer 及其祖先函數的命名空間內的變量(局部變量,函數參數)。
2. 調用 outer 已經返回了,但是它的命名空間被返回的 inner 對象引用,所以還不會被回收。

這部分想深入可以去了解Python的LEGB規(guī)則。

4. 用函數實現裝飾器

裝飾器要求入參是函數對象,返回值是函數對象,嵌套函數完全能勝任。

def make_bold(func):
  print('Initialize')
  def wrapper():
    print('Call')
    return '<b>{}</b>'.format(func())
  return wrapper

用法跟類實現的裝飾器一樣??梢匀サ?@ 語法糖分析下 構造 和 調用 的時機。

>>> @make_bold
... def get_content():
...   return 'hello world'
...
Initialize
>>> get_content()
Call
'<b>hello world</b>'

因為返回的 wrapper 還在引用著,所以存在于 make_bold 命名空間的 func 不會消失。 make_bold 可以裝飾多個函數, wrapper 不會調用混淆,因為每次調用 make_bold ,都會有創(chuàng)建新的命名空間和新的 wrapper 。

到此函數實現裝飾器也理清楚了,完結撒花,可以關掉網頁了~~~(后面是使用裝飾的常見問題)

常見問題

1. 怎么實現帶參數的裝飾器?

帶參數的裝飾器,有時會異常的好用。我們看個例子。

>>> @make_header(2)
... def get_content():
...   return 'hello world'
...
>>> get_content()
'<h2>hello world</h2>'

怎么做到的呢?其實這跟裝飾器語法沒什么關系。去掉 @ 語法糖會變得很容易理解。

@make_header(2)
def get_content():
  return 'hello world'

# 等價于

def get_content():
  return 'hello world'
unnamed_decorator = make_header(2)
get_content = unnamed_decorator(get_content)

上面代碼中的 unnamed_decorator 才是真正的裝飾器, make_header 是個普通的函數,它的返回值是裝飾器。

來看一下實現的代碼。

def make_header(level):
  print('Create decorator')

  # 這部分跟通常的裝飾器一樣,只是wrapper通過閉包訪問了變量level
  def decorator(func):
    print('Initialize')
    def wrapper():
      print('Call')
      return '<h{0}>{1}</h{0}>'.format(level, func())
    return wrapper

  # make_header返回裝飾器
  return decorator

看了實現代碼,裝飾器的 構造 和 調用 的時序已經很清楚了。

>>> @make_header(2)
... def get_content():
...   return 'hello world'
...
Create decorator
Initialize
>>> get_content()
Call
'<h2>hello world</h2>'

2. 如何裝飾有參數的函數?

為了有條理地理解裝飾器,之前例子里的被裝飾函數有意設計成無參的。我們來看個例子。

@make_bold
def get_login_tip(name):
  return 'Welcome back, {}'.format(name)

最直接的想法是把 get_login_tip 的參數透傳下去。

class make_bold(object):
  def __init__(self, func):
    self.func = func

  def __call__(self, name):
    return '<b>{}</b>'.format(self.func(name))

如果被裝飾的函數參數是明確固定的,這么寫是沒有問題的。但是 make_bold 明顯不是這種場景。它既需要裝飾沒有參數的 get_content ,又需要裝飾有參數的 get_login_tip 。這時候就需要可變參數了。

class make_bold(object):
  def __init__(self, func):
    self.func = func
  def __call__(self, *args, **kwargs):
    return '<b>{}</b>'.format(self.func(*args, **kwargs))

當裝飾器不關心被裝飾函數的參數,或是被裝飾函數的參數多種多樣的時候,可變參數非常合適??勺儏挡粚儆谘b飾器的語法內容,這里就不深入探討了。

3. 一個函數能否被多個裝飾器裝飾?

下面這么寫合法嗎?

@make_italic
@make_bold
def get_content():
  return 'hello world'

合法。上面的的代碼和下面等價,留意一下裝飾的順序。

def get_content():
  return 'hello world'
get_content = make_bold(get_content) # 先裝飾離函數定義近的
get_content = make_italic(get_content)

4. functools.wraps 有什么用?

Python的裝飾器倍感貼心的地方是對調用方透明。調用方完全不知道也不需要知道調用的函數被裝飾了。這樣我們就能在調用方的代碼完全不改動的前提下,給函數patch功能。

為了對調用方透明,裝飾器返回的對象要偽裝成被裝飾的函數。偽裝得越像,對調用方來說差異越小。有時光偽裝函數名和參數是不夠的,因為Python的函數對象有一些元信息調用方可能讀取了。為了連這些元信息也偽裝上, functools.wraps 出場了。它能用于把被調用函數的 __module__ , __name__ , __qualname__ , __doc__ , __annotations__ 賦值給裝飾器返回的函數對象。

import functools

def make_bold(func):
  @functools.wraps(func)
  def wrapper(*args, **kwargs):
    return '<b>{}</b>'.format(func(*args, **kwargs))
  return wrapper

對比一下效果。

>>> @make_bold
... def get_content():
...   '''Return page content'''
...   return 'hello world'

# 不用functools.wraps的結果
>>> get_content.__name__
'wrapper'
>>> get_content.__doc__
>>>

# 用functools.wraps的結果
>>> get_content.__name__
'get_content'
>>> get_content.__doc__
'Return page content'

實現裝飾器時往往不知道調用方會怎么用,所以養(yǎng)成好習慣加上 functools.wraps 吧。

這次是真·完結了,撒花吧~~~

相關文章

  • K近鄰法(KNN)相關知識總結以及如何用python實現

    K近鄰法(KNN)相關知識總結以及如何用python實現

    這篇文章主要介紹了K近鄰法(KNN)相關知識總結以及如何用python實現,幫助大家更好的利用python實現機器學習,感興趣的朋友可以了解下
    2021-01-01
  • django ORM之values和annotate使用詳解

    django ORM之values和annotate使用詳解

    這篇文章主要介紹了django ORM之values和annotate使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-05-05
  • Python函數學習筆記

    Python函數學習筆記

    Python探測局部作用域的時候:是在python編譯代碼時檢測,而不是通過他們在運行時的賦值。
    2008-10-10
  • python美多商城項目開發(fā)小結

    python美多商城項目開發(fā)小結

    這篇文章主要介紹了python美多商城項目開發(fā)小結,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • Flask框架的學習指南之開發(fā)環(huán)境搭建

    Flask框架的學習指南之開發(fā)環(huán)境搭建

    本文是Flask框架的學習指南系列文章的第一篇,主要給大家講述的是開發(fā)環(huán)境的搭建工作,有需要的小伙伴可以參考下
    2016-11-11
  • 利用Python演示數型數據結構的教程

    利用Python演示數型數據結構的教程

    這篇文章主要介紹了利用Python演示數型數據結構的教程,核心代碼其實只有一行(XD),需要的朋友可以參考下
    2015-04-04
  • Kotlin 中的 apply 函數用法詳解

    Kotlin 中的 apply 函數用法詳解

    apply 函數是 Kotlin 中一個十分方便的工具,它使得對對象進行初始化和配置變得更加簡潔、清晰,通過與 Java 的對比,我們可以看出 Kotlin 在處理類似情況時具有更高的表現力和簡潔性,本文給大家介紹Kotlin apply 函數的用法,感興趣的朋友一起看看吧
    2024-03-03
  • 詳解Python實現URL監(jiān)測與即時推送

    詳解Python實現URL監(jiān)測與即時推送

    這篇文章主要為大家介紹了Python實現URL監(jiān)測與即時推送,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11
  • 極簡的Python入門指引

    極簡的Python入門指引

    這篇文章是一個極簡的Python入門指引、原文在網絡上人氣相當高,將一些基本知識用實例代碼說明,需要的朋友可以參考下
    2015-04-04
  • Python?列表操作全面教程示例

    Python?列表操作全面教程示例

    這篇文章主要為大家介紹了Python?列表操作的全面教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10

最新評論

安平县| 呼伦贝尔市| 富源县| 额尔古纳市| 会同县| 洛隆县| 巧家县| 昆明市| 马山县| 东山县| 建昌县| 临桂县| 克拉玛依市| 张家口市| 成都市| 济阳县| 曲阳县| 河北省| 诸暨市| 和平县| 孙吴县| 涡阳县| 河池市| 饶阳县| 西充县| 堆龙德庆县| 乌苏市| 枣阳市| 易门县| 布拖县| 白山市| 岳阳县| 八宿县| 玉林市| 花莲市| 仪征市| 忻州市| 临泉县| 吴川市| 高陵县| 永兴县|