• Python--深入理解Python特性 第3章


    第3章 高效的函数

    3.1 函数是Python的头等对象

      Python中一切皆对象,函数也不例外。函数可以分配给变量或存储在数据结构中,还可以传递给其他函数或作为其他函数的返回值。

      函数可以嵌套,并且可以捕获并携带父函数的一些状态。具有这种行为的函数称作闭包

    # 工厂函数
    def make_adder(n):
        def add(x):
            # 这里引用了父函数的n,这种情况称为词法闭包(lexical closure)
            return x + n
        return add
    
    
    plus_3 = make_adder(3)
    plus_5 = make_adder(5)
    
    print(plus_3(1))
    print(plus_5(1))
    View Code

      对象可以设置为可调用的,因此很多情况下可以将对象作为函数对待。

    class Adder:
        def __init__(self, n):
            self.n = n
    
        def __call__(self, x):
            # 实现此方法,则类的实列对象可当中函数调用
            return self.n + x
    
    
    plus_3 = Adder(3)
    plus_5 = Adder(5)
    # plus_3和plus_5实际是Adder的两个实例对象,
    # 这里直接把它们当作函数调用,是因为Adder实现了__call__方法
    print(plus_3(1))
    print(plus_5(1))
    View Code

    3.2 lambda是单表达式函数

      lambda函数不必与名称绑定,因此常作为匿名函数。

    # 工厂函数
    def make_adder(n):
        # 使用lambda匿名函数形成了一个闭包
        return lambda x: x + n
    
    
    plus_3 = make_adder(3)
    plus_5 = make_adder(5)
    
    print(plus_3(1))
    print(plus_5(1))
    View Code

      lambda函数不能使用普通的Python语句,因为lambda会计算其唯一的表达式,并隐式的把计算结果返回。

      不要过度使用lambda函数,使用前请先问问自己:使用普通具名函数或者列表解析式是否更加清晰。

    # 错误的案例
    class Car:
        # rev和crash应该使用常规的函数定义方法def来声明函数,
        # 那样会更清晰
        rev = lambda self: print('Wroom!')
        crash = lambda self: print('Boom!')
    
    
    car = Car()
    car.crash()
    
    # 错误的案例
    l_list = list(filter(lambda x: x % 2 == 0, range(16)))
    # 正确的案例
    l_list = [x for x in range(16) if x % 2 == 0]
    
    # 注:尽可能在表达式清晰简单的时候才使用lambda函数
    # 少敲一些代码并不重要,重要的是能够让代码清晰可读
    View Code

    3.3 装饰器的力量

      装饰器用于定义可重用的组件,可以将其应用于可调用对象以修改其行为,同时无须永久修改可调用对象本身。

      @语法只是在输入函数上调用装饰器的简写,不过@语法会在定义时就立即修饰该函数。在单个函数上应用多个装饰器的顺序是从底部到顶部(装饰器栈)

    def decorator(func):
        print(f'func:{func.__name__}被装饰了')
        return func
    
    
    # 方式1
    # def greet():
    #     print('in greet')
    #     return 'Hello'
    
    
    # 方式2 使用@进行装饰
    @decorator
    def greet():
        print('in greet')
        return 'Hello'
    
    
    print('开始装饰')
    # 方式1 直接调用装饰函数
    # greet = decorator(greet)
    
    greet()
    
    ''' 方式1 输出结果
    开始装饰
    func:greet被装饰了
    in greet
    '''
    # 使用@进行装饰时,在定义的时候就会立即调用
    # 方式1可以把装饰结果赋值给其他变量,这样就可以保留原函数的原滋原味
    ''' 方式2 输出结果
    func:greet被装饰了
    开始装饰
    in greet
    '''
    View Code

      最好在自己的装饰器中使用functools.wraps将被装饰对象中的元数据转移到装饰后的对象中。

    import functools
    
    
    def upper_decorator(func):
        # 使用functools.wraps可把func的元数据移到wrapper中
        # @functools.wraps(func)   # 1
        def wrapper():
            return func().upper()
        return wrapper
    
    
    @upper_decorator
    def greet():
        """这是greet的注释"""
        return 'Hello'
    
    
    print(greet.__name__)
    print(greet.__doc__)
    
    ''' 注释1处代码 输出结果
    wrapper
    None
    '''
    ''' 不注释1处代码 输出结果
    greet
    这是greet的注释
    '''
    View Code

      使用*和** 参数解包操作符完成带参函数的装饰

    def trace(func):
        def wrapper(*args, **kwargs):
            print(f'Trace: calling {func.__name__}() '
                  f'with {args}, {kwargs}')
            original_result = func(*args, **kwargs)
            print(f'Trace: {func.__name__}() '
                  f'returned {original_result}')
            return original_result
        return wrapper
    
    
    @trace
    def say(name, line):
        return f'{name}: {line}'
    
    
    result = say('Jane', line='Welcome')
    print(result)
    
    
    ''' 输出结果
    Trace: calling say() with ('Jane',), {'line': 'Welcome'}
    Trace: say() returned Jane: Welcome
    Jane: Welcome
    '''
    View Code

      装饰器不是万能的,容易产生可怕且不可维护的代码,不应过度使用。

    3.4 有趣的*args和**kwargs

      *args和**kwargs 用于在Python中编写变长参数的函数

      *args收集额外的位置参数组成元组。**kwargs收集额外的关键字参数组成字典。

      实际起作用的语法是*和**,args和kwargs只是命名约定,也可以使用其他名称。建议遵循命名约定。

    3.5 函数参数解包

      *可用于将元组、列表和生成器等序列解包为位置参数。

      如果使用*解包字典,则所有的键将以随机的顺序传递给函数。(我试了下,按key定义顺序输出,不是随机,用的python3.6,不知道是不是跟python版本有关)

    dic_t = {'b': 1, 'a': 2, 'c': 3}
    print(*dic_t)
    ''' 输出结果
    b a c
    '''
    View Code

      **可用于将字典解包为关键字参数。

    3.6 返回空值

      如果函数没有明确的return,则函数返回None。

      return、return None 或者不写return语句效果是一样的,返回的都是None。

      如果函数本身确实不用返回值,可以不显示的写return语句。

      如果函数有返回值,某些情况返回None,这种情况下,建议最后都显示的return None,可以使代码意图更明确。

  • 相关阅读:
    属性 Owner 不可用于 数据库...
    DHCP Client 服务无法启动,错误代码5:访问被拒绝
    删除域控中不活动的计算机
    在VMWare下LINUX中安装VMTool及共享文件夹
    ID 13508
    系统时间同步服务器地址收集
    windows 2003 登陆框 黑色解决办法
    linux下限制su权限
    linux screen + vim + taglist +ctags 使用
    gmail要求启用ActiveX控件,以及人人网没法分享的问题
  • 原文地址:https://www.cnblogs.com/yarightok/p/15202150.html
Copyright © 2020-2023  润新知