函数装饰器和闭包(四)

上一章:函数装饰器和闭包(三)

单分派函数

假设我们现在要开发一个函数,这个函数可以传入一个元素,函数要判断元素的类型,再将其打印出来

from collections.abc import MutableSequence, MutableMapping


def print_item(item):
    if isinstance(item, int):
        print("int:", item)
    elif isinstance(item, MutableSequence):
        print("iter:", item)
    elif isinstance(item, MutableMapping):
        print("map:", item)
    else:
        print(item)

  

运行结果:

>>> print_item(1)
int: 1
>>> print_item([1, 2, 3])
sequence: [1, 2, 3]
>>> print_item({"a": 1, "b": 2})
map: {'a': 1, 'b': 2}

  

这个函数很好理解,没有什么好解释的,但是我们要通过这个函数来展示如何使用functools.singledispatch装饰器让Python实现“重载”的效果

from collections.abc import MutableSequence, MutableMapping
from functools import singledispatch


@singledispatch  # <1>
def print_item(item):
    print(item)


@print_item.register(int)  # <2>
@print_item.register(float)
def _(item):
    print("number:", item)


@print_item.register(str)  # <3>
def _(item):
    print("str:", item)


@print_item.register(MutableSequence)  # <4>
def _(item):
    print("sequence:", item)


@print_item.register(MutableMapping)  # <5>
def _(item):
    print("map:", item)

     

我们用@singledispatch装饰了print_item函数,把这个函数作为基函数,然后再用@print_item.register装饰其他函数,当我们调用print_item,会根据item的类型查找应该执行的函数

  1. @singledispatch标记处理object类型的基函数
  2. 当传入参数为int类型进入此方法,这个时候专门的函数名称已经无关紧要了,我们用_代替,同时可以用多个@print_item.register装饰同一个函数
  3. 当传入参数为str类型进入此方法
  4. 当传入参数为序列类型进入此方法
  5. 当传入参数为字典类型进入此方法

  

运行结果:

>>> print_item(1)
number: 1
>>> print_item(1.1)
number: 1.1
>>> print_item("hello")
str: hello
>>> print_item([1, 2, 3])
sequence: [1, 2, 3]
>>> print_item({"a": 1, "b": 2})
map: {'a': 1, 'b': 2}

 

参数化装饰器

Python在解释装饰器时,把被装饰的函数当做第一个参数传递给装饰器函数,但怎么样才能让装饰器接收其他参数呢?答案是:创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。可能这样的解释听得云里雾里,没关系,我们来看下面一个例子

registry = set()  # <1>


def register(active=True):  # <2>
    def decorate(func):  # <3>
        print('running register(active=%s)->decorate(%s)' % (active, func))
        if active:  # <4>
            registry.add(func)
        else:
            registry.discard(func)  # <5>

        return func  # <6>

    return decorate  # <7>


@register(active=False)  # <8>
def f1():
    print('running f1()')


@register()  # <9>
def f2():
    print('running f2()')


def f3():
    print('running f3()')

  

  1. 声明一个名为registry的集合对象
  2. register方法接收一个可选的关键字参数
  3. decorate这个内部函数才是真正的装饰器,注意它只有一个参数,而且接收的是函数
  4. 只有active的值为真,才将func注册到registry集合中
  5. 如果active的值不为真,则将func从registry集合删除
  6. decorate是装饰器,必须返回一个函数
  7. register是装饰器工厂函数,因此返回decorate
  8. @register工厂函数必须作为函数调用,并且传入所需的参数
  9. 即时不传入参数,register也必须作为函数调用

我们导入这个文件:

>>> from registration_param import *
running register(active=False)->decorate(<function f1 at 0x00000072D8DA4620>)
running register(active=True)->decorate(<function f2 at 0x00000072D8DA46A8>)
>>> registry
{<function f2 at 0x00000072D8DA46A8>}

  

可以看到,当把register作为装饰器工厂函数,返回的decorate函数得到了执行,由于f1传入的active为False,所以f1没有注册到registry集合对象里面,只有f2被注册到集合对象,我们打印registry,可以看到,里面确实只有f2函数对象

 

posted @ 2018-07-14 21:12  北洛  阅读(188)  评论(0编辑  收藏  举报