python模块之functools

栏目: Python · 发布时间: 7年前

内容简介:functools模块提供了某些高阶函数(将比较函数转换为key函数,常在用到key关键字的函数如

functools模块提供了某些高阶函数( high-order function )。

functools.cmp_to_key(func)

比较函数是接收两个参数进行比较的函数,返回一个负数表示 < ,返回0表示 = ,返回一个正数表示 > 。key函数接收一个参数并返回另一个值作为进行 排序 的键。

将比较函数转换为key函数,常在用到key关键字的函数如 sotred() , min() , heapq.nlargest() , itertools,groupby() 中使用。cmp_to_key主要作为过渡 工具python 2中支持的比较函数进行转换。

def numeric_compare(x, y):
    return x - y

# python2
print(sorted([5, 2, 4, 1, 3], cmp=numeric_compare))

# python3
print(sorted([5, 2, 4, 1, 3], key=cmp_to_key(numeric_compare)))

@functools.lru_cache(maxsize=128, typed=False)

使用memoize包装函数,保存最近 maxsize 次的函数调用结果。在 I/O-bound 函数上装饰,处理相同参数时可以节省函数执行时间。

  • 如果 maxsize 为None,会禁用LRU缓存特性且缓存大小会无限增长。maxsize设置为2的n次方时性能最优。
  • 如果 typed 为True,不同类型函数参数的执行结果会被分别缓存,例如 f(3)f(3.0) 会被视为有两个不同结果的不同调用。

因为该装饰器使用字典缓存函数执行结果,所以函数的位置参数和关键字参数必须是可哈希的。

不同的参数模式可能会被视为不同的缓存实体。例如 f(a=1, b=2)f(b=2, a=1) 虽然只是关键字顺序不同但可能有两个单独的缓存实体。

被包装的函数可以调用 cache_info() ,它返回一个 (hits, misses, maxsize, currsize) 形式的命名元组;可以调用 cache_clear() 清空或使缓存无效;还可以调用 __wrapped__ 属性绕过缓存,访问原始的底层函数。

LRU( least recently used )缓存通常应仅用在需要重用先前计算的值的场景,其他场景如使用LRU有不良影响、每次调用需要返回不同结果、time()、random()等应禁止使用。maxsize表示缓存大小限制,确保不会无限制增长。

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
    try:
        with urllib.request.urlopen(resource) as s:
            return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
...     pep = get_pep(n)
...     print(n, len(pep))

>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

@functools.total_ordering

类装饰器,包装在定义了一个或多个富比较排序方法的类上,会补足其他的比较方法。

该类必须定义 __lt__() , __le__() , __gt__() , 或 __ge__() 中至少一个方法,并建议额外定义 __eq__() 方法。

@total_ordering
class Student:
    def __init__(self, lastname, firstname):
        self.lastname = lastname
        self.firstname = firstname

    def _is_valid_operand(self, other):
        return (hasattr(other, "lastname") and
                hasattr(other, "firstname"))

    def __eq__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))

    def __lt__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

NOTE:使用这个装饰器的代价是,装饰器自动补足的比较方法耗费了更多的执行时间并拥有更复杂的堆栈跟踪。如果应用性能基准测试表明是使用此装饰器造成的瓶颈,手动实现所有六种富比较方法可以带来直观的速度提升。

functools.partial(func, args, *keywords)

偏函数,返回一个 partial 对象,调用时等同调用了使用预设位置参数和关键字参数的func函数。如果partial对象调用时又提供了额外的位置参数,追加到 args ,如果提供了额外的关键字参数,扩展 keywords 并覆盖相同的键值。

大致等同于:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

partial()用于“冻结”函数的部分位置参数和/或关键字参数而产生一个代表某部分函数功能的简化标志。

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18

class functools.partialmethod(func, args, *keywords)

后续补充

functools.reduce(function, iterable[, initializer])

将可迭代对象iterable中的元素从左到右累计到接收两个参数的函数func中。例如 reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 的计算等同于 ((((1+2)+3)+4)+5)

如果设置了可选参数 initializer ,它被放置在要计算的序列之前,并在序列为空时作为默认值;如果未提供 initializer 的值且序列仅包含一个元素,返回该元素。

@functools.singledispatch

将函数转换为单分派泛函数( single-dispatch generic function )。

使用 @singledispatch 装饰器定义一个泛函数,单分派仅限于第一个参数的类型:

from functools import singledispatch

@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg)

使用泛函数的 register() 属性添加重载实现,该属性是一个装饰器。对于使用类型注解的函数,该装饰器将自动推断第一个参数的类型:

@fun.register
def _(arg: int, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)

@fun.register
def _(arg: list, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem)

如果不使用类型注解,可以显式地给装饰器传递类型参数:

@fun.register(complex)
def _(arg, verbose=False):
    if verbose:
        print("Better than complicated.", end=" ")
    print(arg.real, arg.imag)

也可以以函数的形式使用register()注册lambda函数和已定义的函数:

def nothing(arg, verbose=False):
    print("Nothing.")

fun.register(type(None), nothing)

register()属性返回允许装饰器堆叠、序列化以及创建独立的单元测试的未装饰的函数:

from decimal import Decimal

@fun.register(float)
@fun.register(Decimal)
def fun_num(arg, verbose=False):
    if verbose:
        print("Half of your number:", end=" ")
    print(arg / 2)

>>> fun_num is fun
False

调用时,泛函数只分派第一个参数的类型:

>>> fun("Hello, world.")
Hello, world.
>>> fun("test.", verbose=True)
Let me just say, test.
>>> fun(42, verbose=True)
Strength in numbers, eh? 42
>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
Enumerate this:
0 spam
1 spam
2 eggs
3 spam
>>> fun(None)
Nothing.
>>> fun(1.23)
0.615

如果某个类型没有相应的实现,将查找更通用的实现进行解析。使用 @singledispatch 装饰的原始函数注册为 object 类型,将在没有更好实现的情况下使用。调用 dispatch() 属性查看泛函数使用了哪个实现:

>>> fun.dispatch(float)
<function fun_num at 0x1035a2840>
>>> fun.dispatch(dict)    # note: default implementation
<function fun at 0x103fe0000>

调用 registry 属性查看注册的所有实现:

>>> fun.registry.keys()
dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>, <class 'decimal.Decimal'>, <class 'list'>, <class 'float'>])
>>> fun.registry[float]
<function fun_num at 0x1035a2840>
>>> fun.registry[object]
<function fun at 0x103fe0000>

functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

更新包装函数( wrapper ),使其看起来更像被包装的原始函数( wrapped )。元组类型的可选参数 assigned 指定wrapped函数的哪些属性直接分派到wrapper函数对应的属性上, updated 参数指定使用wrapped函数的哪些属性更新wrapper函数对应的属性上。

WRAPPER_ASSIGNMENTS 的默认值是'__module__', '__name__', '__qualname__', '__doc__','__annotations__'

WRAPPER_UPDATES 的默认值是'__dict__'

通过访问包装函数的 __wrapped__ 属性可以获取到被包装的原始函数。

该函数主要用于装饰器使用场景下,如果不更新包装函数,返回函数的元数据将指向包装函数而非被包装的原始函数,一般来说没太大意义。

def wrapper(f):
    def wrapper_function(*args, **kwargs):
        """this is a wrapper function"""
        return f(*args, **kwargs)
    # update_wrapper(wrapper_function, f)
    return wrapper_function

@wrapper
def wrapped():
    """this is a wrapped function"""
    pass

print(wrapped.__doc__)
print(wrapped.__name__)
# 不使用update_wrapper的结果:
>>> this is a wrapper function
>>> wrapper_function

# 使用update_wrapper的结果:
>>> this is a wrapped function
>>> wrapped

@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

等同于partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated),不过是作为包装函数定义时的装饰器使用。

def wrapper(f):
    @wraps(f)
    def wrapper_function(*args, **kwargs):
        """this is a wrapper function"""
        return f(*args, **kwargs)
    return wrapper_function

@wrapper
def wrapped():
    """this is a wrapped function"""
    pass

print(wrapped.__doc__)
print(wrapped.__name__)

partial对象

partial对象是由partial()方法创建的可调用对象,它有三个只读属性:

partial.func

一个可调用对象或函数。调用partial对象将使用新的位置参数和关键字参数转发到func。

partial.args

调用partial()时提供的位置参数

partial.keywords

调用partial()时提供的关键字参数


以上所述就是小编给大家介绍的《python模块之functools》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Practical Vim, Second Edition

Practical Vim, Second Edition

Drew Neil / The Pragmatic Bookshelf / 2015-10-31 / USD 29.00

Vim is a fast and efficient text editor that will make you a faster and more efficient developer. It’s available on almost every OS, and if you master the techniques in this book, you’ll never need an......一起来看看 《Practical Vim, Second Edition》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具