函数的使用方式

  • 将函数视为“一等公民”

    • 函数可以赋值给变量
    • 函数可以作为函数的参数
    • 函数可以作为函数的返回值
  • 高阶函数的用法(filtermap以及它们的替代品)

    1. items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10))))
    2. items2 = [x ** 2 for x in range(1, 10) if x % 2]
  • 位置参数、可变参数、关键字参数、命名关键字参数

  • 参数的元信息(代码可读性问题)

  • 匿名函数和内联函数的用法(lambda函数)

  • 闭包和作用域问题

    • Python搜索变量的LEGB顺序(Local >>> Embedded >>> Global >>> Built-in)

    • globalnonlocal关键字的作用

      global:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)。

      nonlocal:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。

  • 装饰器函数(使用装饰器和取消装饰器)

    例子:输出函数执行时间的装饰器。

    1. def record_time(func):
    2. """自定义装饰函数的装饰器"""
    3. @wraps(func)
    4. def wrapper(*args, **kwargs):
    5. start = time()
    6. result = func(*args, **kwargs)
    7. print(f'{func.__name__}: {time() - start}秒')
    8. return result
    9. return wrapper

    如果装饰器不希望跟print函数耦合,可以编写可以参数化的装饰器。

    ```Python from functools import wraps from time import time

def record(output): “””可以参数化的装饰器”””

  1. def decorate(func):
  2. @wraps(func)
  3. def wrapper(*args, **kwargs):
  4. start = time()
  5. result = func(*args, **kwargs)
  6. output(func.__name__, time() - start)
  7. return result
  8. return wrapper
  9. return decorate
  1. ```Python
  2. from functools import wraps
  3. from time import time
  4. class Record():
  5. """通过定义类的方式定义装饰器"""
  6. def __init__(self, output):
  7. self.output = output
  8. def __call__(self, func):
  9. @wraps(func)
  10. def wrapper(*args, **kwargs):
  11. start = time()
  12. result = func(*args, **kwargs)
  13. self.output(func.__name__, time() - start)
  14. return result
  15. return wrapper

说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过func.__wrapped__方式获得被装饰之前的函数或类来取消装饰器的作用。

例子:用装饰器来实现单例模式。

  1. from functools import wraps
  2. def singleton(cls):
  3. """装饰类的装饰器"""
  4. instances = {}
  5. @wraps(cls)
  6. def wrapper(*args, **kwargs):
  7. if cls not in instances:
  8. instances[cls] = cls(*args, **kwargs)
  9. return instances[cls]
  10. return wrapper
  11. @singleton
  12. class President:
  13. """总统(单例类)"""
  14. pass

提示:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?

线程安全的单例装饰器。

  1. from functools import wraps
  2. from threading import RLock
  3. def singleton(cls):
  4. """线程安全的单例装饰器"""
  5. instances = {}
  6. locker = RLock()
  7. @wraps(cls)
  8. def wrapper(*args, **kwargs):
  9. if cls not in instances:
  10. with locker:
  11. if cls not in instances:
  12. instances[cls] = cls(*args, **kwargs)
  13. return instances[cls]
  14. return wrapper

提示:上面的代码用到了with上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持__enter____exit__魔术方法)。在wrapper函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。