PyTips 0x0f
项目地址: https://git.io/pytips
Python 的修饰器是一种语法糖(Syntactic Sugar),也就是说:
@decorator@wrapdef func(): pass
是下面语法的一种简写:
def func(): passfunc = decorator(wrap(func))
关于修饰器的两个主要问题:
-
修饰器用来修饰谁
-
谁可以作为修饰器
修饰函数
修饰器最常见的用法是修饰新定义的函数,在 0x0d 上下文管理器 中提到上下文管理器主要是为了 更优雅地完成善后工作 ,而修饰器通常用于扩展函数的行为或属性:
def log(func): def wraper(): print("INFO: Starting {}".format(func.__name__)) func() print("INFO: Finishing {}".format(func.__name__)) return wraper@logdef run(): print("Running run...")run()
INFO: Starting runRunning run...INFO: Finishing run
修饰类
除了修饰函数之外,Python 3.0 之后增加了对新定义类的修饰( PEP 3129 ),但是对于类别属性的修改可以通过 Metaclasses 或继承来实现,而新增加的类别修饰器更多是出于 Jython 以及 IronPython 的考虑,但其语法还是很一致的:
from time import sleep, timedef timer(Cls): def wraper(): s = time() obj = Cls() e = time() print("Cost {:.3f}s to init.".format(e - s)) return obj return wraper@timerclass Obj: def __init__(self): print("Hello") sleep(3) print("Obj")o = Obj()
HelloObjCost 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}</{0}>".format(self.tag, func(), self.tag)@HTML("html")@HTML("body")@HTML("div")def body(): return "Hello"print(body())
LOG: Baking Tag <html>!LOG: Baking Tag <body>!LOG: Baking Tag <div>!<html><body><div>Hello</div></body></html>
传递参数
在实际使用过程中,我们可能需要向修饰器传递参数,也有可能需要向被修饰的函数(或类)传递参数。按照语法约定,只要修饰器 @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!{'/': <function index at 0x10706f730>, '/home': <function home at 0x10706f8c8>}
向被修饰的函数传递参数,要看我们的修饰器是如何作用的,如果像上面这个例子一样未执行被修饰函数只是将其原模原样地返回,则不需要任何处理(这就把函数当做普通的值一样看待即可):
@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@logdef run(hello = "world"): print("Hello {}".format(hello))run("Python")
INFO: Start LoggingHello PythonINFO: 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 <body>!<lambda>None
因为 body = HTML("body")(body) ,而 HTML("body").__call__() 返回的是一个 lambda 函数,因此 body 已经被替换成了 lambda ,虽然都是可执行的函数,但原来定义的 body 中的一些属性,例如 __doc__ / __name__ / __module__ 都被替换了(在本例中 __module__ 没变因为都在同一个文件中)。为了解决这一问题 Python 提供了 functools 标准库,其中包括了 update_wrapper 和 wraps 两个方法( 源码 )。其中 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}</{0}>".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>!body return body content!
有趣的是 update_wrapper 的用法本身就很像是修饰器,因此 functools.wraps 就利用 functools.partial (还记得函数式编程中的偏应用吧!)将其变成一个修饰器:
from functools import update_wrapper, partialdef 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@logdef run(): """ Docs' of run """ print("Running run...")print(run.__name__)print(run.__doc__)
run Docs' of run
欢迎关注公众号 PyHub!
参考
-
Python修饰器的函数式编程

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











이 기사는 HTML & lt; Progress & Gt에 대해 설명합니다. 요소, 그 목적, 스타일 및 & lt; meter & gt의 차이; 요소. 주요 초점은 & lt; progress & gt; 작업 완료 및 & lt; meter & gt; Stati의 경우

이 기사는 HTML & LT; Datalist & GT에 대해 논의합니다. 자동 완성 제안을 제공하고, 사용자 경험을 향상시키고, 오류를 줄임으로써 양식을 향상시키는 요소. 문자 수 : 159

이 기사는 HTML & lt; meter & gt에 대해 설명합니다. 범위 내에 스칼라 또는 분수 값을 표시하는 데 사용되는 요소 및 웹 개발의 일반적인 응용 프로그램. & lt; meter & gt; & lt; Progress & Gt; 그리고 Ex

기사는 HTML5 크로스 브라우저 호환성을 보장하기위한 모범 사례에 대해 논의하고 기능 감지, 점진적 향상 및 테스트 방법에 중점을 둡니다.

이 기사에서는 브라우저에서 직접 사용자 입력을 검증하기 위해 필요한, Pattern, Min, Max 및 Length 한계와 같은 HTML5 양식 검증 속성을 사용하는 것에 대해 설명합니다.

이 기사는 모바일 장치의 반응 형 웹 디자인에 필수적인 Viewport Meta Tag에 대해 설명합니다. 적절한 사용이 최적의 컨텐츠 스케일링 및 사용자 상호 작용을 보장하는 방법을 설명하는 반면, 오용은 설계 및 접근성 문제로 이어질 수 있습니다.

이 기사는 & lt; iframe & gt; 외부 컨텐츠를 웹 페이지, 공통 용도, 보안 위험 및 객체 태그 및 API와 같은 대안을 포함시키는 태그의 목적.

GiteEpages 정적 웹 사이트 배포 실패 : 404 오류 문제 해결 및 해결시 Gitee ...
