functools下的partial模块应用
问题
你有一个被其他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模块应用的更多相关文章
- windows下安装python模块
如何在windows下安装python模块 1. 官网下载安装包,比如(pip : https://pypi.python.org/pypi/pip#downloads) pip-9.0.1.tar. ...
- Linux下安装opencv模块
最近微信上流行的给自己的头像加一顶圣诞帽,想用python写一个程序自己实现一下,其中需要用到opencv import cv2 现在记录一下如何在Linux系统(ubutun)下安装该模块: 参考了 ...
- debian+python3.5环境下安装paramiko模块:
debian+python3.5环境下安装paramiko模块: 1.确保安装了操作系统安装了libssl-dev,zlib1g-dev (redhat,centos下这两包包名为openssl-d ...
- 【LDAP安装】在已编译安装的PHP环境下安装LDAP模块
在已编译安装的PHP环境下安装LDAP模块 (乐维温馨提示:其他模块也能以这个方式安装) 1.在PHP源码包内找到ldap模块文件 cd php-5.6.37 cd ext/ldap/ 2.phpiz ...
- python下的select模块使用 以及epoll与select、poll的区别
python下的select模块使用 以及epoll与select.poll的区别 先说epoll与select.poll的区别(总结) 整理http://www.zhihu.com/question ...
- selenium从入门到应用 - 5,页面对象设计模式下的页面模块
本系列所有代码 https://github.com/zhangting85/simpleWebtest 本文将介绍一个Java+TestNG+Maven+Selenium的web自动化测试脚本环境下 ...
- PHP window下安装Spl_Types模块
1. Window下,Spl_Types的模块的下载地址:http://pecl.php.net/package/SPL_Types/0.4.0/windows 2. php的可执行文件已经加到系统的 ...
- linux下利用GPRS模块发短信、打电话
一.开发环境 内核版本:linux-3.0 开发板:FL2440(nandflash:K9F1G08 128M) GPRS模块:SIM900 二.与发短信和拨号相关的 AT 指 ...
- mac下Nginx+lua模块编译安装
Nginx的nb之处就不说了,lua也是一个小巧的脚本语言,由标准C编写而成,几乎可以运行在所有的平台上,也非常强大,其他特性请自行度娘.nginx_lua_module是由淘宝的工程师清无(王晓哲) ...
随机推荐
- Scala实现树形结构
package graphx import java.util.ArrayList import java.util.List /** * Created by zhen on 2018/12/28. ...
- 数据库之mysql篇(6)—— mysql常用函数函数/自定义函数
常用函数 运算函数 我相信你都能看懂,所以以上的不再做过多解释 然后还有个,前面漏掉的between and: 意指10是否在0到20之间,如果是返回1,否则返回0 日期函数 这个要稍微注意一下参数, ...
- 洗礼灵魂,修炼python(66)--爬虫篇—BeauitifulSoup进阶之“我让你忘记那个负心汉,有我就够了”
说明一下,这个标题可能有点突兀,结合上一篇一起看就行 前面已经对BeautifulSoup有了了解了,相信你基本已经学会怎么获取网页数据了,那么BeautifulSoup这么吊,还有没有其他的功能呢? ...
- C# -- 随机数(Random)的使用
使用随机数产生一组大乐透号码 1. C#代码 1 Console.WriteLine("===============大乐透===红色球==============="); Lis ...
- CSS2属性选择器和css3选择器的用法和区别
兄弟们,这是我第一次写博客,希望对进来的人有用,写的不好别喷哈,谢谢. css2属性选择器: 1.[attribute] 例子: [title] 解释: 选择含有 title 属性的所有元 ...
- Java入门(一):Hello World !
前言 从今天开始,准备写Java Web开发的系列文章,毕竟自己主攻的还是Java方向,Python只是业余学习的兴趣使然,在第二技能还没有培养成熟前,做好第一技能的巩固和提高是很有必要的.从正式入行 ...
- Python虚拟环境笔记
虚拟环境 为什么需要虚拟环境: 到目前位置,我们所有的第三方包安装都是直接通过pip install xx的方式进行安装的,这样安装会将那个包安装到你的系统级的Python环境中.但是这样有一个问题, ...
- JAVA 容易忽略的东西
Java中的取余会出现负数.用Math.floorMod()方法可以掰正,但是也仅限被除数是负数的情况,如果除数是负数,这个没用. 和C不一样,Java中的字符串是不可变字符串,不能修改Java字符串 ...
- C# - 汉字与unicode之间的转换
/// <summary> /// 字符串转Unicode码 /// </summary> /// <returns>The to unicode.</ret ...
- MySQL高级知识(十三)——表锁
前言:锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算机资源(如CPU.RAM.I/O等)的争用外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是 ...