Python核心技术与实战
景霄
Facebook资深工程师
立即订阅
13891 人已学习
课程目录
已完结 46 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 从工程的角度深入理解Python
免费
基础篇 (14讲)
01 | 如何逐步突破,成为Python高手?
02 | Jupyter Notebook为什么是现代Python的必学技术?
03 | 列表和元组,到底用哪一个?
04 | 字典、集合,你真的了解吗?
05 | 深入浅出字符串
06 | Python “黑箱”:输入与输出
07 | 修炼基本功:条件与循环
08 | 异常处理:如何提高程序的稳定性?
09 | 不可或缺的自定义函数
10 | 简约不简单的匿名函数
11 | 面向对象(上):从生活中的类比说起
12 | 面向对象(下):如何实现一个搜索引擎?
13 | 搭建积木:Python 模块化
14 | 答疑(一):列表和元组的内部实现是怎样的?
进阶篇 (11讲)
15 | Python对象的比较、拷贝
16 | 值传递,引用传递or其他,Python里参数是如何传递的?
17 | 强大的装饰器
18 | metaclass,是潘多拉魔盒还是阿拉丁神灯?
19 | 深入理解迭代器和生成器
20 | 揭秘 Python 协程
21 | Python并发编程之Futures
22 | 并发编程之Asyncio
23 | 你真的懂Python GIL(全局解释器锁)吗?
24 | 带你解析 Python 垃圾回收机制
25 | 答疑(二):GIL与多线程是什么关系呢?
规范篇 (7讲)
26 | 活都来不及干了,还有空注意代码风格?!
27 | 学会合理分解代码,提高代码可读性
28 | 如何合理利用assert?
29 | 巧用上下文管理器和With语句精简代码
30 | 真的有必要写单元测试吗?
31 | pdb & cProfile:调试和性能分析的法宝
32 | 答疑(三):如何选择合适的异常处理方式?
量化交易实战篇 (8讲)
33 | 带你初探量化世界
免费
34 | RESTful & Socket: 搭建交易执行层核心
35 | RESTful & Socket: 行情数据对接和抓取
36 | Pandas & Numpy: 策略与回测系统
免费
37 | Kafka & ZMQ:自动化交易流水线
38 | MySQL:日志和数据存储系统
39 | Django:搭建监控平台
40 | 总结:Python中的数据结构与算法全景
技术见闻与分享 (4讲)
41 | 硅谷一线互联网公司的工作体验
42 | 细数技术研发的注意事项
加餐 | 带你上手SWIG:一份清晰好用的SWIG编程实践指南
43 | Q&A:聊一聊职业发展和选择
结束语 (1讲)
结束语 | 技术之外的几点成长建议
Python核心技术与实战
登录|注册

17 | 强大的装饰器

景霄 2019-06-17
你好,我是景霄。这节课,我们一起来学习装饰器。
装饰器一直以来都是 Python 中很有用、很经典的一个 feature,在工程中的应用也十分广泛,比如日志、缓存等等的任务都会用到。然而,在平常工作生活中,我发现不少人,尤其是初学者,常常因为其相对复杂的表示,对装饰器望而生畏,认为它“too fancy to learn”,实际并不如此。
今天这节课,我会以前面所讲的函数、闭包为切入点,引出装饰器的概念、表达和基本用法,最后,再通过实际工程中的例子,让你再次加深理解。
接下来,让我们进入正文一起学习吧!

函数 -> 装饰器

函数核心回顾

引入装饰器之前,我们首先一起来复习一下,必须掌握的函数的几个核心概念。
第一点,我们要知道,在 Python 中,函数是一等公民(first-class citizen),函数也是对象。我们可以把函数赋予变量,比如下面这段代码:
def func(message):
print('Got a message: {}'.format(message))
send_message = func
send_message('hello world')
# 输出
Got a message: hello world
这个例子中,我们把函数 func() 赋予了变量 send_message,这样之后你调用 send_message,就相当于是调用函数 func()。
第二点,我们可以把函数当作参数,传入另一个函数中,比如下面这段代码:
def get_message(message):
return 'Got a message: ' + message
def root_call(func, message):
print(func(message))
root_call(get_message, 'hello world')
# 输出
Got a message: hello world
这个例子中,我们就把函数 get_message() 以参数的形式,传入了函数 root_call() 中然后调用它。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Python核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(63)

  • Wing·三金
    老师能否补充下,用 @functools.wraps(func) 来保留原来的元信息,有哪些现实意义呢?
    2019-06-17
    4
    29
  • 程序员人生
    我感觉python的装饰器的应用场景有点像AOP的应用场景,把一些常用的业务逻辑分离,提高程序可重用性,降低耦合度,提高开发效率。

    作者回复: 是的,你的理解很正确

    2019-06-17
    19
  • 三水
    请教前辈们或老师一个初入门的问题:在本方前部"函数回顾"中,把函数赋给一个变量时,
    第1点:send_message = func
    第4点:send_message = func_closure()

    我尝试如下会报错:
    第1点:send_message = func()
    第4点:send_message = func_closure

    想知道这是哪一个知识点,谢谢

    作者回复: 第一点:
    直接赋值send_message = func()是错误的,因为func()必须接受一个参数,send_message = func('hello world')就正确了,他等同于send_message = func然后send_message('hello world')

    第4点:func_closure()是一个闭包,返回的是函数对象。不能直接用send_message = func_closure,然后send_message('hello world')调用,必须是send_message = func_closure(),然后再send_message('hello world'),这样才能把参数'hello world'正确传给内部函数

    2019-06-17
    18
  • Hector
    lru cache常用来做一些小规模缓存,比如最近浏览记录,空间浏览记录等等,常用三种策略:1.FIFO(先进先出)2.最少使用LRU 3.最近最少使用LRU. 看了下源码,原来python原生的functools中的lru是链表写的
    2019-06-17
    8
  • farFlight
    请问一下,lru cache不是应该删除最久没有访问的内容吗。

    作者回复: LRU cache is to remove the least recently used data when the cache is full。翻译过来可能有点问题,意思就是删除最久没有访问的,我还是直接保留英文解释吧。

    2019-06-17
    5
  • 🇨🇳
    1、总结中,倒数第二行发现错别字(程序)不是程度。
    2、类装饰器在实际中有哪些应用场景呢

    作者回复: 欢迎指正错别字。类装饰器的用途和函数装饰器差不多,比如文中所讲的机器学习中需要对输入进行合理性检查,他也常常可以写成类装饰器的形式,进行调用。写成类的话,优点是程序的分解度更加高,具体用类装饰器和函数装饰器,视情况而定,二者本质是一样的

    2019-06-17
    1
    4
  • enjoylearning
    还有类装饰器,又长见识了,最近正愁参数校验放哪里,参照本文终于开窍了

    作者回复: 很高兴看到你有所收获

    2019-06-17
    4
  • 吴星
    请教下,为什么count那儿是单例模式吗?为什么二次执行会加1?

    作者回复: 因为num_calls这个变量是类变量,不是具体的实例变量,二次执行相当于调用了函数__call__两次,因此变量num_calls会变为2

    2019-06-17
    4
  • 一叶知秋
    平时似乎也就property、staticmethod、classmethod用的比较多一点
    2019-06-18
    3
  • GentleCP
    老师,装饰器嵌套的时候,执行顺序不是decorator1->decorator2->func吗,应该是从外到内吧,外层的装饰器先执行,打印结果是
    decorator1
    decorator2
    hello world
    2019-06-18
    3
    3
  • 岁月婧好
    “原函数还是原函数吗?”一栏中“为了解决这个问题,我们通常使用内置的装饰器@functools.wrap“,应该是@functools.wraps,有s的吧
    2019-07-08
    2
  • 被炸的油条
    工作当中,如果是二次开发,在原来的需求基础之上做优化,原逻辑不需要修改的情况下,只需增加新的业务场景的时候,感觉用装饰器挺好的。不动原来的逻辑,增加程序的健壮性。

    作者回复: 正解

    2019-09-22
    1
  • A.Windy
    装饰器完全可以转换为方法调用:
    import functools
    def my_decorator(param):
        def inner(func):
            @functools.wraps(func) #保留函数的元信息
            def wrapper(*args, **kwargs):
                print("before func run. {}".format(param))
                func(*args, **kwargs)
                print("after func run. {}".format(param))
            return wrapper
        return inner
    @my_decorator("装饰器参数形式")
    def test(p, p1):
        print("i am func with param.{}, {}".format(p, p1))
    # 装饰器方式
    test("decorator", "p22")

    # 函数定义方式,等价于装饰器
    def test1(p, p1):
        print("i am func 函数调用 param.{}, {}".format(p, p1))
    f = my_decorator("函数参数形式")(test1)
    f("func call", "p2")
    #
    print(test, f)
    输出:
    before func run. 装饰器参数形式
    i am func with param.decorator, p22
    after func run. 装饰器参数形式
    before func run. 函数参数形式
    i am func 函数调用 param.func call, p2
    after func run. 函数参数形式
    <function my_decorator.<locals>.inner.<locals>.wrapper at 0x10a7107b8> <function my_decorator.<locals>.inner.<locals>.wrapper at 0x10a710bf8>
    2019-07-11
    1
  • magician
    api权限验证,缓存,日志,api运行时间
    2019-06-30
    1
  • 峥嵘
    请问老师,在“输入合理性检查“部分,为什么装饰器validation_check的参数不是func?
    谢谢老师

    def validation_check(input):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            ... # 检查输入是否合法
        
    @validation_check
    def neural_network_training(param1, param2, ...):
        ...
    2019-06-23
    1
    1
  • 不实用,代码可读性太低
    2019-06-20
    1
  • 木木杰
    没看懂啊,是我的问题吗?😂
    2019-06-19
    1
    1
  • ALAN
    老师,您好,locked_cached_property是类,它没有__call__方法,但是却能用来装饰另一个方法(name),这是为什么了?以下是代码,来自flask源码

    class locked_cached_property(object):
        """A decorator that converts a function into a lazy property. The
        function wrapped is called the first time to retrieve the result
        and then that calculated result is used the next time you access
        the value. Works like the one in Werkzeug but has a lock for
        thread safety.
        """

        def __init__(self, func, name=None, doc=None):
            self.__name__ = name or func.__name__
            self.__module__ = func.__module__
            self.__doc__ = doc or func.__doc__
            self.func = func
            self.lock = RLock()

        def __get__(self, obj, type=None):
            if obj is None:
                return self
            with self.lock:
                value = obj.__dict__.get(self.__name__, _missing)
                if value is _missing:
                    value = self.func(obj)
                    obj.__dict__[self.__name__] = value
                return value

    @locked_cached_property
        def name(self):
            """The name of the application. This is usually the import name
            with the difference that it's guessed from the run file if the
            import name is main. This name is used as a display name when
            Flask needs the name of the application. It can be set and overridden
            to change the value.
            .. versionadded:: 0.8
            """
            if self.import_name == "__main__":
                fn = getattr(sys.modules["__main__"], "__file__", None)
                if fn is None:
                    return "__main__"
                return os.path.splitext(os.path.basename(fn))[0]
            return self.import_name

    2019-06-18
    1
    1
  • upempty
    老师好,num_calls不是实例属性?example实例对象一次,call两次实例属性num_calls得到2。谢谢!
    2019-06-17
    1
  • Geek_59f23e
    1. 在类装饰器那一节中,’每当你调用一个类的示例时‘,应该是类的实例吧。
    另外这里还是有点疑问,类装饰器被调用两次时self.num_calls这个变量不是实例变量么,第二次调用时为什么没有生成新的实例,同时把之前的实例变量清空呢?
    2. 输入合理性检查一节中,应该是@functools.wraps(input)吧,最后还要加上return wrapper。
    3. 缓存一节中,代码头加上from functools import lru_cache可能更容易理解些。

    4. 感觉装饰器和中间件很像,都是一层包一层的堆栈结构,框架好像都少不了使用闭包装饰器。
    2019-06-17
    1
收起评论
63
返回
顶部