当前位置: 移动技术网 > IT编程>脚本编程>Python > 为什么说Python无所不能?它几乎可以调用所有语言的程序和代码!

为什么说Python无所不能?它几乎可以调用所有语言的程序和代码!

2018年09月18日  | 移动技术网IT编程  | 我要评论

哈尔滨大润发超市,校园混混道路,音速启动皮肤

---恢复内容开始---

为什么说python无所不能?它几乎可以调用所有语言的程序和代码!

 

python 常被称为是胶水语言,主要原因就是其对于系统命令、各种语言的代码和程序都具有良好的调用能力。这里列举了一些在 windows 系统中,常见的接口与调用。在 linux 中,实现起来都非常简单直接,符合直觉。在 windows 中,程序、代码、动态链接库的调用相对比较复杂,因此 python 的这种能力也格外地有用。

为什么说python无所不能?它几乎可以调用所有语言的程序和代码!

 

进群:548377875  即可获取数十套pdf哦!

示例

这里举一个在开源软件中实际应用的例子,是 matplotlib 中 imread 函数调用 pillow 图像读取代码的实例。

试着引入一个包来读图像,不成功就返回空。

def pilread(fname):
"""try to load the image with pil or return none"""
try:
from pil import image
except importerror:
return none
with image.open(fname) as image:
return pil_to_array(image)

参见 github 源码。

邪恶的小技巧

  • 引入一个包,再删除(matplotlib)
# get the version from the _version.py versioneer file. for a git checkout,
# this is computed based on the number of commits since the last tag.
from ._version import get_versions
__version__ = str(get_versions()['version'])
del get_versions

参见 github 源码。

  • 手动版本检验,出错手动报错(matplotlib)
_python27 = (sys.version_info.major == 2 and sys.version_info.minor >= 7)
_python34 = (sys.version_info.major == 3 and sys.version_info.minor >= 4)
if not (_python27 or _python34):
raise importerror("matplotlib requires python 2.7 or 3.4 or later")

参见 github 源码。

用 python 调用 exe

话说有这么一个程序 cos_exe 可以求一个角度的余弦。你想调用这个程序,怎么做?

可以使用 subprosess 包(无需安装)。

文档参见:17.5. subprocess - subprocess management - python 3.5.4 documentation

# test_cos_exe.py
import subprocess
import unittest
class testcosexe(unittest.testcase):
"""docstring for testcosexe"""
def test_result(self):
subprocess.check_output([
'..\..\tcc.exe',
str('cos_exe.c')])
result = subprocess.check_output([
str('cos_exe.exe'),
str('45')])
self.assertequal(result, b'0.70711')
if __name__ == '__main__':
unittest.main()
// cos_exe.c
#include <stdio.h>
#include <math.h>
inline float to_radians(float radians) {
return radians * (m_pi / 180.0);
}
int main(int argc, char const *argv[]) {
float angle;
if (argc == 2) {
/* code */
sscanf(argv[1], "%f", &angle);
}
else {
printf("wrong argument. should be: "%s 45" ", argv[0]);
scanf("%f", &angle);
printf("%0.5f", cos(to_radians(angle)));
sleep(2000);
return -1;
}
printf("%0.5f", cos(to_radians(angle)));
return 0;
}

subprocess 可以调用一个 exe 程序,然后把标准输出和错误输出都接收回来,以字符串变量的形式存储下来。

如果程序复杂,也可以保存成文件,然后读取文件中的数据。也可以直接存成图像、声音等多媒体文件。但要注意这种使用方式会给程序带来很多额外的依赖关系,对程序的稳定性与健壮性损害较大。

示例

同样也举一个 matplotlib 中的例子。matplotlib 中对于 latex 的支持就是通过系统调用实现的。

在查看到当前目录下不存在同名 dvi 文件后,matplotlib 调用系统中的 latex 编译 tex 源文件。如果调用中出现错误,则代理输出。

if (debug or not os.path.exists(dvifile) or
not os.path.exists(baselinefile)):
texfile = self.make_tex_preview(tex, fontsize)
command = [str("latex"), "-interaction=nonstopmode",
os.path.basename(texfile)]
_log.debug(command)
try:
report = subprocess.check_output(command,
cwd=self.texcache,
stderr=subprocess.stdout)
except subprocess.calledprocesserror as exc:
raise runtimeerror(
('latex was not able to process the following '
'string: %s '
'here is the full report generated by latex: %s '
' ' % (repr(tex.encode('unicode_escape')),
exc.output.decode("utf-8"))))
_log.debug(report)

参见 github 源码。

用 python 调用 dll

python 调用简单的 c 语言编译的 dll,怎么做?

可以简单地使用 ctypes 包(无需安装)。

import ctypes
user32 = ctypes.windll.loadlibrary('user32.dll') # load dll
assert(user32.messageboxa(0, b'ctypes is cool!', b'ctypes', 0))
# call message box function

(示例代码来源久远,已不可考,首次使用在该文中:some notes on python and pygame)

相当简单直接。其实只需要用 dependency walker 找到 dll 文件中的函数名,就可以使用 ctypes 包直接调用。(http://dependencywalker.com/)

这里其实揭示了 dll 文件的本质,就是一组二进制代码,函数名就是代码的入口定位符号。

用 ctypes 调用 dll 需要查看 dll 本身的手册,以便了解相关函数的功能和参数。ctypes 本身只是一层接口,不提供相关功能。

示例

opencv 里面,使用 ctypes 调用系统调用,来获得当前进程的 exe 文件名和起始地址。

def getrunningprocessexepathbyname_win32(name):
from ctypes import windll, pointer, pointer, structure, sizeof
from ctypes import c_long , c_int , c_uint , c_char , c_ubyte , c_char_p , c_void_p
class processentry32(structure):
_fields_ = [ ( 'dwsize' , c_uint ) ,
( 'cntusage' , c_uint) ,
( 'th32processid' , c_uint) ,
( 'th32defaultheapid' , c_uint) ,
( 'th32moduleid' , c_uint) ,
( 'cntthreads' , c_uint) ,
( 'th32parentprocessid' , c_uint) ,
( 'pcpriclassbase' , c_long) ,
( 'dwflags' , c_uint) ,
( 'szexefile' , c_char * 260 ) ,
( 'th32memorybase' , c_long) ,
( 'th32accesskey' , c_long ) ]
class moduleentry32(structure):
_fields_ = [ ( 'dwsize' , c_long ) ,
( 'th32moduleid' , c_long ),
( 'th32processid' , c_long ),
( 'glblcntusage' , c_long ),
( 'proccntusage' , c_long ) ,
( 'modbaseaddr' , c_long ) ,
( 'modbasesize' , c_long ) ,
( 'hmodule' , c_void_p ) ,
( 'szmodule' , c_char * 256 ),
( 'szexepath' , c_char * 260 ) ]
th32cs_snapprocess = 2
th32cs_snapmodule = 0x00000008
## createtoolhelp32snapshot
createtoolhelp32snapshot= windll.kernel32.createtoolhelp32snapshot
createtoolhelp32snapshot.reltype = c_long
createtoolhelp32snapshot.argtypes = [ c_int , c_int ]
## process32first
process32first = windll.kernel32.process32first
process32first.argtypes = [ c_void_p , pointer( processentry32 ) ]
process32first.rettype = c_int
## process32next
process32next = windll.kernel32.process32next
process32next.argtypes = [ c_void_p , pointer(processentry32) ]
process32next.rettype = c_int
## closehandle
closehandle = windll.kernel32.closehandle
closehandle.argtypes = [ c_void_p ]
closehandle.rettype = c_int
## module32first
module32first = windll.kernel32.module32first
module32first.argtypes = [ c_void_p , pointer(moduleentry32) ]
module32first.rettype = c_int
hprocesssnap = c_void_p(0)
hprocesssnap = createtoolhelp32snapshot( th32cs_snapprocess , 0 )
pe32 = processentry32()
pe32.dwsize = sizeof( processentry32 )
ret = process32first( hprocesssnap , pointer( pe32 ) )
path = none
while ret :
if name + ".exe" == pe32.szexefile:
hmodulesnap = c_void_p(0)
me32 = moduleentry32()
me32.dwsize = sizeof( moduleentry32 )
hmodulesnap = createtoolhelp32snapshot( th32cs_snapmodule, pe32.th32processid )
ret = module32first( hmodulesnap, pointer(me32) )
path = me32.szexepath
closehandle( hmodulesnap )
if path:
break
ret = process32next( hprocesssnap, pointer(pe32) )
closehandle( hprocesssnap )
return path

参见 github 源码。

用 python 调用 c/c++ 代码

推荐使用 cython 生成并调用 pyd。

其实 python 基本实现就是 c 写的。`#include <python.h>` 之后,其实就可以直接写 python 的扩展了。例如 opencv 就是这么实现的,参见 github 源码。但这需要对 python 内部底层模型比较详细的了解,相对比较麻烦,工程量也比较大。而 cython 的打包方式,相对简单直接得多。

cos_pyd.pyx

# distutils: language = c
# distutils: sources =
# distutils: include_dirs =
# distutils: extra_compile_args =
# distutils: extra_link_args =
# distutils: libraries =
cdef extern from 'math.h':
double m_pi
cdef extern from 'cos_exe.c':
int cos_c(double alpha, double * cos_of_alpha)
def cos_pyd(double alpha):
cdef double cos_of_alpha
cos_c(alpha, &cos_of_alpha)
return cos_of_alpha

pyx 是整个调用工程的组织文件,其中面向 distutils 声明了整个项目的组织参数。包含了所需的头文件与源代码文件。更是写清了对于 python 这个 python 包的组织。

// source.c
#include<math>
int cos_c(double alpha, double * cos_of_alpha){
double cos_of_alpha = 0;
cos_of_alpha = cos(alpha);
return 0;
}

这个 source.c 是示例的一部分,简单写清了输入输出,完成一个简单的功能。也可以拓展为多输入、多输出。

# cos_build_and_app.py
from distutils.core import setup
from cython.build import cythonize
setup(ext_modules=cythonize('cos_pyd.pyx'),
script_args='build_ext -i -t .'.split(' '))
import cos_pyd as c
if __name__ == '__main__':
result = c.cos_pyd(45)
print('the cos of 45 is {0:.5f}.'.format(result))

这个 py 文件,实现了 pyd 工程的编译和调用两个步骤。

在 import 语句以前的上半部分,完成了一个 pyd 包的编译过程。编译后要保存好 pyd 文件,以后在使用的时候,需要把 pyd 放在 python 路径下。

从 import 语句开始,就是一个典型 python 包的使用方法了。通过使用 pyd 就可以直接实现各种功能。

也可以将 pyd 当作一种打包方式,打包发布供二次开发的代码。

复杂工程的调用

再复杂的工程,其实都可以用 c/c++ 封装起来,做成一个工程,然后用 cython 编译成 pyd,都可以做成 python 包,被调用。简单的,其实可以直接用 c 打包,用 ctypes 调用。稍复杂的,再用 cython。

用 python 调用 java 代码

简单的,可以使用 jpype 包。

安装:conda install -c conda-forge jpype1

考虑到网络问题,可以从这里下载 whl:unofficial windows binaries for python extension packages,然后使用 pip install *.whl 安装。

使用

from jpype import *
startjvm("c:/program files/java/jdk1.8.0_144/jre/bin/server/jvm.dll", "-ea")
# java code here
# create a java.lang.string object
javapackage = jpackage("java.lang")
javaclass = javapackage.string
javaobject = javaclass("hello, jpype")
# print the object
java.lang.system.out.println(javaobject)
# call string.length() and integer.tostring() methods
java.lang.system.out.println("this string's length: " +
javapackage.integer.tostring(javaobject.length()))
shutdownjvm()

(示例代码来源于:https://www.quora.com/can-we-write-c-and-java-in-python,有修改)

从调用方式中可以看出,其实也是调用 jvm 的 dll。

复杂的,可以尝试使用 pyjnius。

用 python 调用 matlab 代码

有原生工具,但本质上就是调用 matlab engine 的 dll。

安装

cd "matlabrootexternenginespython"
python setup.py install

调用

import matlab.engine
eng = matlab.engine.start_matlab()
tf = eng.isprime(37)
print(tf)
# true

具体请参考文档。

用 python 调用 r 代码

跟调用 exe 一样,只不过这个 exe 是 rscript。

# run_max.py
import subprocess
# 声明要调用的命令和参数
command = 'rscript'
path2script = 'path/to your script/max.r'
# 把参数(这里是数据)放在一个列表里
args = [str(11), str(3), str(9), str(42)]
# 生成要执行的命令
cmd = [command, path2script] + args
# check_output 执行命令,保存结果
return_str = subprocess.check_output(cmd, universal_newlines=true)
print('the maximum of the numbers is:', return_str)

(示例代码来源于:https://www.mango-solutions.com/blog/integrating-python-and-r-part-ii-executing-r-from-python-and-vice-versa,有修改)

总结

python 的开发,是以 c/c++ 为基础的,所以针对 c/c++ 的调用最为方便。其它程序、动态链接库、代码的调用,都可以通过 exe、dll、c/c++ 三种渠道之一实现。python 还是比较适合粘合各种程序与代码的。

---恢复内容结束---

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网