问题

你有一个被其他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. 智能POS正餐主副机模式FAQ(无桌台或桌台模块)

    1.无桌台 (1).如果是初次使用,首先检查是否是新建的机器号,新建的机器是默认关闭桌台的,需要到模块管理中开启. 2.无桌台模块 (1).是否在主副机开启连接上后重启主机与副机,且同步数据.

  2. Python lambda介绍

    转自:http://www.cnblogs.com/evening/archive/2010/03/29/2423554.html Python lambda 介绍   在学习python的过程中,l ...

  3. c/c++ 网络编程与多线程 编译参数

    网络编程与多线程 编译参数 编译时要链接操作系统的pthread库 g++ -g socket01.cpp -std=c++11 -pthread 不加-pthread的话,出现下面的错误: term ...

  4. Windows Server 2016-域站点链接及子网调整

    很多情况下我们在判别域控间或者域中各站点同步是否正常往往的操作内容就是查看两台域控间PING或者解析是否正常,或者查看双方防火墙是否关闭,但实际情况下我们需要注意的是,保证站点间Active Dire ...

  5. MC9SD64单片机快速入门 I/O寄存器

    I/O的使用 数据方向寄存器和数据寄存器的配置 I/O输入输出的使用: 数据方向寄存器与数据寄存器 寄存器的概念: 寄存器,是集成电路中非常重要的一种存储单元,通常由触发器组成.在集成电路设计中,寄存 ...

  6. mysql 中的内置函数

    一.字符串函数 select concat(name,"age is",age) from users;  insert(str,x,y,insert)//将字符串x位置开始y个位 ...

  7. JavaScript数据类型之布尔类型

    引言 布尔值指代真或假.开或关.是或否.这个类型只有两个值,保留字true和false.JavaScript程序中的比较语句的结果通常都是布尔值.布尔值通常用于JavaScript中的控制结构中. 真 ...

  8. Node.js作web服务器总结

    1.为什么Node.js用JS开发    Node.js 含有一系列内置模块,使得程序可以脱离 Apache HTTP Server 或 IIS,作为独立服务器运行. 首先,我们都清楚的是,同时接收数 ...

  9. Spring中事务配置以及事务不起作用可能出现的问题

    前言:在Spring中可以通过对方法进行事务的配置,而不是像原来通过手动写代码的方式实现事务的操作,这在很大程度上减少了开发的难度,本文介绍Spring事务配置的两种方式:基于配置文件的方式和基于注解 ...

  10. GUNS后台管理框架部署与发布

    一.GUNS介绍 Guns基于SpringBoot,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + mybatis-plus + beetl + flowable!Gun ...