国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PyTips 0x0f - Python 修飾器與 functools

dingding199389 / 2949人閱讀

項目地址:https://git.io/pytips

Python 的修飾器是一種語法糖(Syntactic Sugar),也就是說:

@decorator
@wrap
def func():
    pass

是下面語法的一種簡寫:

def func():
    pass
func = decorator(wrap(func))

關于修飾器的兩個主要問題:

修飾器用來修飾誰

誰可以作為修飾器

修飾函數

修飾器最常見的用法是修飾新定義的函數,在 0x0d 上下文管理器中提到上下文管理器主要是為了更優雅地完成善后工作,而修飾器通常用于擴展函數的行為或屬性:

def log(func):
    def wraper():
        print("INFO: Starting {}".format(func.__name__))
        func()
        print("INFO: Finishing {}".format(func.__name__))
    return wraper

@log
def run():
    print("Running run...")
run()
INFO: Starting run
Running run...
INFO: Finishing run
修飾類

除了修飾函數之外,Python 3.0 之后增加了對新定義類的修飾(PEP 3129),但是對于類別屬性的修改可以通過 Metaclasses 或繼承來實現,而新增加的類別修飾器更多是出于 Jython 以及 IronPython 的考慮,但其語法還是很一致的:

from time import sleep, time
def timer(Cls):
    def wraper():
        s = time()
        obj = Cls()
        e = time()
        print("Cost {:.3f}s to init.".format(e - s))
        return obj
    return wraper
@timer
class Obj:
    def __init__(self):
        print("Hello")
        sleep(3)
        print("Obj")
o = Obj()
Hello
Obj
Cost 3.005s to init.
類作為修飾器

上面兩個例子都是以函數作為修飾器,因為函數才可以被調用(callable) decorator(wrap(func))。除了函數之外,我們也可以定義可被調用的類,只要添加 __call__ 方法即可:

class HTML(object):
    """
        Baking HTML Tags!
    """
    def __init__(self, tag="p"):
        print("LOG: Baking Tag <{}>!".format(tag))
        self.tag = tag
    def __call__(self, func):
        return lambda: "<{0}>{1}".format(self.tag, func(), self.tag)

@HTML("html")
@HTML("body")
@HTML("div")
def body():
    return "Hello"

print(body())
LOG: Baking Tag !
LOG: Baking Tag !
LOG: Baking Tag 
!
Hello
傳遞參數

在實際使用過程中,我們可能需要向修飾器傳遞參數,也有可能需要向被修飾的函數(或類)傳遞參數。按照語法約定,只要修飾器 @decorator 中的 decorator 是可調用即可,decorator(123) 如果返回一個新的可調用函數,那么也是合理的,上面的 @HTML("html") 即是一例,下面再以 flask 的路由修飾器為例說明如何傳遞參數給修飾器:

RULES = {}
def route(rule):
    def decorator(hand):
        RULES.update({rule: hand})
        return hand
    return decorator
@route("/")
def index():
    print("Hello world!")

def home():
    print("Welcome Home!")
home = route("/home")(home)

index()
home()
print(RULES)
Hello world!
Welcome Home!
{"/": , "/home": }

向被修飾的函數傳遞參數,要看我們的修飾器是如何作用的,如果像上面這個例子一樣未執行被修飾函數只是將其原模原樣地返回,則不需要任何處理(這就把函數當做普通的值一樣看待即可):

@route("/login")
def login(user = "user", pwd = "pwd"):
    print("DB.findOne({{{}, {}}})".format(user, pwd))
login("hail", "python")
DB.findOne({hail, python})

如果需要在修飾器內執行,則需要稍微變動一下:

def log(f):
    def wraper(*args, **kargs):
        print("INFO: Start Logging")
        f(*args, **kargs)
        print("INFO: Finish Logging")
    return wraper

@log
def run(hello = "world"):
    print("Hello {}".format(hello))
run("Python")
INFO: Start Logging
Hello Python
INFO: Finish Logging
functools

由于修飾器將函數(或類)進行包裝之后重新返回:func = decorator(func),那么有可能改變原本函數(或類)的一些信息,以上面的 HTML 修飾器為例:

@HTML("body")
def body():
    """
        return body content
    """
    return "Hello, body!"
print(body.__name__)
print(body.__doc__)
LOG: Baking Tag !

None

因為 body = HTML("body")(body) ,而 HTML("body").__call__() 返回的是一個 lambda 函數,因此 body 已經被替換成了 lambda,雖然都是可執行的函數,但原來定義的 body 中的一些屬性,例如 __doc__/__name__/__module__ 都被替換了(在本例中__module__沒變因為都在同一個文件中)。為了解決這一問題 Python 提供了 functools 標準庫,其中包括了 update_wrapperwraps 兩個方法(源碼)。其中 update_wrapper 就是用來將原來函數的信息賦值給修飾器中返回的函數:

from functools import update_wrapper
"""
functools.update_wrapper(wrapper, wrapped[, assigned][, updated])
"""


class HTML(object):
    """
        Baking HTML Tags!
    """
    def __init__(self, tag="p"):
        print("LOG: Baking Tag <{}>!".format(tag))
        self.tag = tag
    def __call__(self, func):
        wraper = lambda: "<{0}>{1}".format(self.tag, func(), self.tag)
        update_wrapper(wraper, func)
        return wraper
@HTML("body")
def body():
    """
        return body content!
    """
    return "Hello, body!"
print(body.__name__)
print(body.__doc__)
LOG: Baking Tag !
body

        return body content!
    

有趣的是 update_wrapper 的用法本身就很像是修飾器,因此 functools.wraps 就利用 functools.partial(還記得函數式編程中的偏應用吧!)將其變成一個修飾器:

from functools import update_wrapper, partial

def my_wraps(wrapped):
    return partial(update_wrapper, wrapped=wrapped)

def log(func):
    @my_wraps(func)
    def wraper():
        print("INFO: Starting {}".format(func.__name__))
        func()
        print("INFO: Finishing {}".format(func.__name__))
    return wraper

@log
def run():
    """
    Docs" of run
    """
    print("Running run...")
print(run.__name__)
print(run.__doc__)
run

    Docs" of run
    

歡迎關注公眾號 PyHub!

參考

Python修飾器的函數式編程

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/37841.html

相關文章

  • PyTips 0x02 - Python 中的函數式編程

    摘要:項目地址中的函數式編程函數式編程英語或稱函數程序設計,又稱泛函編程,是一種編程范型,它將電腦運算視為數學上的函數計算,并且避免使用程序狀態以及易變對象。 項目地址:https://git.io/pytips Python 中的函數式編程 函數式編程(英語:functional programming)或稱函數程序設計,又稱泛函編程,是一種編程范型,它將電腦運算視為數學上的函數計算,并且...

    FrozenMap 評論0 收藏0
  • PyTips 0x01 - 迭代器與生成器

    摘要:項目地址迭代器與生成器迭代器與生成器是中比較常用又很容易混淆的兩個概念,今天就把它們梳理一遍,并舉一些常用的例子。生成器前面說到創建迭代器有種方法,其中第三種就是生成器。 項目地址:https://git.io/pytips 迭代器與生成器 迭代器(iterator)與生成器(generator)是 Python 中比較常用又很容易混淆的兩個概念,今天就把它們梳理一遍,并舉一些常用的例...

    chemzqm 評論0 收藏0
  • 流暢的 Python - 5. 裝飾器與閉包

    摘要:看了這一章,發現原來是裝飾器,又一新知識。期間,裝飾器會做一些額外的工作。書中介紹了模塊中的三個裝飾器。另一個是,這個裝飾器把函數結果保存了起來,避免傳入相同參數時重復計算。疊放不奇怪,裝飾器返回的就是函數或可調用對象。 在 Web 框架 Flask 中,最常看到的或許是以@app.route開頭的那行代碼。由于還是剛接觸 Flask,所以對這種語法還不熟悉。看了這一章,發現原來是裝飾...

    Markxu 評論0 收藏0
  • PyTips 0x0d - Python 上下文管理器

    摘要:項目地址引入了語句與上下文管理器類型,其主要作用包括保存重置各種全局狀態,鎖住或解鎖資源,關閉打開的文件等。了解了語句的執行過程,我們可以編寫自己的上下文管理器。生成器的寫法更簡潔,適合快速生成一個簡單的上下文管理器。 項目地址:https://git.io/pytips Python 2.5 引入了 with 語句(PEP 343)與上下文管理器類型(Context Manager ...

    yuxue 評論0 收藏0
  • PyTips 0x17-Python 中的枚舉類型

    摘要:中的枚舉類型枚舉類型可以看作是一種標簽或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期月份狀態等。 Python 中的枚舉類型 枚舉類型可以看作是一種標簽或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、狀態等。Python 的原生類型(Built-in types)里并沒有專門的枚舉類型,但是我們可以通過很多方法來實現它,例如字典、類等: WEEKD...

    yedf 評論0 收藏0

發表評論

0條評論

dingding199389

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<