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

Python裝飾器用法實(shí)例總結(jié)

 更新時(shí)間:2018年05月26日 09:54:57   作者:zhangfh1990  
這篇文章主要介紹了Python裝飾器用法,結(jié)合實(shí)例形式總結(jié)分析了Python裝飾器的功能、原理及常見(jiàn)使用方法,需要的朋友可以參考下

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

寫(xiě)裝飾器

裝飾器只不過(guò)是一種函數(shù),接收被裝飾的可調(diào)用對(duì)象作為它的唯一參數(shù),然后返回一個(gè)可調(diào)用對(duì)象(就像前面的簡(jiǎn)單例子)
注意重要的一點(diǎn),當(dāng)裝飾器被應(yīng)用到被裝飾函數(shù)上時(shí),裝飾器代碼本身就會(huì)運(yùn)行,而不是當(dāng)被裝飾函數(shù)被調(diào)用時(shí).理解這個(gè)很關(guān)鍵,接下來(lái)的幾個(gè)例子的講解過(guò)程也會(huì)變得很清楚

第一個(gè)例子: 函數(shù)注冊(cè)

看下面簡(jiǎn)單的函數(shù)注冊(cè):

registry = []
def register(decorated):
 registry.append(decorated)
 return decorated

注冊(cè)器方法是一個(gè)簡(jiǎn)單的裝飾器。它追加位置參數(shù),也就是被裝飾函數(shù)到registry變量中,然后不做改變地返回被裝飾方法。任何接受register裝飾器的方法會(huì)把它自己追加到registry變量上。

@register
def foo():
 return 3
@register
def bar():
 return 5

如果你訪問(wèn)了registry,可以很容易地在上面迭代并執(zhí)行里面的函數(shù)。

answers = []
for func in registry:
  answers.append(func())

answers 列表現(xiàn)在回包含 [3, 5]. 這是因?yàn)楹瘮?shù)已按次序執(zhí)行,并且它們的返回值被追加到 answers中.

對(duì)于現(xiàn)有的函數(shù)注冊(cè),有幾類(lèi)簡(jiǎn)單的應(yīng)用,例如添加“鉤子(hooks)”到代碼中,這樣的話(huà)自定義的功能在條件事件之前或之后運(yùn)行。 下面的Registry類(lèi)能夠處理這種情況:

class Registry(object):
 def __init__(self):
  self._functions = []
 def register(self, decorated):
  self._functions.append(decorated)
  return decorated
 def run_all(self, *args, **kwargs):
   return_values = []
   for func in self._functions:
    return_values.append(func(*args, **kwargs))
   return return_values

這個(gè)類(lèi)里的register方法讓然像之前一樣按同樣方法工作。用一個(gè)綁定(bound)的方法作為裝飾器完全沒(méi)問(wèn)題。它接收self作為第一參數(shù)(像任何綁定方法一樣),并且需要一個(gè)額外的位置參數(shù),那就是被裝飾函數(shù),通過(guò)創(chuàng)建幾個(gè)不同的 registry實(shí)例,你可以擁有一些完全分開(kāi)的注冊(cè)器。使用相同函數(shù)并且,用超過(guò)一個(gè)注冊(cè)器注冊(cè)它也是可行的,像下面展示的一樣 :

a = Registry()
b = Registry()
@a.register
def foo(x=3):
 return x
@b.register
def bar(x=5):
 return x
@a.register
@b.register
def baz(x=7):
 return x

運(yùn)行兩個(gè)注冊(cè)器的run_alll方法,得到如下結(jié)果:

a.run_all() # [3, 7]
b.run_all() # [5, 7]

注意,run_all 方法能夠使用參數(shù),當(dāng)它們運(yùn)行時(shí)會(huì)把參數(shù)傳給內(nèi)部函數(shù)

a.run_all(x=4) # [4, 4]

運(yùn)行時(shí)包裝代碼

以上這些裝飾器都很簡(jiǎn)單,因?yàn)楸谎b飾方法被傳遞后未經(jīng)更改。然而,有些時(shí)候當(dāng)被裝飾方法執(zhí)行時(shí),你想要運(yùn)行額外的功能。你通過(guò)返回一個(gè)添加了相關(guān)功能并且在它執(zhí)行過(guò)程中調(diào)用被裝飾方法的不同的可調(diào)用對(duì)象來(lái)實(shí)現(xiàn)。

簡(jiǎn)單的類(lèi)型檢查

這有一個(gè)簡(jiǎn)單的裝飾器,確保函數(shù)接收到的每一個(gè)參數(shù)都是整數(shù),否則進(jìn)行報(bào)告:

def requires_ints(decorated):
 def inner(*args, **kwargs):
  #獲取任何可能被發(fā)送的關(guān)鍵值參數(shù)
  kwarg_values = [i for i in kwargs.values()]
  #在發(fā)送給被裝飾方法的每個(gè)值上面進(jìn)行迭代,確保每一個(gè)都是整數(shù);
  #如果不是拋 TypeError 
  for arg in list(args) + kwarg_values:
   if not isinstance(arg, int):
    raise TypeError('%s only accepts integers as  
       arguments.' % decorated.__name__)        
    #運(yùn)行被裝飾方法,返回結(jié)果
   return decorated(*args, **kwargs)
 return inner

發(fā)生了什么?

裝飾器是 requires_ints. 它接受一個(gè)參數(shù),即被裝飾的可調(diào)用對(duì)象。這個(gè)裝飾器做的唯一事情是返回一個(gè)新的可調(diào)用對(duì)象,一個(gè)內(nèi)部的本地函數(shù)。這個(gè)函數(shù)替代了被裝飾的可調(diào)用對(duì)象。你可以看到它如何發(fā)揮作用,聲明一個(gè)函數(shù)并且用requires_ints來(lái)裝飾

@requires_ints
def foo(x, y):
"""Return the sum of x and y."""
 return x + y

注意如果你運(yùn)行 help(foo)獲取的:

Help on function inner in module __main__:
inner(*args, **kwargs)
(END)

inner 函數(shù)已被指派了名字foo,而不是初始的,已定義了的函數(shù)。如果你運(yùn)行 foo(3, 5), inner 函數(shù)會(huì)用這些參數(shù)來(lái)運(yùn)行,inner函數(shù)進(jìn)行類(lèi)型檢查,然后運(yùn)行被裝飾函數(shù),因?yàn)閕nner函數(shù)調(diào)用它,使用decorated(*args, **kwargs),返回8.沒(méi)有這個(gè)調(diào)用,被裝飾方法會(huì)被忽略。

保留helpPreserving the help

一般不想讓裝飾器破壞你的函數(shù)的docstring或者操縱help輸出。

因?yàn)檠b飾器是用來(lái)添加通用的和可重用功能的工具,他們有必要更泛化些。

并且,通常來(lái)說(shuō)如果有人使用一個(gè)函數(shù)試圖在上面運(yùn)行help,他想要的是關(guān)于函數(shù)內(nèi)臟(guts)的信息,而不是外殼(shell)的信息。解決這個(gè)問(wèn)題的方法實(shí)際上應(yīng)用到了 … 仍然是裝飾器. Python 實(shí)現(xiàn)了一個(gè)叫做 @functools.wraps 的裝飾器,它復(fù)制一個(gè)函數(shù)的內(nèi)部元素到另一個(gè)函數(shù)。它把一個(gè)函數(shù)的重要的內(nèi)省元素(introspection elements)復(fù)制給另一個(gè)函數(shù)。

這是同一個(gè)@requires_ints 裝飾器, 但添加了@functools.wraps的使用:

import functools
def requires_ints(decorated):
 @functools.wraps(decorated)
 def inner(*args, **kwargs):
  #獲取可能已作為鍵值參數(shù)發(fā)送的任何值
  kwarg_values = [i for i in kwargs.values()]
  #迭代發(fā)送給被裝飾函數(shù)的每個(gè)值, 并
  #確保每個(gè)參數(shù)都是整數(shù),否則拋TypeError
  for arg in args + kwarg_values:
   if not isinstance(i, int):
    raise TypeError('%s only accepts integers as 
     arguments.' %decorated.__name__)
  #運(yùn)行被裝飾函數(shù)然后返回結(jié)果
  return decorated(*args, **kwargs)
 return inner

裝飾器本身幾乎沒(méi)有改變,除了第二行給inner函數(shù)使用了@functools.wraps裝飾器。你現(xiàn)在必須導(dǎo)入functools(在標(biāo)準(zhǔn)庫(kù)中)。你也會(huì)注意到些額外語(yǔ)法。這個(gè)裝飾器實(shí)際上使用了一個(gè)參數(shù)(稍后會(huì)有更多)。

現(xiàn)在你可以應(yīng)用這個(gè)裝飾器給相同的函數(shù),像下面這樣:

@requires_ints
def foo(x, y):
"""Return the sum of x and y."""
 return x + y

現(xiàn)在當(dāng)你運(yùn)行help(foo)的結(jié)果:

Help on function foo in module __main__:
foo(x, y)
Return the sum of x and y.
(END)

你看到了 foo的docstring ,同時(shí)還有它的方法簽名,然而在蓋頭(hood)下面,@requires_ints裝飾器仍然被應(yīng)用,并且 inner函數(shù)仍然正常運(yùn)行 。取決于你使用的python版本,運(yùn)行結(jié)果可能稍有不同,尤其當(dāng)忽略函數(shù)簽名時(shí)。前面的輸出源自Python 3.4。然而在python 2,提供的函數(shù)簽名仍然有點(diǎn)隱秘(因此,是*args和**kwargs而不是x和y)

用戶(hù)認(rèn)證

這個(gè)模式(即在運(yùn)行被裝飾方法前進(jìn)行過(guò)濾驗(yàn)證)的通常使用場(chǎng)景是用戶(hù)認(rèn)證。考慮一個(gè)需要user作為它的第一個(gè)參數(shù)的方法,user應(yīng)該是User和AnonymousUser類(lèi)的實(shí)例:

class User(object):
"""A representation of a user in our application."""
 def __init__(self, username, email):
  self.username = username
  self.email = email
class AnonymousUser(User):
"""An anonymous user; a stand-in for an actual user that nonetheless
is not an actual user.
"""
 def __init__(self):
  self.username = None
  self.email = None
 def __nonzero__(self):
  return False

裝飾器在此成為隔離用戶(hù)驗(yàn)證的樣板代碼的有力工具。@requires_user裝飾器可以很輕松地認(rèn)證你獲得了一個(gè)User對(duì)象并且不是匿名user

import functools
def requires_user(func):
 @functools.wraps(func)
 def inner(user, *args, **kwargs):
  """Verify that the user is truthy; if so, run the 
  decorated method,
  and if not, raise ValueError.
  """
  # Ensure that user is truthy, and of the correct type.
  # The "truthy"check will fail on anonymous users, since the
  # AnonymousUser subclass has a ‘__nonzero__‘ method that
  # returns False.
  if user and isinstance(user, User):
   return func(user, *args, **kwargs)
  else:
   raise ValueError('A valid user is required to run 
    this.')
 return inner

這個(gè)裝飾器應(yīng)用了一個(gè)通用的,需要樣板化的驗(yàn)證—-用戶(hù)是否登錄進(jìn)系統(tǒng)的驗(yàn)證。當(dāng)你把它作為裝飾器導(dǎo)入,它可重用且易于管理,它應(yīng)用至函數(shù)上也清晰明了。注意這個(gè)裝飾器只會(huì)正確地包裝一個(gè)函數(shù)或者靜態(tài)方法,如果包裝一個(gè)類(lèi)的綁定方法就會(huì)失敗,這是因?yàn)檠b飾器忽視了發(fā)送self作為第一個(gè)參數(shù)到綁定方法的需要。

格式化輸出

除了過(guò)濾一個(gè)函數(shù)的輸入,裝飾器的另一個(gè)用處是過(guò)濾一個(gè)函數(shù)的輸出。當(dāng)你用Python工作時(shí),只要可能就希望使用Python本地對(duì)象。然而通常想要一個(gè)序列化的輸出格式(例如,JSON)
在每個(gè)相關(guān)函數(shù)的結(jié)尾手動(dòng)轉(zhuǎn)換成JSON會(huì)顯得很笨(也不是個(gè)好主意)。
理想的你應(yīng)該使用Python數(shù)據(jù)結(jié)構(gòu)直到需要序列化,但在序列化前仍然可能有其他重復(fù)代碼。
裝飾器為這個(gè)問(wèn)題提供了一個(gè)出色的,輕便的解決方案。考慮下面的裝飾器,它采用python輸出,并序列化結(jié)果為JSON

import functools
import json
def json_output(decorated):
 """Run the decorated function, serialize the result of 
 that function
 to JSON, and return the JSON string.
 """
 @functools.wraps(decorated)
 def inner(*args, **kwargs):
  result = decorated(*args, **kwargs)
  return json.dumps(result)
 return inner

給一個(gè) 簡(jiǎn)單函數(shù)應(yīng)用@json_output 裝飾器 :

@json_output
def do_nothing():
 return {'status': 'done'}

在Python shell中運(yùn)行這個(gè)函數(shù):

>>> do_nothing()
'{"status": "done"}'

結(jié)果是一個(gè)包含JSON的字符串,而不是一個(gè)字典。

這個(gè)裝飾器的優(yōu)美在于它的簡(jiǎn)潔。把這個(gè)裝飾器應(yīng)用到一個(gè)函數(shù),本來(lái)返回python字典,列表或者其它對(duì)象的函數(shù)現(xiàn)在會(huì)返回它的JSON序列化的版本。你可能會(huì)問(wèn)這有什么價(jià)值?畢竟你加了一行裝飾器,實(shí)質(zhì)上只移除了一行調(diào)用json.dumps的代碼。

然而,由于應(yīng)用的需求會(huì)擴(kuò)展,還是考慮一下?lián)碛写搜b飾器的價(jià)值。

例如,某種異常需要被捕獲,并以特定的格式化的json輸出,而不是讓異常上浮產(chǎn)生堆棧跟蹤,該怎么做?因?yàn)橛醒b飾器,這個(gè)功能很容易添加。

import functools
import json
class JSONOutputError(Exception):
 def __init__(self, message):
  self._message = message
 def __str__(self):
  return self._message
def json_output(decorated):
 """Run the decorated function, serialize the result of  
 that function
 to JSON, and return the JSON string.
 """
 @functools.wraps(decorated)
 def inner(*args, **kwargs):
  try:
   result = decorated(*args, **kwargs)
  except JSONOutputError as ex:
   result = {
   'status': 'error',
   'message': str(ex),
   }
  return json.dumps(result)
 return inner

通過(guò)使用錯(cuò)誤處理增強(qiáng)@json_output裝飾器,你已經(jīng)把該功能添加給了應(yīng)用了這個(gè)裝飾器的任何函數(shù)。

這是讓裝飾器如此有價(jià)值的部分原因。對(duì)于代碼輕便化,可重用化而言,它們是非常有用的工具。

現(xiàn)在,如果一個(gè)用@json_output裝飾的函數(shù)拋出了JSONOutputError異常,就會(huì)有特別的錯(cuò)誤處理:

@json_output
def error():
 raise JSONOutputError('This function is erratic.')

運(yùn)行error 函數(shù):

>>> error()
'{"status": "error", "message": "This function is erratic."}'

注意,只有JSONOutputError異常類(lèi)(或它的子類(lèi))會(huì)獲得這種特別的錯(cuò)誤處理。任何其它異常會(huì)正常通過(guò),并產(chǎn)生堆棧跟蹤。

實(shí)質(zhì)上,裝飾器是避免重復(fù)你自己的工具,并且它們的部分價(jià)值在于給未來(lái)的維護(hù)提供鉤子(hooks)。這些不用裝飾器也可以實(shí)現(xiàn),考慮要求用戶(hù)登錄進(jìn)系統(tǒng)的例子,寫(xiě)一個(gè)函數(shù)并把它放在需要這項(xiàng)功能的函數(shù)的入口處就行了。裝飾器首先是一種語(yǔ)法糖(syntactic sugar)。然而是一種很有價(jià)值的語(yǔ)法糖。畢竟,相較于寫(xiě),代碼更多時(shí)候用來(lái)讀,而且你可以一眼定位到裝飾器的位置。

日志記錄Logging

執(zhí)行時(shí)包裝代碼的最后一個(gè)例子是一個(gè)通用的日志記錄函數(shù)。 考慮下面引起函數(shù)調(diào)用的裝飾器, 運(yùn)行時(shí)間,
結(jié)果會(huì)被記錄:

import functools
import logging
import time
def logged(method):
 """Cause the decorated method to be run and its results  
 logged, along
 with some other diagnostic information.
 """
 @functools.wraps(method)
 def inner(*args, **kwargs): 
  #Record our start time.
  start = time.time()
  #Run the decorated method.
  return_value = method(*args, **kwargs)
  #Record our completion time, and calculate the delta.
  end = time.time()
  delta = end - start
  #Log the method call and the result.
  logger = logging.getLogger('decorator.logged')
  logger.warn('Called method %s at %.02f; execution time  
   %.02f seconds; result %r.' %
   (method.__name__, start, delta, return_value))    
   #Return the methods original return value.
  return return_value
 return inner

當(dāng)應(yīng)用到一個(gè)函數(shù)上后,這個(gè)裝飾器正常地運(yùn)行那個(gè)函數(shù),但函數(shù)調(diào)用結(jié)束后會(huì)使用Python logging模塊記錄信息。

>>> import time
>>> @logged… def sleep_and_return(return_value):
... time.sleep(2)
... return return_value…
>>>
>>> sleep_and_return(42)
Called method sleep_and_return at 1424462194.70;
execution time 2.00 seconds; result 42.
42

不像先前的例子,這個(gè)裝飾器不顯式地更改函數(shù)調(diào)用. 不存在你應(yīng)用這個(gè)裝飾器后獲得的結(jié)果與沒(méi)有被裝飾的函數(shù)的結(jié)果不一樣的情況。這個(gè)裝飾器做了些幕后工作,但并不改變實(shí)際結(jié)果。
值得注意的是, @json_output 和 @logged 裝飾器都提供 inner 函數(shù) ,這個(gè)函數(shù)簡(jiǎn)單地以最小的偵測(cè)采用和傳遞可變參數(shù)和關(guān)鍵字參數(shù)。

這是一種重要的模式。一種它尤其重要的方式是,許多裝飾器可能被用來(lái)裝飾純粹的函數(shù)和類(lèi)的方法。記住,在Python中,類(lèi)中聲明的方法會(huì)獲得一個(gè)額外位置參數(shù),即廣為人知的self。當(dāng)裝飾器在使用時(shí),它不會(huì)改變 (這就是為什么先前的requires_user裝飾器在類(lèi)的綁定方法上不起作用)

例如@json_result被用來(lái)裝飾一個(gè)類(lèi)的方法,inner函數(shù)被調(diào)用,它接收一個(gè)類(lèi)的實(shí)例作為第一個(gè)參數(shù)。實(shí)際上這沒(méi)有問(wèn)題。在這種情況下,這個(gè)參數(shù)就是args[0],它被傳送給被裝飾方法。

裝飾器參數(shù)

到目前為止列出的所有裝飾器都沒(méi)有任何參數(shù)。作為討論過(guò)的內(nèi)容,有一個(gè)暗含的參數(shù)–被裝飾的方法。然而,有時(shí)讓裝飾器自身使用一些它需要的信息去裝飾相關(guān)方法會(huì)有用處。

一個(gè)參數(shù)傳給一個(gè)裝飾器和一個(gè)參數(shù)傳給一個(gè)正在調(diào)用的方法之間的不同是,當(dāng)一個(gè)函數(shù)被聲明并被裝飾,傳給裝飾器的參數(shù)會(huì)被立刻處理。相反,傳給函數(shù)的參數(shù)在函數(shù)調(diào)用時(shí)被處理。通過(guò)@functools.wraps的多次使用,你已經(jīng)看到了一個(gè)參數(shù)傳給裝飾器的例子。它使用一個(gè)參數(shù)—-被包裝的方法,方法的help和docstring等類(lèi)似的東西應(yīng)該被保留。然而,裝飾器有內(nèi)含的調(diào)用簽名。他們使用一個(gè)位置參數(shù)–被裝飾的方法。所以,這是怎么工作的?答案說(shuō)來(lái)就復(fù)雜了。

回想運(yùn)行時(shí)包裝代碼的基本裝飾器 ,他們?cè)诰植糠秶暶髁艘粋€(gè) inner 方法 然后返回它. 這就是由裝飾器返回的可調(diào)用對(duì)象. 它被指派了被調(diào)用函數(shù)的名字. 使用參數(shù)的裝飾器多添加一個(gè)包裝層,這是因?yàn)?,使用參?shù)的裝飾器不再是一個(gè)實(shí)際的裝飾器。它是一個(gè)返回裝飾器的函數(shù),是一個(gè)使用一個(gè)參數(shù)(被裝飾的方法)的函數(shù)。然后裝飾函數(shù)并返回一個(gè)可調(diào)用對(duì)象。聽(tīng)起來(lái)混亂,考慮下面的例子,在這里,裝飾器@json_output的功能被增強(qiáng)了,要求縮進(jìn)和排序:

import json
class JSONOutputError(Exception):
 def __init__(self, message):
  self._message = message
 def __str__(self):
  return self._message
def json_output(indent=None, sort_keys=False):
 """Run the decorated function, serialize the result of 
 that function
 to JSON, and return the JSON string.
 """
 def actual_decorator(decorated):
  @functools.wraps(decorated)
  def inner(*args, **kwargs):
   try:
     result = decorated(*args, **kwargs)
   except JSONOutputError as ex:
     result = {
     'status': 'error',
     'message': str(ex),
     }
   return json.dumps(result, indent=indent,   
    sort_keys=sort_keys)
   return inner
 return actual_decorator

那么,發(fā)生了什么,為什么這回起作用?這是一個(gè)函數(shù),json_output,接收兩個(gè)參數(shù)(indent 和 sort_keys). 它返回另一個(gè)函數(shù), 叫 actual_decorator, 這是 (如同名字表名的) 要作為裝飾器使用的. 這是一個(gè)典型的裝飾器—一個(gè)接收可調(diào)用對(duì)象(被裝飾的)做參數(shù)的可調(diào)用對(duì)象,并且返回一個(gè)可調(diào)用對(duì)象(inner).

注意函數(shù)已經(jīng)有所改變來(lái)容納indent和sort_keys參數(shù)。

inner 函數(shù)是最終使用 indent 和 sort_keys 參數(shù)的. 這沒(méi)有問(wèn)題,因?yàn)镻ython的塊作用域規(guī)則允許這樣。使用不同的indent和sort_keys來(lái)調(diào)用也不成問(wèn)題,因?yàn)閕nner是本地函數(shù)(每次裝飾器被使用都會(huì)返回一個(gè)不同的副本)應(yīng)用 json_output 函數(shù):

@json_output(indent=4)
def do_nothing():
 return {'status': 'done'}

現(xiàn)在運(yùn)行do_nothing , 會(huì)產(chǎn)生一個(gè)帶縮進(jìn)的JSON:

>>> do_nothing()
'{\n   "status": "done"\n}'

這是怎么起作用的?

但是,等一等. 如果json_output 不是一個(gè)裝飾器, 而是一個(gè)返回裝飾器的函數(shù),為什么它使用起來(lái)看著像是一個(gè)裝飾器?在這里,Python解釋器做了什么來(lái)讓它工作的?更多的解釋已經(jīng)就緒。在這的關(guān)鍵是操作順序。

特別地,函數(shù)調(diào)用(json_output(indent=4)) 先于裝飾器應(yīng)用語(yǔ)法(@)被處理 。因此,函數(shù)調(diào)用的結(jié)果會(huì)應(yīng)用給裝飾器。

發(fā)生的第一件事情是解釋器尋找 json_output 函數(shù)調(diào)用,然后解析這個(gè)調(diào)用:

@json_output(indent=4)
def do_nothing():
 return {'status': 'done'}

json_output 函數(shù)所要做的一切就是定義另一個(gè)函數(shù), actual_decorator, 然后返回它. 這個(gè)函數(shù)的結(jié)果會(huì)提供給@,像下面這樣:

@actual_decorator
def do_nothing():
 return {'status': 'done'}

現(xiàn)在, actual_decorator 在運(yùn)行. 它聲明另一個(gè)本地函數(shù), inner, 并返回它. 像先前討論過(guò)的,這個(gè)函數(shù)會(huì)被指派名字 do_nothing, 被裝飾方法的名字. 當(dāng)do_nothing被調(diào)用, inner 函數(shù)就會(huì)被調(diào)用, 運(yùn)行被裝飾方法, JSON使用合適縮進(jìn) 調(diào)用dumps 處理結(jié)果

調(diào)用簽名很重要

當(dāng)你引進(jìn)了你的新的,更改過(guò)后的json_output函數(shù),你實(shí)際引進(jìn)了一個(gè)反向不兼容(backward-incompatible )的改變,意識(shí)到這點(diǎn)很重要。

為什么?因?yàn)楝F(xiàn)在期待一個(gè)額外的函數(shù)調(diào)用。如果你想要舊的json_output的行為,不需要任何可用的參數(shù)的值,你仍然必須調(diào)用這個(gè)方法
換句話(huà)說(shuō),你必須像下面這樣做:

@json_output()
def do_nothing():
 return {'status': 'done'}

注意圓括號(hào). 它們有影響,因?yàn)樗鼈冎赋隽撕瘮?shù)正在被調(diào)用(即便沒(méi)有參數(shù)),然后結(jié)果應(yīng)用給@.
前面的代碼不等價(jià)于下面:

@json_output
def do_nothing():
 return {'status': 'done'}

這呈現(xiàn)出兩個(gè)問(wèn)題。有點(diǎn)讓人迷惑,如果你習(xí)慣于看到不帶簽名的裝飾器的應(yīng)用,提供一個(gè)空簽名的需要就違背直覺(jué)。

第二,如果舊的裝飾器在你的應(yīng)用中已經(jīng)存在,你必須返回并編輯所有它們的現(xiàn)有的調(diào)用。如果可能的話(huà),你應(yīng)該避免反向不減容(backward-incompatible)改變。
完美的情況下,下面三種不同的使用方式,裝飾器都會(huì)工作

@json_output
@json_output()
@json_output(indent=4)

讓裝飾器基于接收到的參數(shù)來(lái)改變它的行為是可能的。記住,裝飾器只是一個(gè)函數(shù),擁有任何其它函數(shù)所擁有的所有靈活性,包括對(duì)它獲取到的輸入做出需要做出的響應(yīng)。

考慮這個(gè)對(duì) json_output的更加靈活的迭代:

import functools
import json
class JSONOutputError(Exception):
 def __init__(self, message):
  self._message = message
 def __str__(self):
  return self._message
def json_output(decorated_=None, indent=None, sort_keys=False):
 """Run the decorated function, serialize the result of that function
 to JSON, and return the JSON string.
 """
 # Did we get both a decorated method and keyword arguments?
 # That should not happen.
 if decorated_ and (indent or sort_keys):
  raise RuntimeError('Unexpected arguments.')
 # Define the actual decorator function.
 def actual_decorator(func):
  @functools.wraps(func)
  def inner(*args, **kwargs):
   try:
    result = func(*args, **kwargs)
   except JSONOutputError as ex:
    result = {
    'status': 'error',
    'message': str(ex),
    }
   return json.dumps(result, indent=indent,  
    sort_keys=sort_keys)
  return inner
 #Return either the actual decorator, or the result of applying
 #the actual decorator, depending on what arguments we got.
 if decorated_:
  return actual_decorator(decorated_)
 else:
  return actual_decorator

在目前是不是正作為裝飾器使用這一方面,這個(gè)函數(shù)正努力變得智能。

首先,它確保它不會(huì)以出乎意料的方式被調(diào)用

你永遠(yuǎn)不要期待接收被裝飾方法同時(shí)關(guān)鍵值參數(shù),因?yàn)檠b飾器被調(diào)用時(shí)總是以被裝飾方法作為唯一參數(shù)。

第二,它定義了actual_decorator函數(shù),這是要被返回和應(yīng)用的實(shí)際裝飾器。它定義了inner 函數(shù),它時(shí)從裝飾器中返回的最終函數(shù)。

最終, 它返回合適結(jié)果,這基于它被如何調(diào)用:

如果 設(shè)置了decorated_ , 它會(huì)被作為純粹的裝飾器調(diào)用, 沒(méi)有方法簽名,然后它的響應(yīng)應(yīng)用給最終裝飾器并返回inner函數(shù) . 再次注意使用參數(shù)的裝飾器如何實(shí)際地運(yùn)作。首先, actual_decorator(decorated_)被調(diào)用,解析。然后它的結(jié)果(必須是一個(gè)可調(diào)用對(duì)象,因?yàn)檫@是一個(gè)裝飾器)被調(diào)用,inner被提供作為唯一的參數(shù)。

如果decorated_沒(méi)被設(shè)置,就會(huì)使用關(guān)鍵字參數(shù)調(diào)用,這個(gè)函數(shù)必須返回一個(gè)實(shí)際的裝飾器,它接收被裝飾方法,并返回inner。因此,這個(gè)函數(shù)返回actual_decorator
然后這會(huì)被python解釋器作為實(shí)際裝飾器(最終返回inner)

為何這個(gè)技術(shù)有價(jià)值?它讓你能夠先先前使用過(guò)的一樣管理你的裝飾器的功能。意味著你不用去更新已經(jīng)應(yīng)用了裝飾器的每個(gè)地方,但仍然獲得了在你需要時(shí)添加參數(shù)的靈活性。

更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python加密解密算法與技巧總結(jié)》、《Python編碼操作技巧總結(jié)》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》及《Python入門(mén)與進(jìn)階經(jīng)典教程

希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。

相關(guān)文章

  • Python中遇到的小問(wèn)題及解決方法匯總

    Python中遇到的小問(wèn)題及解決方法匯總

    到年底了,現(xiàn)在的時(shí)間適合寫(xiě)點(diǎn)最近的小總結(jié),所以下面這篇文章主要介紹了Python中遇到的一些小問(wèn)題及解決方法,需要的朋友可以參考借鑒,下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-01-01
  • python pow函數(shù)的底層實(shí)現(xiàn)原理介紹

    python pow函數(shù)的底層實(shí)現(xiàn)原理介紹

    這篇文章主要介紹了python pow函數(shù)的底層實(shí)現(xiàn)原理介紹,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-03-03
  • 詳談python3中用for循環(huán)刪除列表中元素的坑

    詳談python3中用for循環(huán)刪除列表中元素的坑

    下面小編就為大家分享一篇詳談python3中用for循環(huán)刪除列表中元素的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • Python實(shí)現(xiàn)將Word和Excel文件轉(zhuǎn)換為PPT

    Python實(shí)現(xiàn)將Word和Excel文件轉(zhuǎn)換為PPT

    在日常工作中,我們經(jīng)常需要將多個(gè)Word文檔或Excel表格的內(nèi)容匯總到一個(gè)PPT演示文稿中,手動(dòng)執(zhí)行這項(xiàng)任務(wù)可能非常耗時(shí),因此,使用Python自動(dòng)化這個(gè)過(guò)程可以大大提高效率,所以本文給大家介紹了Python實(shí)現(xiàn)將Word和Excel文件轉(zhuǎn)換為PPT,需要的朋友可以參考下
    2024-08-08
  • 詳解Python使用Plotly繪圖工具,繪制甘特圖

    詳解Python使用Plotly繪圖工具,繪制甘特圖

    這篇文章主要介紹了Python使用Plotly繪圖工具,繪制甘特圖,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • python函數(shù)的兩種嵌套方法小結(jié)

    python函數(shù)的兩種嵌套方法小結(jié)

    這篇文章主要介紹了python函數(shù)的兩種嵌套方法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-01-01
  • tensorflow 查看梯度方式

    tensorflow 查看梯度方式

    今天小編就為大家分享一篇tensorflow 查看梯度方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • 如何用python實(shí)現(xiàn)結(jié)構(gòu)體數(shù)組

    如何用python實(shí)現(xiàn)結(jié)構(gòu)體數(shù)組

    這篇文章主要介紹了如何用python實(shí)現(xiàn)結(jié)構(gòu)體數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Python requests亂碼的五種解決辦法

    Python requests亂碼的五種解決辦法

    在Python中使用requests庫(kù)發(fā)送HTTP請(qǐng)求時(shí),有時(shí)會(huì)遇到亂碼的問(wèn)題,亂碼通常是由于編碼不一致或解碼錯(cuò)誤導(dǎo)致的,這篇文章給大家介紹了Python requests亂碼的五種解決辦法,并通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • 無(wú)需邀請(qǐng)碼!Manus復(fù)刻開(kāi)源版OpenManus下載安裝與體驗(yàn)

    無(wú)需邀請(qǐng)碼!Manus復(fù)刻開(kāi)源版OpenManus下載安裝與體驗(yàn)

    Manus的完美復(fù)刻開(kāi)源版OpenManus安裝與體驗(yàn),無(wú)需邀請(qǐng)碼,手把手教你如何在本地安裝與配置Manus的開(kāi)源版OpenManus
    2025-03-03

最新評(píng)論

秦皇岛市| 淳化县| 长武县| 额尔古纳市| 塘沽区| 南召县| 平武县| 安化县| 荆州市| 偏关县| 织金县| 灵宝市| 东乌珠穆沁旗| 正定县| 阿克陶县| 油尖旺区| 萨嘎县| 巫山县| 和林格尔县| 德惠市| 同德县| 崇州市| 论坛| 新泰市| 临夏市| 祁东县| 长阳| 武山县| 南平市| 张家口市| 纳雍县| 探索| 利川市| 宁强县| 北宁市| 平武县| 剑川县| 柘荣县| 开封市| 罗江县| 砚山县|