求助Python代码


昨天接触了yield ,所以学习一下,学得过程中 看到了这段代码,来自

说是求 组合的! 我周围没朋友学python,所以我上这来问了!

不知是这段代码不对,还是我对这段代码的功能理解错了,请指正!!!

}

浅析python调用外部代码的三种方式(上篇)


    项目中使用Python总会由于某种原因(人力/时间/金钱等等),需要借助外部代码,幸好Python是容易扩展的语言,本文浅析三种常见扩展方式:subprocess调用外部可执行程序;ctypes调用外部DLL函数;C/C++ Extending标准库。用一个需求的三种实现方式贯穿始终,正是对应标题的“Python调用外部代码的三种方式”,该需求比“hello,world”复杂,但依然只有百行规模的代码,容易看懂,力争做好“万事开头难”中的“开头”,以后修行就看个人了,同时作为自己学习的备忘,以免隔三差五查文档或犯同样的错误。理解全文需熟悉Python和C,如果有Linux/UNIX系统编程知识更好,全文篇幅较长,不得不分三篇(上,中,下)发布,上篇包括subprocess和ctypes,中篇是C/C++ Extending,下篇包含文中所有代码压缩包的附件。

此处“外部代码”是指:
1. 
本地可执行程序,通常是原生二进制文件或解释器文本文件。
2. 
本地动态链接库,通常由C/C++编译而成。

先简要概括下这三种调用方式:
1. 
subprocess。这是一个标准库,可以运行外部程序,接口简单易用,容易跨平台。不过灵活性欠佳,因为Python无法控制外部程序的运行逻辑,只适合做简单的输入输出交互。
2. 
ctypes。这是一个标准库,可以加载动态链接库,借助ctypes提供的wrapper,可以方便的使用C库函数和系统调用,功能和灵活性大增,做hack的利器。不过毕竟是按照C API的方式利用这类接口,用动态解释语言去适配静态编译语言接口,别扭程度可想而知。
Extending,即用C/C++写Python模块或定制Python解释器,这是扩展Python的终极方案,可以随心所欲,将项目已有的C/C++接口包装为Python模块,或将有性能瓶颈的纯Python模块用C/C++重写,或为Python增加内置函数和对象。不过这是扩展Python中最复杂的方式,将模块与CPython绑定,而与JPython/Iron Python/PyPy无缘了,关键是“来到危险的C/C++世界”!

关于C++。后文中所有出现“C”的地方,都可以用“C++”代替,实现原理不变,但本文的实现用C语法,如果要移植到C++,需要做几点语法上的调整,留给感兴趣的读者。
关于示例代码。如果先前没有接触过本文介绍的三种方式,快速进入状态的方法就是下载附件中的代码,编译运行调试修改,同时看文档找资料,示例虽简单,但属于麻雀虽小五脏俱全的类型,有助于在这三个领域中继续深入。

简单说就是监控一个目录内文件的变化(访问,打开,关闭,数据修改,属性修改,移动,删除等等),然后打印出(时间,文件名,相关事件)。方案也是现成的,直接用Linux的inotify机制,比如BSD的kqueue也提供了类似功能,但Python标准库没有inotify API,这也正好是Python需调用外部代码的场景之一。

场景。需求已由外部程序实现,Python只需要做简单输入输出的整合。

1. 准备C程序,作为外部代码。













代码行72~73:struct inotify_event表示的真实内容是变长的,但受限于系统文件名长度,可以知道最大长度EVENT_SIZE_MAX,传入的ev_buffer至少保证放入一个事件,否则在新内核(kernel 2.6.21以后)下会报错,如果有新事件,返回的len至少应该是EVENT_SIZE_MIN。
代码行85~86:这两句在测试中是没执行过的,通过终端手动CTRL+C,SIGINT直接终止进程了,系统可以保证清理进程打开的fd,此处仅为展示应有逻辑,但在一些实际要求可重入的环境下,必须考虑合理的清理操作。
代码行103~106:同一个ev下可能含有多个mask位,需要循环处理。

2. subprocess调用。主要工作被外部代码做了,Python只是简单传参和输出就OK,在交互shell演示结果。

Python shell中直接调用,监控当前目录的变化,可以看到setup.py的重命名操作:

*** 参数“shell=True”表示调用系统shell执行命令, 如果执行内容不可控,会有重大安全隐患,避免使用。
*** 
pipe相关的阻塞。实践中可能会用到更复杂的交互,比如与子进程的stdin/stdout/stderr交互,此时要注意可能的死锁,下面是一个简单例子




程序逻辑:每次向cat的stdin写1k数据,若干次后,在从cat的stdout读取回来。如果cnt只是几十次看不出什么问题,几百次后就会发生死锁。
问题原因:内核为pipe分配的空间是有限的,目前一个pipe为64k,双向128k,加上一些用户空间缓存,处理大量数据时很容易爆掉。
解决方案:一种办法就是用另外一个进程或线程处理stdout,工作流就像shell的pipe,连续不断,不会卡壳。

场景。无可用Python模块,只好求助于C API,搞定燃眉之急。

1. 已有第四节subprocess C版本的实现,而ctypes解决问题的途径就是调用C函数,下面的Python实现就水到渠成了。










2. 代码分析。因为本文是讨论“Python调用外部代码”,而这是第一个Python和外部代码有多次交互的例子,需要分析几个关键的地方。不管Python和C语法上的差异,大家可能注意到一些实现细节的改变,这些地方恰好能体现出ctypes功能与局限。
行7~30:定义了所有mask的名称和数值,对比C实现的那个相似结构,这里hardcode了数值,因为Python不认识C的宏定义,这些数值只能人肉从inotify.h中提取,如果实践中遇到更复杂的头文件,而所需要的常量包含在众多的条件编译选项中,建议还是写几行C代码来提取这些数值。
行32~38:依然在处理C中的常量,C的NAME_MAX通过Python的os提取,C的struct inotify_event成员分布可以由Python的struct模拟,初步可以看出ctypes的代价了,如果这类常量定义有任何改变,C实现只要一次重编就搞定,Python实现就是噩梦了!
/usr/lib,其实中间还有其他环节,但实践中通过这三个层次应该定位到自己的DLL了。
3. 输出分析。功能上与第四节的C版本完全一致,只是此处观察目录中几个不同的事件,用make在目录中编译C版本实现,用Python实现观察目录内文件变化。

}

我要回帖

更多关于 简单代码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信