资讯专栏INFORMATION COLUMN

python模块之functools

骞讳护 / 1313人阅读

摘要:比较函数是接收两个参数进行比较的函数,返回一个负数表示,返回表示,返回一个正数表示。表示缓存大小限制,确保不会无限制增长。大致等同于用于冻结函数的部分位置参数和或关键字参数而产生一个代表某部分函数功能的简化标志。

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

functools.cmp_to_key(func)

比较函数是接收两个参数进行比较的函数,返回一个负数表示<,返回0表示=,返回一个正数表示>。key函数接收一个参数并返回另一个值作为进行排序的键。
将比较函数转换为key函数,常在用到key关键字的函数如sotred(), min(), heapq.nlargest(), itertools,groupby()中使用。cmp_to_key主要作为过渡工具将python2中支持的比较函数进行转换。

</>复制代码

  1. def numeric_compare(x, y):
  2. return x - y
  3. # python2
  4. print(sorted([5, 2, 4, 1, 3], cmp=numeric_compare))
  5. # python3
  6. 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表示缓存大小限制,确保不会无限制增长。

</>复制代码

  1. @lru_cache(maxsize=32)
  2. def get_pep(num):
  3. "Retrieve text of a Python Enhancement Proposal"
  4. resource = "http://www.python.org/dev/peps/pep-%04d/" % num
  5. try:
  6. with urllib.request.urlopen(resource) as s:
  7. return s.read()
  8. except urllib.error.HTTPError:
  9. return "Not Found"
  10. >>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
  11. ... pep = get_pep(n)
  12. ... print(n, len(pep))
  13. >>> get_pep.cache_info()
  14. CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)
@functools.total_ordering

类装饰器,包装在定义了一个或多个富比较排序方法的类上,会补足其他的比较方法。
该类必须定义__lt__(), __le__(), __gt__(), 或__ge__()中至少一个方法,并建议定义__eq__()方法。

</>复制代码

  1. @total_ordering
  2. class Student:
  3. def __init__(self, lastname, firstname):
  4. self.lastname = lastname
  5. self.firstname = firstname
  6. def _is_valid_operand(self, other):
  7. return (hasattr(other, "lastname") and
  8. hasattr(other, "firstname"))
  9. def __eq__(self, other):
  10. if not self._is_valid_operand(other):
  11. return NotImplemented
  12. return ((self.lastname.lower(), self.firstname.lower()) ==
  13. (other.lastname.lower(), other.firstname.lower()))
  14. def __lt__(self, other):
  15. if not self._is_valid_operand(other):
  16. return NotImplemented
  17. return ((self.lastname.lower(), self.firstname.lower()) <
  18. (other.lastname.lower(), other.firstname.lower()))

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

functools.partial(func, *args, **keywords)

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

大致等同于:

</>复制代码

  1. def partial(func, *args, **keywords):
  2. def newfunc(*fargs, **fkeywords):
  3. newkeywords = keywords.copy()
  4. newkeywords.update(fkeywords)
  5. return func(*args, *fargs, **newkeywords)
  6. newfunc.func = func
  7. newfunc.args = args
  8. newfunc.keywords = keywords
  9. return newfunc

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

</>复制代码

  1. >>> from functools import partial
  2. >>> basetwo = partial(int, base=2)
  3. >>> basetwo.__doc__ = "Convert base 2 string to an int."
  4. >>> basetwo("10010")
  5. 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装饰器定义一个泛函数,单分派仅限于第一个参数的类型:

</>复制代码

  1. from functools import singledispatch
  2. @singledispatch
  3. def fun(arg, verbose=False):
  4. if verbose:
  5. print("Let me just say,", end=" ")
  6. print(arg)

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

</>复制代码

  1. @fun.register
  2. def _(arg: int, verbose=False):
  3. if verbose:
  4. print("Strength in numbers, eh?", end=" ")
  5. print(arg)
  6. @fun.register
  7. def _(arg: list, verbose=False):
  8. if verbose:
  9. print("Enumerate this:")
  10. for i, elem in enumerate(arg):
  11. print(i, elem)

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

</>复制代码

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

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

</>复制代码

  1. def nothing(arg, verbose=False):
  2. print("Nothing.")
  3. fun.register(type(None), nothing)

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

</>复制代码

  1. from decimal import Decimal
  2. @fun.register(float)
  3. @fun.register(Decimal)
  4. def fun_num(arg, verbose=False):
  5. if verbose:
  6. print("Half of your number:", end=" ")
  7. print(arg / 2)
  8. >>> fun_num is fun
  9. False

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

</>复制代码

  1. >>> fun("Hello, world.")
  2. Hello, world.
  3. >>> fun("test.", verbose=True)
  4. Let me just say, test.
  5. >>> fun(42, verbose=True)
  6. Strength in numbers, eh? 42
  7. >>> fun(["spam", "spam", "eggs", "spam"], verbose=True)
  8. Enumerate this:
  9. 0 spam
  10. 1 spam
  11. 2 eggs
  12. 3 spam
  13. >>> fun(None)
  14. Nothing.
  15. >>> fun(1.23)
  16. 0.615

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

</>复制代码

  1. >>> fun.dispatch(float)
  2. >>> fun.dispatch(dict) # note: default implementation

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

</>复制代码

  1. >>> fun.registry.keys()
  2. dict_keys([, , , , , ])
  3. >>> fun.registry[float]
  4. >>> fun.registry[object]
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__属性可以获取到被包装的原始函数。

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

</>复制代码

  1. def wrapper(f):
  2. def wrapper_function(*args, **kwargs):
  3. """this is a wrapper function"""
  4. return f(*args, **kwargs)
  5. # update_wrapper(wrapper_function, f)
  6. return wrapper_function
  7. @wrapper
  8. def wrapped():
  9. """this is a wrapped function"""
  10. pass
  11. print(wrapped.__doc__)
  12. print(wrapped.__name__)

</>复制代码

  1. # 不使用update_wrapper的结果:
  2. >>> this is a wrapper function
  3. >>> wrapper_function
  4. # 使用update_wrapper的结果:
  5. >>> this is a wrapped function
  6. >>> wrapped
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

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

</>复制代码

  1. def wrapper(f):
  2. @wraps(f)
  3. def wrapper_function(*args, **kwargs):
  4. """this is a wrapper function"""
  5. return f(*args, **kwargs)
  6. return wrapper_function
  7. @wrapper
  8. def wrapped():
  9. """this is a wrapped function"""
  10. pass
  11. print(wrapped.__doc__)
  12. print(wrapped.__name__)
partial对象

partial对象是由partial()方法创建的可调用对象,它有三个只读属性:
partial.func
一个可调用对象或函数。调用partial对象将使用新的位置参数和关键字参数转发到func。
partial.args
调用partial()时提供的位置参数
partial.keywords
调用partial()时提供的关键字参数

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/45040.html

相关文章

  • Python学习路24-一等函数

    摘要:函数内省的内容到此结束。函数式编程并不是一个函数式编程语言,但通过和等包的支持,也可以写出函数式风格的代码。 《流畅的Python》笔记。本篇主要讲述Python中函数的进阶内容。包括函数和对象的关系,函数内省,Python中的函数式编程。 1. 前言 本片首先介绍函数和对象的关系;随后介绍函数和可调用对象的关系,以及函数内省。函数内省这部分会涉及很多与IDE和框架相关的东西,如果平时...

    wind3110991 评论0 收藏0
  • Python中的函数装饰器和闭包

    摘要:变量查找规则在中一个变量的查找顺序是局部环境,闭包,全局,内建闭包引用了自由变量的函数。闭包的作用闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数,此时即便生成闭包的环境父函数已经释放,闭包仍然存在。 导语:本文章记录了本人在学习Python基础之函数篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、掌握装饰器的本质、功...

    caozhijian 评论0 收藏0
  • 5-functools模块

    摘要:看一个简单的例子在实际实验中,加不加并没有区别。仅作了解这是个有趣的装饰器,传入的参数被打上了,当下一次传入的参数是一样的时候,就会从中直接取出对应的值,而不需要进行重新的运算。这样做的好处是可以帮助我们分离代码逻辑输出 functools functools 包含了用于创建装饰函数,启动面向切面的编程,超出面向对象编程范围的代码复用,同时提供了装饰函数用于丰富的快捷比较的API, p...

    YancyYe 评论0 收藏0
  • Python一等函数简析

    摘要:本文重点了解函数在中是一等对象了解中的可调用对象掌握正确定义函数参数的方法了解和中支持函数式编程的方法。归约函数定义能够接受一个可迭代对象并返回单个结果的函数是归约函数。 导语:本文章记录了本人在学习Python基础之函数篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、了解函数在Python中是一等对象;2、了解Python中的可调用对象;3...

    shusen 评论0 收藏0
  • Python一等函数简析

    摘要:本文重点了解函数在中是一等对象了解中的可调用对象掌握正确定义函数参数的方法了解和中支持函数式编程的方法。归约函数定义能够接受一个可迭代对象并返回单个结果的函数是归约函数。 本文章记录了本人在学习Python基础之函数篇的重点知识及个人心得,欢迎打算入门Python的朋友与我一起学习交流。。 本文重点: 1、了解函数在Python中是一等对象;2、了解Python中的可调用对象;3、掌握...

    邹立鹏 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<