datetime:2019/5/16 13:23
author:nzb

函数的使用方式

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

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

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

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

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

  • 闭包和作用域问题

    • 闭包

      函数内的属性,都是有生命周期,都是在函数执行期间

      内部函数对外部函数作用域变量的引用

      闭包内的闭包函数私有化了变量,完成了数据的封装,类似面向对象

    • 作用域

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

      • globalnonlocal关键字的作用

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

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

  • 装饰器函数(使用装饰器和取消装饰器)语法糖 @

    最简单的例子:

def func1(func):  # 外部闭包函数的参数是被装饰的函数对象
    def func2():
        print('aaabbb')
        return func()  # 返回了外部函数接收的被装饰函数的调用

    return func2


# return func # 返回了函数对象
# return func() # 返回的是一个函数调用

# func1(myfunc)() # 接收别装饰的函数作为参数,而且还要继续调用一次
# func2() -> print('aaabbb') -> return myfunc()

@func1
def myfunc():
    print('你好')
    # 不影响原有函数的功能,还能添加新的功能


myfunc()  # func1(myfunc)()

装饰器函数带参数(与下面一样)多一层包装来接收装饰器的参数

def arg_func(sex):
    def func1(b_func):
        def func2():
            if sex == 'man':
                print('你是男士')
            if sex == 'woman':
                print('你是女士')
            return b_func()

        return func2

    return func1


@arg_func(sex='man')
def man():
    print('好好上班')


@arg_func(sex='woman')
def woman():
    print('好好上班')


man()
woman()

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

def record_time(func):
    """自定义装饰函数的装饰器"""

    @wraps(func)
    def wrapper(*args, **kwargs):  # 被装饰的函数带参数(最常见)
        start = time()
        result = func(*args, **kwargs)  # 被装饰的函数带参数(最常见)
        print(f'{func.__name__}: {time() - start}秒')
        return result

    return wrapper

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

from functools import wraps
from time import time


def record(output):
    """自定义带参数的装饰器"""

    def decorate(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start = time()
            result = func(*args, **kwargs)
            output(func.__name__, time() - start)
            return result

        return wrapper

    return decorate
from functools import wraps
from time import time


class Record():
    """自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""

    def __init__(self, output):
        self.output = output

    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start = time()
            result = func(*args, **kwargs)
            self.output(func.__name__, time() - start)
            return result

        return wrapper

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

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

from functools import wraps


def singleton(cls):
    """装饰类的装饰器"""
    instances = {}

    @wraps(cls)
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return wrapper


@singleton
class President():
    """总统(单例类)"""
    pass

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

from functools import wraps


def singleton(cls):
    """线程安全的单例装饰器"""
    instances = {}
    locker = Lock()

    @wraps(cls)
    def wrapper(*args, **kwargs):
        if cls not in instances:
            with locker:
                if cls not in instances:
                    instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return wrapper
  • 装饰器模板

    装饰器的作用:装饰器即可以装饰函数也可以装饰类。
    装饰器的原理:函数也是对象

    • 使用装饰器:假设decorator是定义好的装饰器。

      • 方法一:不用@符号

              # 装饰器不传入参数时
              f = decorator(函数名)
              # 装饰器传入参数时
              f = (decorator(参数))(函数名) 或 decorator(参数)(函数名)
        
      • 方法二:使用@符号

              # 已定义的装饰器
              @decorator
              def f():
                  pass
        
              # 执行被装饰过的函数
              f()
        

results matching ""

    No results matching ""