更新一个最近发现的技巧,一行代码实现多线程/多进程,来源于python开发者微信公众号。
首先来看下代码:
import urllib2
from multiprocessing.dummy import Pool as ThreadPool
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003
]
pool = ThreadPool(4)
results = pool.map(urllib2.urlopen, urls)
pool.close()
pool.join()
对,你没有看错,只要一行代码就可以把普通的任务变成并行任务。不用手动管理线程,一切都由map自动完成。这里演示的是多线程,如果要多进程的话只需把 from multiprocessing.dummy 改成 from multiprocessing ,就是这么任性!
以下为这个库的详细介绍:
在 Python 中有个两个库包含了 map 函数: multiprocessing 和它鲜为人知的子库 multiprocessing.dummy.
这里多扯两句: multiprocessing.dummy? mltiprocessing 库的线程版克隆?这是虾米?即便在 multiprocessing 库的官方文档里关于这一子库也只有一句相关描述。而这句描述译成人话基本就是说:”嘛,有这么个东西,你知道就成.”相信我,这个库被严重低估了!
dummy 是 multiprocessing 模块的完整克隆,唯一的不同在于 multiprocessing 作用于进程,而 dummy 模块作用于线程(因此也包括了 Python 所有常见的多线程限制)。
所以替换使用这两个库异常容易。你可以针对 IO 密集型任务和 CPU 密集型任务来选择不同的库。
原文链接 : http://mp.weixin.qq.com/s?__biz=MzA4MjEyNTA5Mw==&mid=2652563685&idx=2&sn=f563f8913630a4334219ed4a9fa99653&scene=0#wechat_redirect
———————以下为原答案———————————
搜了一下,发现没人说这个。
废话不说,直接上图:
在python中,下滑杠代表上一次运行的结果。不要问我为什么,我也不知道。
这个奇技淫巧是我在查scapy的资料的时候意外发现的,网上关于这个技巧的资料似乎也很少,嗯。(顺便提一下,scapy是个非常强大的库,几乎涵盖了所有网络相关的功能,推荐学习。)
再来说一个吧,关于动态修改代码的。直接上代码:
# socket.py
#
import sys
del sys.modules['socket'] # 从内存中删除当前的socket包
import sys
import time
import logging
import types
path = sys.path[0]
sys.path.pop(0)
import socket # 导入真正的socket包
sys.path.insert(0, path)
# 动态path类方法
def re_class_method(_class, method_name, re_method):
method = getattr(_class, method_name)
info = sys.version_info
if info[0] >= 3: # py2和py3的语法略有不同,需要做下判断。
setattr(_class, method_name,
types.MethodType(lambda *args, **kwds: re_method(method, *args, **kwds), _class))
else:
setattr(_class, method_name,
types.MethodType(lambda *args, **kwds: re_method(method, *args, **kwds), None, _class))
# 动态path实例方法
def re_self_method(self, method_name, re_method):
method = getattr(self, method_name)
setattr(self, method_name, types.MethodType(lambda *args, **kwds: re_method(method, *args, **kwds), self, self))
# 需要修改的类方法
def re_accept(old_method, self, *args, **kwds):
return_value = old_method(self, *args, **kwds)
#do something
return return_value
# 需要修改的实例方法
def re_recvfrom(old_method, self, *args, **kwds):
return_value = old_method(*args, **kwds)
# do something
return return_value
# 需要修改的类方法(无返回值)
def re_bind(old_method, self, *args, **kwds):
re_self_method(self, 'recvfrom', re_recvfrom) #把self实例的recvfrom方法替换成re_recvfrom
#do something
old_method(self, *args, **kwds)
setattr(socket.socket, '_list_client_ip', {}) # 绑定类属性(socket不能动态绑定实例属性,只好绑定类属性了)
re_class_method(socket.socket, 'bind', re_bind) #把socket类的bind方法替换成re_bind
re_class_method(socket.socket, 'accept', re_accept) #把socket类的accept方法替换成re_accept
把上面的代码保存为socket.py,并放入使用了socket的程序的文件夹中,就可以达到动态修改代码的目的。即在不修改库也不修改程序的前提下动态“修改”原有的代码并实现一些功能。
简单来说这段代码实现了两个功能:
1. 劫持库(暂且叫这个名字吧):
因为python会优先导入当前文件夹下的包(不管其他地方是否有同名的包),所以把socket.py文件放入文件夹之后,python自然会导入我们所编写的这个socket.py文件,这等于是劫持了系统中的socket库。
但事情到这里还没有完,如果只是劫持的话socket原有的功能就会失效,所以我们还要导入真正的socket库。
其实导入真正的库之后任务基本就算结束了,我们可以在socket.py文件中做任何想做的事,比如打印一些东西之类的。这样当这个socket文件被导入的时候这些代码就会被执行,但这样做显然还不够,我们还有更高级的玩法!2. 动态修改类方法:
首先获取旧方法并保存,然后编写一个新方法,内容是调用旧方法。之后用setattr方法把类或实例的方法替换成新方法。在得到原方法的结果之后,我们可以选择做一些事,然后再返回结果。说得有点绕,还是看代码好明白一些。
最后,完整的代码在这里:
限制ss客户端数量(基于ip判断),理论上可以用于其他使用了socket模块的python程序。 · GitHub
功能是动态修改socket库,以实现限制客户端数量的目的,代码写的很烂,不喜勿喷。