最近浏览python官方文档中的new feature中看到一些新的功能,其中有几个功能是非常棒的,在使用python时可能会经常使用。因此进行测试以及记录。其中就包括singledispatch,这个功能好比c++面向对象中,可以存在多个同名的方法,可以根据不同类型的参数自己选择适配的方法。也可以称为范型,重载等。在很多的编程语言中都存在这样的情况,比如swift语言中也类似,通过函数的参数类型和返回类型确定一个函数(函数签名),在函数调用时根据参数的类型选择适配的函数。

装饰器singledispatch

该装饰器在python3.7中已经存在了,主要是对函数实现范型,官方的描述为:将一个函数转换为 单分派 generic function。要定义一个泛型函数,应使用 @singledispatch 装饰器进行装饰。但是必须注意的是当函数有多个参数时,只作用于第一个参数。下面通过示例观察:

from functools import singledispatch

@singledispatch
def func(x: int, verbose: bool = True):
    if verbose:
        print("The param 'x' type: ", type(x))
    print("\t", x)

@func.register
def _(x: list, verbose: bool = True):
    if verbose:
        print("The param 'x' type: ", type(x))
    for _x in x:
        print("\t", _x)

@func.register()
def _(x, verbose: bool = True):
    if verbose:
        print("The param 'x' type: ", type(x))
    for _k, _x in x.items():
        print("\t", _k, _x)

def reg(x: str, verbose: bool = False):
    if verbose:
        print(print("The param 'x' type: ", type(x)))
    print("---------- register -------------")
    print("\t", x)

if __name__ == '__main__':
    func.register(reg)
    for p in (10, [101, 102, 103], {"a": 99, "b": 98, "c": 97}, "Hello World!"):
        func(p)

# output
The param 'x' type:  <class 'int'>
         10
The param 'x' type:  <class 'list'>
         101
         102
         103
The param 'x' type:  <class 'dict'>
         a 99
         b 98
         c 97
---------- register -------------
         Hello World!

可以看出,不同的参数类型,选择的函数不同。实现方式如上,通过装饰器的方式进行。在编码中通过register来实现分配。在python3.8中对该方法进行了优化,在register中可以传入类型参数,如下:

@func.register(dict)
def _(x, verbose: bool = True):
    if verbose:
        print("The param 'x' type: ", type(x))
    for _k, _x in x.items():
        print("\t", _k, _x)

def reg(x, verbose: bool = False):
    if verbose:
        print(print("The param 'x' type: ", type(x)))
    print("---------- register -------------")
    print("\t", x)

func.register(str, reg)

装饰器singledispatchmethod

该装饰器主要是针对类下面的方法实现单分配,实现和上面singledispatch一样的功能。但是在python3.8中支持对classmethodstaticmethodabstractmethod等等的嵌套。但是singledispatchmethod必须在最外层进行装饰。必须注意的是,该装饰器也是根据非selfclass的第一个参数有效。下面是对它的测试:

class MyClass(object):
    @singledispatchmethod
    def singe(self, args, verbose=1):
        if verbose:
            print("The args type: ", type(args))
        raise NotImplementedError

    @singe.register
    def _(self, args: int, verbose=1):
        if verbose:
            print("The args type: ", type(args))
        print("\t", args)

    @singe.register
    def _(self, args: dict, verbose=0):
        if verbose:
            print("The args type: ", type(args))
        for _k, _x in args.items():
            print("\t", _k, _x)

c = MyClass()
for p in (10, {"a": 99, "b": 98, "c": 97}, [101, 102, 103]):
    c.singe(p)
# output
The args type:  <class 'int'>
     10
     a 99
     b 98
     c 97
The args type:  <class 'list'>
raise NotImplementedError

类属性缓存cached_property

该方法在很早以前就已经实现,但是都未纳入到标准库中,该装饰器主要实现类中属性的缓存。再未纳入标准库之前就有很多实现方法,如函数装饰器,类装饰器等等。现在使用只需要从functools中导入cached_property即可。


Python      python3 装饰器

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

【转载】机器学习中样本失衡下的分类评估指标选取指南 下一篇