Python之functools模块的使用

 functools模块的作用

functools模块提供了一些工具来调整或扩展函数和其他callable对象,从而不必完全重写。

1、functools.partial,给函数默认传参,再使用函数一样的调用方式的示例

import functools

def myfunc(a, b=2):
    '''
        定义myfunc函数,打印变量值
    '''
    print('  called myfunc with:', (a, b))

def show_details(name, f, is_partial=False):
    "传入函数名和函数的对象,判断是否使用functools工具,进行参数的打印调用"
    print('{}:'.format(name))
    print('  object:', f)
    if not is_partial:
        print('  __name__:', f.__name__)
    if is_partial:
        print('  func:', f.func)
        print('  args:', f.args)
        print('  keywords:', f.keywords)
    return

show_details('myfunc', myfunc)
myfunc('a', 3)

# 运行结果
# myfunc:
#   object: <function myfunc at 0x000001E15CD045E8>
#   __name__: myfunc
#   called myfunc with: ('a', 3)
print()

p1 = functools.partial(myfunc, b=4)
show_details('partial with named default', p1, True)
# 运行结果
# partial with named default:
#   object: functools.partial(<function myfunc at 0x000002720F1D4678>, b=4)
#   func: <function myfunc at 0x000002720F1D4678>
#   args: ()
#   keywords: {'b': 4}

# 直接使用functools修饰一个函数,让调用变得更加简单
p1('passing a')
p1('override b', b=5)
print()

p2 = functools.partial(myfunc, 'default a', b=99)
show_details('partial with defaults', p2, True)
# 运行结果
# partial with defaults:
#   object: functools.partial(<function myfunc at 0x000002838A8445E8>, 'default a', b=99)
#   func: <function myfunc at 0x000002838A8445E8>
#   args: ('default a',)
#   keywords: {'b': 99}

p2()
# called myfunc with: ('default a', 99)

p2(b='override b')
# called myfunc with: ('default a', 'override b')

print()

print('参数缺少')
p1()
# TypeError: myfunc() missing 1 required positional argument: 'a'
functools_partial.py

运行效果

TypeError: myfunc() missing 1 required positional argument: 'a'
myfunc:
  object: <function myfunc at 0x000001F5877045E8>
  __name__: myfunc
  called myfunc with: ('a', 3)

partial with named default:
  object: functools.partial(<function myfunc at 0x000001F5877045E8>, b=4)
  func: <function myfunc at 0x000001F5877045E8>
  args: ()
  keywords: {'b': 4}
  called myfunc with: ('passing a', 4)
  called myfunc with: ('override b', 5)

partial with defaults:
  object: functools.partial(<function myfunc at 0x000001F5877045E8>, 'default a', b=99)
  func: <function myfunc at 0x000001F5877045E8>
  args: ('default a',)
  keywords: {'b': 99}
  called myfunc with: ('default a', 99)
  called myfunc with: ('default a', 'override b')

参数缺少

2、更新被functools.partial装饰过的的内置属性值

import functools


def myfunc(a, b=2):
    '''
        定义myfunc函数,打印变量值
    '''
    print('  called myfunc with:', (a, b))


def show_details(name, f, is_partial=False):
    "传入函数名和函数的对象,判断是否使用functools工具,进行参数的打印调用"
    print('{}:'.format(name))
    print('  object:', f)
    print('  __name__:', end=' ')
    try:
        print(f.__name__)
    except AttributeError:
        print('(no __name__)')
    print('  __doc__', repr(f.__doc__))
    print()


show_details('myfunc', myfunc)
# myfunc:  信息输出正常
#   object: <function myfunc at 0x000001BDCE624678>
#   __name__: myfunc
#   __doc__ '\n        定义myfunc函数,打印变量值\n    '

p1 = functools.partial(myfunc, b=4)
show_details('raw wrapper', p1)
# raw wrapper: 装饰过,函数名和
#   object: functools.partial(<function myfunc at 0x0000025E5A834678>, b=4)
#   __name__: (no __name__)
#   __doc__ 'partial(func, *args, **keywords) - new function with partial application\n    of the given arguments and keywords.\n'

print('Updating wrapper:')
print('  assign:', functools.WRAPPER_ASSIGNMENTS)
print('  update:', functools.WRAPPER_UPDATES)
print()
# Updating wrapper: 更新包装类的数据
#   assign: ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')
#   update: ('__dict__',)

# 更新包装类后的数据
functools.update_wrapper(p1, myfunc)
show_details('updated wrapper', p1)
# updated wrapper:
#   object: functools.partial(<function myfunc at 0x000001B3E7B84678>, b=4)
#   __name__: myfunc
#   __doc__ '\n        定义myfunc函数,打印变量值\n    '
functools_update_wrapper.py

运行效果

myfunc:
  object: <function myfunc at 0x000001B3E7B84678>
  __name__: myfunc
  __doc__ '\n        定义myfunc函数,打印变量值\n    '

raw wrapper:
  object: functools.partial(<function myfunc at 0x000001B3E7B84678>, b=4)
  __name__: (no __name__)
  __doc__ 'partial(func, *args, **keywords) - new function with partial application\n    of the given arguments and keywords.\n'

Updating wrapper:
  assign: ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')
  update: ('__dict__',)

updated wrapper:
  object: functools.partial(<function myfunc at 0x000001B3E7B84678>, b=4)
  __name__: myfunc
  __doc__ '\n        定义myfunc函数,打印变量值\n    '

3、functools.partial装饰类,并且修改内置的属性

import functools


class MyClass:
    "模拟工具示范课"

    def __call__(self, e, f=6):
        "Docstring for MyClass.__call__"
        print('  called object with:', (self, e, f))


def show_details(name, f):
    "Show details of a callable object."
    print('{}:'.format(name))
    print('  object:', f)
    print('  __name__:', end=' ')
    try:
        print(f.__name__)
    except AttributeError:
        print('(no __name__)')
    print('  __doc__', repr(f.__doc__))
    return


o = MyClass()

show_details('instance', o)
o('e goes here')
print()

p = functools.partial(o, e='default for e', f=8)  # 如果是装饰,类的话,默认实例后,会调用类的内置方法__call__
functools.update_wrapper(p, o)
show_details('instance wrapper', p)
p()
functools_callable.py

运行效果

instance:
  object: <__main__.MyClass object at 0x000001C73ABFDFC8>
  __name__: (no __name__)
  __doc__ '模拟工具示范课'
  called object with: (<__main__.MyClass object at 0x000001C73ABFDFC8>, 'e goes here', 6)

instance wrapper:
  object: functools.partial(<__main__.MyClass object at 0x000001C73ABFDFC8>, e='default for e', f=8)
  __name__: (no __name__)
  __doc__ '模拟工具示范课'
  called object with: (<__main__.MyClass object at 0x000001C73ABFDFC8>, 'default for e', 8)

4、functools.partialmethod与functools.partial的区别

import functools

def standalone(self, a=1, b=2):
    "Standalone function"
    print('  called standalone with:', (self, a, b))
    if self is not None:
        print('  self.attr =', self.attr)

class MyClass:
    "Demonstration class for functools"

    def __init__(self):
        self.attr = 'instance attribute'

    method1 = functools.partialmethod(standalone)
    method2 = functools.partial(standalone)

o = MyClass()

print('standalone')
standalone(None)
print()

print('method1 as partialmethod')
o.method1()
print()

print('method2 as partial')
try:
    o.method2()
except TypeError as err:
    print('ERROR: {}'.format(err))
functools_partialmethod.py

运行效果

standalone
  called standalone with: (None, 1, 2)

method1 as partialmethod
  called standalone with: (<__main__.MyClass object at 0x0000024184B0DE08>, 1, 2)
  self.attr = instance attribute

method2 as partial
ERROR: standalone() missing 1 required positional argument: 'self'
总结:
  functools.partialmethod:在类中,调用外部函数时,会自动传类的对象
  functools.partial:在类中,调用外部函数时,需要手动传类的对象

5、@functools.wraps,装饰器的使用

import functools


def show_details(name, f):
    "显示一个对象的详细信息"
    print('{}:'.format(name))
    print('  object:', f)
    print('  __name__:', end=' ')
    try:
        print(f.__name__)
    except AttributeError:
        print('(no __name__)')
    print('  __doc__', repr(f.__doc__))
    print()


def simple_decorator(f):
    @functools.wraps(f)
    def decorated(a='decorated defaults', b=1):
        print('  decorated:', (a, b))
        print('  ', end=' ')
        return f(a, b=b)

    return decorated


def myfunc(a, b=2):
    "myfunc() is not complicated"
    print('  myfunc:', (a, b))
    return


# The raw function
show_details('myfunc', myfunc)
myfunc('unwrapped, default b')
myfunc('unwrapped, passing b', 3)
print()

# Wrap explicitly
wrapped_myfunc = simple_decorator(myfunc)
show_details('wrapped_myfunc', wrapped_myfunc)
wrapped_myfunc()
wrapped_myfunc('args to wrapped', 4)
print()


# Wrap with decorator syntax
@simple_decorator
def decorated_myfunc(a, b):
    myfunc(a, b)
    return


show_details('decorated_myfunc', decorated_myfunc)
decorated_myfunc()
decorated_myfunc('args to decorated', 4)
functools_wraps.py

 运行效果

myfunc:
  object: <function myfunc at 0x0000023E36C23708>
  __name__: myfunc
  __doc__ 'myfunc() is not complicated'

  myfunc: ('unwrapped, default b', 2)
  myfunc: ('unwrapped, passing b', 3)

wrapped_myfunc:
  object: <function myfunc at 0x0000023E36C23678>
  __name__: myfunc
  __doc__ 'myfunc() is not complicated'

  decorated: ('decorated defaults', 1)
     myfunc: ('decorated defaults', 1)
  decorated: ('args to wrapped', 4)
     myfunc: ('args to wrapped', 4)

decorated_myfunc:
  object: <function decorated_myfunc at 0x0000023E36C233A8>
  __name__: decorated_myfunc
  __doc__ None

  decorated: ('decorated defaults', 1)
     myfunc: ('decorated defaults', 1)
  decorated: ('args to decorated', 4)
     myfunc: ('args to decorated', 4)

6、functools.total_ordering的装饰示例

import functools
import inspect
from pprint import pprint


@functools.total_ordering
class MyObject:

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

    def __eq__(self, other):
        print('  testing __eq__({}, {})'.format(
            self.val, other.val))
        return self.val == other.val

    def __gt__(self, other):
        print('  testing __gt__({}, {})'.format(
            self.val, other.val))
        return self.val > other.val


print('Methods:\n')
pprint(inspect.getmembers(MyObject, inspect.isfunction)) #获取内置函数的实现对应方法

a = MyObject(1)
b = MyObject(2)

print('\nComparisons:')
for expr in ['a < b', 'a <= b', 'a == b', 'a >= b', 'a > b']:
    print('\n{:<6}:'.format(expr))
    result = eval(expr)
    print('  result of {}: {}'.format(expr, result))
functools_total_ordering.py

 运行效果

Methods:

[('__eq__', <function MyObject.__eq__ at 0x0000020AB4282A68>),
 ('__ge__', <function _ge_from_gt at 0x0000020AB403DD38>),
 ('__gt__', <function MyObject.__gt__ at 0x0000020AB4282708>),
 ('__init__', <function MyObject.__init__ at 0x0000020AB40EE318>),
 ('__le__', <function _le_from_gt at 0x0000020AB403DDC8>),
 ('__lt__', <function _lt_from_gt at 0x0000020AB403D0D8>)]

Comparisons:

a < b :
  testing __gt__(1, 2)
  testing __eq__(1, 2)
  result of a < b: True

a <= b:
  testing __gt__(1, 2)
  result of a <= b: True

a == b:
  testing __eq__(1, 2)
  result of a == b: False

a >= b:
  testing __gt__(1, 2)
  testing __eq__(1, 2)
  result of a >= b: False

a > b :
  testing __gt__(1, 2)
  result of a > b: False

总结:
  使用functools.total_ordering装饰的时候,必须要实现__eq__和其它的比较方法

7、比较顺序的示例

import functools

class MyObject:

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

    def __str__(self):
        return 'MyObject({})'.format(self.val)

def compare_obj(a, b):
    """
    旧式的比较功能。
    """
    print('comparing {} and {}'.format(a, b))
    if a.val < b.val:
        return -1
    elif a.val > b.val:
        return 1
    return 0

# 制用一个key函数,使用cmp_to_key()
get_key = functools.cmp_to_key(compare_obj)

def get_key_wrapper(o):
    "get_key允许打印语句的Wrapper函数."
    new_key = get_key(o)
    print('key_wrapper({}) -> {!r}'.format(o, new_key))
    return new_key

objs = [MyObject(x) for x in range(5, 0, -1)]

for o in sorted(objs, key=get_key_wrapper):
    print(o)
functools_cmp_to_key.py

运行效果

key_wrapper(MyObject(5)) -> <functools.KeyWrapper object at 0x0000025797BBBB50>
key_wrapper(MyObject(4)) -> <functools.KeyWrapper object at 0x0000025797BBBB30>
key_wrapper(MyObject(3)) -> <functools.KeyWrapper object at 0x0000025797BBBB10>
key_wrapper(MyObject(2)) -> <functools.KeyWrapper object at 0x0000025797BBBAF0>
key_wrapper(MyObject(1)) -> <functools.KeyWrapper object at 0x0000025797BBBA90>
comparing MyObject(4) and MyObject(5)
comparing MyObject(3) and MyObject(4)
comparing MyObject(2) and MyObject(3)
comparing MyObject(1) and MyObject(2)
MyObject(1)
MyObject(2)
MyObject(3)
MyObject(4)
MyObject(5)

8、函数中最近最少使用的缓存查看,删除的示例

import functools


@functools.lru_cache()
def expensive(a, b):
    print('expensive({}, {})'.format(a, b))
    return a * b


MAX = 2

print('First set of calls:')
for i in range(MAX):
    for j in range(MAX):
        expensive(i, j)
print(expensive.cache_info())

print('\nSecond set of calls:')
for i in range(MAX + 1):
    for j in range(MAX + 1):
        expensive(i, j)
print(expensive.cache_info())

print('\nClearing cache:')
expensive.cache_clear()
print(expensive.cache_info())

print('\nThird set of calls:')
for i in range(MAX):
    for j in range(MAX):
        expensive(i, j)
print(expensive.cache_info())
functools_lru_cache.py

运行效果

First set of calls:
expensive(0, 0)
expensive(0, 1)
expensive(1, 0)
expensive(1, 1)
CacheInfo(hits=0, misses=4, maxsize=128, currsize=4)

Second set of calls:
expensive(0, 2)
expensive(1, 2)
expensive(2, 0)
expensive(2, 1)
expensive(2, 2)
CacheInfo(hits=4, misses=9, maxsize=128, currsize=9)

Clearing cache:
CacheInfo(hits=0, misses=0, maxsize=128, currsize=0)

Third set of calls:
expensive(0, 0)
expensive(0, 1)
expensive(1, 0)
expensive(1, 1)
CacheInfo(hits=0, misses=4, maxsize=128, currsize=4)

9、设置缓存的过期大小示例

import functools

@functools.lru_cache(maxsize=2)
def expensive(a, b):
    print('called expensive({}, {})'.format(a, b))
    return a * b

def make_call(a, b):
    print('({}, {})'.format(a, b), end=' ')
    pre_hits = expensive.cache_info().hits
    expensive(a, b)
    post_hits = expensive.cache_info().hits
    if post_hits > pre_hits:
        print('cache hit')

print('Establish the cache')
make_call(1, 2)
make_call(2, 3)

print('\nUse cached items')
make_call(1, 2)
make_call(2, 3)

print('\nCompute a new value, triggering cache expiration')
make_call(3, 4)

print('\nCache still contains one old item')
make_call(2, 3)

print('\nOldest item needs to be recomputed')
make_call(1, 2)
functools_lru_cache_expire.py

 运行效果

Establish the cache
(1, 2) called expensive(1, 2)
(2, 3) called expensive(2, 3)

Use cached items
(1, 2) cache hit
(2, 3) cache hit

Compute a new value, triggering cache expiration
(3, 4) called expensive(3, 4)

Cache still contains one old item
(2, 3) cache hit

Oldest item needs to be recomputed
(1, 2) called expensive(1, 2)

 10、列表和字典不可散列运算的示例

import functools


@functools.lru_cache(maxsize=2)
def expensive(a, b):
    print('called expensive({}, {})'.format(a, b))
    return a * b


def make_call(a, b):
    print('({}, {})'.format(a, b), end=' ')
    pre_hits = expensive.cache_info().hits
    expensive(a, b)
    post_hits = expensive.cache_info().hits
    if post_hits > pre_hits:
        print('cache hit')


make_call(1, 2)

try:
    make_call([1], 2)
except TypeError as err:
    print('ERROR: {}'.format(err))

try:
    make_call(1, {'2': 'two'})
except TypeError as err:
    print('ERROR: {}'.format(err))
functools_lru_cache_arguments.py

运行效果

(1, 2) called expensive(1, 2)
([1], 2) ERROR: unhashable type: 'list'
(1, {'2': 'two'}) ERROR: unhashable type: 'dict'

11、functools.reduce递归求指定序列值的和

import functools


def do_reduce(a, b):
    print('do_reduce({}, {})'.format(a, b))
    return a + b


data = range(1, 5)
print(data)
result = functools.reduce(do_reduce, data)
print('result: {}'.format(result))
functools_reduce.py

运行效果

range(1, 5)
do_reduce(1, 2)
do_reduce(3, 3)
do_reduce(6, 4)
result: 10

12、指定初始化值,递归求和的示例

import functools


def do_reduce(a, b):
    print('do_reduce({}, {})'.format(a, b))
    return a + b


data = range(1, 5)
print(data)
result = functools.reduce(do_reduce, data, 99)
print('result: {}'.format(result))
functools_reduce_initializer.py

运行效果

range(1, 5)
do_reduce(99, 1)
do_reduce(100, 2)
do_reduce(102, 3)
do_reduce(105, 4)
result: 109

 13、支持列表递归求和的示例

import functools


def do_reduce(a, b):
    print('do_reduce({}, {})'.format(a, b))
    return a + b


print('Single item in sequence:', functools.reduce(do_reduce, [1]))

print('Single item in sequence with initializer:', functools.reduce(do_reduce, [1,2], 99))

print('Empty sequence with initializer:', functools.reduce(do_reduce, [], 99))

try:
    print('Empty sequence:', functools.reduce(do_reduce, []))
except TypeError as err:
    print('ERROR: {}'.format(err))
functools_reduce_short_sequences.py

运行效果

Single item in sequence: 1
do_reduce(99, 1)
do_reduce(100, 2)
Single item in sequence with initializer: 102
Empty sequence with initializer: 99
ERROR: reduce() of empty sequence with no initial value

14、泛型函数,根据函数基本类型形参调用不同的函数

import functools

@functools.singledispatch
def myfunc(arg):
    print('default myfunc({!r})'.format(arg))

@myfunc.register(int)
def myfunc_int(arg):
    print('myfunc_int({})'.format(arg))

@myfunc.register(list)
def myfunc_list(arg):
    print('myfunc_list()')
    for item in arg:
        print('  {}'.format(item))

myfunc('string argument')
myfunc(1)
myfunc(2.3)
myfunc(['a', 'b', 'c'])
functools_singledispatch.py

运行效果

default myfunc('string argument')
myfunc_int(1)
default myfunc(2.3)
myfunc_list()
  a
  b
  c

 15、泛型函数,根据函数自己定义类的形参调用不用的函数

import functools

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B):
    pass

class E(C, D):
    pass

@functools.singledispatch
def myfunc(arg):
    print('default myfunc({})'.format(arg.__class__.__name__))


@myfunc.register(A)
def myfunc_A(arg):
    print('myfunc_A({})'.format(arg.__class__.__name__))


@myfunc.register(B)
def myfunc_B(arg):
    print('myfunc_B({})'.format(arg.__class__.__name__))


@myfunc.register(C)
def myfunc_C(arg):
    print('myfunc_C({})'.format(arg.__class__.__name__))


myfunc(A())
myfunc(B())
myfunc(C())
myfunc(D())
myfunc(E())
functools_singledispatch_mro.py

 

 运行效果

myfunc_A(A)
myfunc_B(B)
myfunc_C(C)
myfunc_B(D)
myfunc_C(E)
posted @ 2020-06-01 16:39  小粉优化大师  阅读(719)  评论(0编辑  收藏  举报