问题

你有一个被其他python代码使用的callable对象,可能是一个回调函数或者是一个处理器, 但是它的参数太多了,导致调用时出错。

解决方案

如果需要减少某个函数的参数个数,你可以使用 functools.partial()partial() 函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数。 为了演示清楚,假设你有下面这样的函数:

def spam(a, b, c, d):
print(a, b, c, d)

现在我们使用 partial() 函数来固定某些参数值:

>>> from functools import partial
>>> s1 = partial(spam, ) # a =
>>> s1(, , ) >>> s1(, , ) >>> s2 = partial(spam, d=) # d =
>>> s2(, , ) >>> s2(, , ) >>> s3 = partial(spam, , , d=) # a = , b = , d =
>>> s3() >>> s3() >>> s3() >>>

可以看出 partial() 固定某些参数并返回一个新的callable对象。这个新的callable接受未赋值的参数, 然后跟之前已经赋值过的参数合并起来,最后将所有参数传递给原始函数。

讨论

本节要解决的问题是让原本不兼容的代码可以一起工作。下面我会列举一系列的例子。

第一个例子是,假设你有一个点的列表来表示(x,y)坐标元组。 你可以使用下面的函数来计算两点之间的距离:

points = [ (, ), (, ), (, ), (, ) ]

import math
def distance(p1, p2):
x1, y1 = p1
x2, y2 = p2
return math.hypot(x2 - x1, y2 - y1)

说明一下这里的math.hypot默认是以坐标原点为几点计算坐标到原点的直线距离

import math
print(math.hypot(,))
>>>

现在假设你想以某个点为基点,根据点和基点之间的距离来排序所有的这些点。 列表的 sort() 方法接受一个关键字参数来自定义排序逻辑, 但是它只能接受一个单个参数的函数(distance()很明显是不符合条件的)。 现在我们可以通过使用 partial() 来解决这个问题:

>>> pt = (, )
>>> points.sort(key=partial(distance,pt))
>>> points
[(, ), (, ), (, ), (, )]
>>>

更进一步,partial() 通常被用来微调其他库函数所使用的回调函数的参数。 例如,下面是一段代码,使用 multiprocessing 来异步计算一个结果值, 然后这个值被传递给一个接受一个result值和一个可选logging参数的回调函数:

def output_result(result, log=None):
if log is not None:
log.debug('Got: %r', result) # A sample function
def add(x, y):
return x + y if __name__ == '__main__':
import logging
from multiprocessing import Pool
from functools import partial logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger('test') p = Pool()
p.apply_async(add, (, ), callback=partial(output_result, log=log))
p.close()
p.join()

当给 apply_async() 提供回调函数时,通过使用 partial() 传递额外的 logging 参数。 而 multiprocessing 对这些一无所知——它仅仅只是使用单个值来调用回调函数。

作为一个类似的例子,考虑下编写网络服务器的问题,socketserver 模块让它变得很容易。 下面是个简单的echo服务器:

from socketserver import StreamRequestHandler, TCPServer

class EchoHandler(StreamRequestHandler):
def handle(self):
for line in self.rfile:
self.wfile.write(b'GOT:' + line) serv = TCPServer(('', ), EchoHandler)
serv.serve_forever()

不过,假设你想给EchoHandler增加一个可以接受其他配置选项的 __init__ 方法。比如:

class EchoHandler(StreamRequestHandler):
# ack is added keyword-only argument. *args, **kwargs are
# any normal parameters supplied (which are passed on)
def __init__(self, *args, ack, **kwargs):
self.ack = ack
super().__init__(*args, **kwargs) def handle(self):
for line in self.rfile:
self.wfile.write(self.ack + line)

这么修改后,我们就不需要显式地在TCPServer类中添加前缀了。 但是你再次运行程序后会报类似下面的错误:

Exception happened during processing of request from ('127.0.0.1', )
Traceback (most recent call last):
...
TypeError: __init__() missing required keyword-only argument: 'ack'

初看起来好像很难修正这个错误,除了修改 socketserver 模块源代码或者使用某些奇怪的方法之外。 但是,如果使用 partial() 就能很轻松的解决——给它传递 ack 参数的值来初始化即可,如下:

from functools import partial
serv = TCPServer(('', ), partial(EchoHandler, ack=b'RECEIVED:'))
serv.serve_forever()

在这个例子中,__init__() 方法中的ack参数声明方式看上去很有趣,其实就是声明ack为一个强制关键字参数。 关于强制关键字参数问题我们在7.2小节我们已经讨论过了,读者可以再去回顾一下。

很多时候 partial() 能实现的效果,lambda表达式也能实现。比如,之前的几个例子可以使用下面这样的表达式:

points.sort(key=lambda p: distance(pt, p))
p.apply_async(add, (, ), callback=lambda result: output_result(result,log))
serv = TCPServer(('', ),
lambda *args, **kwargs: EchoHandler(*args, ack=b'RECEIVED:', **kwargs))

这样写也能实现同样的效果,不过相比而已会显得比较臃肿,对于阅读代码的人来讲也更加难懂。 这时候使用 partial() 可以更加直观的表达你的意图(给某些参数预先赋值)。

functools下的partial模块应用的更多相关文章

  1. windows下安装python模块

    如何在windows下安装python模块 1. 官网下载安装包,比如(pip : https://pypi.python.org/pypi/pip#downloads) pip-9.0.1.tar. ...

  2. Linux下安装opencv模块

    最近微信上流行的给自己的头像加一顶圣诞帽,想用python写一个程序自己实现一下,其中需要用到opencv import cv2 现在记录一下如何在Linux系统(ubutun)下安装该模块: 参考了 ...

  3. debian+python3.5环境下安装paramiko模块:

    debian+python3.5环境下安装paramiko模块: 1.确保安装了操作系统安装了libssl-dev,zlib1g-dev  (redhat,centos下这两包包名为openssl-d ...

  4. 【LDAP安装】在已编译安装的PHP环境下安装LDAP模块

    在已编译安装的PHP环境下安装LDAP模块 (乐维温馨提示:其他模块也能以这个方式安装) 1.在PHP源码包内找到ldap模块文件 cd php-5.6.37 cd ext/ldap/ 2.phpiz ...

  5. python下的select模块使用 以及epoll与select、poll的区别

    python下的select模块使用 以及epoll与select.poll的区别 先说epoll与select.poll的区别(总结) 整理http://www.zhihu.com/question ...

  6. selenium从入门到应用 - 5,页面对象设计模式下的页面模块

    本系列所有代码 https://github.com/zhangting85/simpleWebtest 本文将介绍一个Java+TestNG+Maven+Selenium的web自动化测试脚本环境下 ...

  7. PHP window下安装Spl_Types模块

    1. Window下,Spl_Types的模块的下载地址:http://pecl.php.net/package/SPL_Types/0.4.0/windows 2. php的可执行文件已经加到系统的 ...

  8. linux下利用GPRS模块发短信、打电话

    一.开发环境     内核版本:linux-3.0    开发板:FL2440(nandflash:K9F1G08 128M)    GPRS模块:SIM900   二.与发短信和拨号相关的 AT 指 ...

  9. mac下Nginx+lua模块编译安装

    Nginx的nb之处就不说了,lua也是一个小巧的脚本语言,由标准C编写而成,几乎可以运行在所有的平台上,也非常强大,其他特性请自行度娘.nginx_lua_module是由淘宝的工程师清无(王晓哲) ...

随机推荐

  1. HandyEditor 富文本编辑器整合到python flask项目中

    1.下载HandyEditor,地址http://he.catfish-cms.com/ 2.解压后的文件名HandyEditor-master改为HandyEditor,文件夹里的文件如下 3.将H ...

  2. Orchard详解--第二篇 启动

    Orchard Framework作为框架它与类库最大的区别就是框架是将一系列零散的组件组合在一起形成一个整体,接下来就对Orchard Framework如何分析Orchard如何将相关组件结合在一 ...

  3. DBA思考系列——学会拒绝不合理的需求

    DBA思考系列--学会拒绝不合理的需求   一直以来,个性都比较随意,一般很少拒绝开发人员的一些需求(有点老好人的感觉). 这点一直被老大诟病,也一直在反省!最近又有一件事情,让我觉得:应该学会拒绝不 ...

  4. python列表与元组的用法

    python列表与元组的用法 目录: 列表的用法: 1.增   append   +    extend 2.删  del    remove 3.改   insert 4.查  index 5.反向 ...

  5. 数据挖掘---Numpy的学习

    什么是Numpy NumPy系统是Python的一种开源的数值计算扩展.这种工具可用来存储和处理大型矩阵(任意维度的数据处理),比Python自身的嵌套列表(nested list structure ...

  6. IntelliJ IDEA 创建maven管理的webapp项目

    因为使用框架时基本需要使用maven管理项目,所以单独写一个搭建maven项目的流程 第一步:  File-->New--Project 第二步:  选择maven框架 第三步:  输入工程id ...

  7. C语言 设一个函数process,调用它时,实现不同功能。

    //凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 输入a, b,第一次调用process找最大值,第二次调用process找最小值,第三次调用求和. 方法1 ...

  8. 【夯实PHP基础】PHP多进程-- pcntl_fork实现

    本文地址 参考文档 分享提纲: 1. 概述 2.安装(只支持Linux) 3. 代码实验多进程pcntl_fork 4. 具体解释 1. 概述 PHP有个pcntl_fork的函数可以实现多进程,但要 ...

  9. Linux之权限详解

    如何知道你有什么权限 我能干什么?这是我最关心的问题! 切换到普通用户 [root@luffy-01 ~]# su - pizza [pizza@luffy-01 ~]$ ls /root ls: c ...

  10. 大学?做码农?做project师?

        近期看到一个知乎里非常热闹的讨论.当中讨论到科研能力与project能力,我有非常多感想. 想说说大学CS方向的一些东西.     我不是计算机专业的,如今大二本科工科在读.     我接触编 ...