﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-vibilin-随笔分类-Extract</title><link>http://www.cppblog.com/vibilin/category/11690.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 14 Sep 2009 14:20:15 GMT</lastBuildDate><pubDate>Mon, 14 Sep 2009 14:20:15 GMT</pubDate><ttl>60</ttl><item><title>(摘录)py文件编译为pyc</title><link>http://www.cppblog.com/vibilin/archive/2009/09/05/95368.html</link><dc:creator>vibilin</dc:creator><author>vibilin</author><pubDate>Sat, 05 Sep 2009 05:38:00 GMT</pubDate><guid>http://www.cppblog.com/vibilin/archive/2009/09/05/95368.html</guid><wfw:comment>http://www.cppblog.com/vibilin/comments/95368.html</wfw:comment><comments>http://www.cppblog.com/vibilin/archive/2009/09/05/95368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vibilin/comments/commentRss/95368.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vibilin/services/trackbacks/95368.html</trackback:ping><description><![CDATA[<div id=art style="MARGIN: 15px"><strong><font color=#000099>原文地址</font></strong> <a href="http://blog.csdn.net/sislcb/archive/2009/03/18/4002414.aspx" target=_blank>http://blog.csdn.net/sislcb/archive/2009/03/18/4002414.aspx</a> </div>
<div style="MARGIN: 15px">
<h3>什么是pyc文件</h3>
<p>pyc是一种二进制文件，是由py文件经过编译后，生成的文件，是一种byte code，py文件变成pyc文件后，加载的速度有所提高，而且pyc是一种跨平台的字节码，是由python的虚拟机来执行的，这个是类似于JAVA或者.NET的虚拟机的概念。pyc的内容，是跟python的版本相关的，不同版本编译后的pyc文件是不同的，2.5编译的pyc文件，2.4版本的 python是无法执行的。</p>
<h3>什么是pyo文件</h3>
<span class=body>pyo是优化编译后的程序 python -O 源文件即可将源程序编译为pyo文件 </span><br><br>
<h3>什么是pyd文件</h3>
<span class=body>pyd是python的动态链接库。</span><br><br>
<h3>为什么需要pyc文件</h3>
<p>这个需求太明显了，因为py文件是可以直接看到源码的，如果你是开发商业软件的话，不可能把源码也泄漏出去吧？所以就需要编译为pyc后，再发布出去。当然，pyc文件也是可以反编译的，不同版本编译后的pyc文件是不同的，根据python源码中提供的opcode，可以根据pyc文件反编译出 py文件源码，网上可以找到一个反编译python2.3版本的pyc文件的工具，不过该工具从python2.4开始就要收费了，如果需要反编译出新版本的pyc文件的话，就需要自己动手了（俺暂时还没这能力^--^）,不过你可以自己修改python的源代码中的opcode文件，重新编译 python，从而防止不法分子的破解。</p>
<h3>生成单个pyc文件</h3>
<p>python就是个好东西，它提供了内置的类库来实现把py文件编译为pyc文件，这个模块就是 <span style="FONT-FAMILY: Courier New">py_compile 模块。</span></p>
<p><span style="FONT-FAMILY: Courier New">使用方法非常简单，如下所示，直接在idle中，就可以把一个py文件编译为pyc文件了。(假设在windows环境下)</span></p>
<p>&nbsp;</p>
<p><span style="FONT-FAMILY: Courier New">import py_compile</span></p>
<p><span style="FONT-FAMILY: Courier New">py_compile.compile(r'H:\game\test.py')</span></p>
<p>&nbsp;</p>
<p><span style="FONT-FAMILY: Courier New">compile函数原型：</span></p>
<p><span style="FONT-FAMILY: Courier New">compile(file[, cfile[, dfile[, doraise]]])</span></p>
<p><span style="FONT-FAMILY: Courier New">file 表示需要编译的py文件的路径</span></p>
<p><span style="FONT-FAMILY: Courier New">cfile 表示编译后的pyc文件名称和路径，默认为直接在file文件名后加c 或者 o，o表示优化的字节码</span></p>
<p><span style="FONT-FAMILY: Courier New">dfile 这个参数英文看不明白，请各位大大赐教。(鄙视下自己)原文：it is used as the name of the source file in error messages instead of <var>file</var></span></p>
<p><span style="FONT-FAMILY: Courier New">doraise 可以是两个值，True或者False，如果为True，则会引发一个PyCompileError，否则如果编译文件出错，则会有一个错误，默认显示在sys.stderr中，而不会引发异常</span></p>
<p><span style="FONT-FAMILY: Courier New">(来自python2.5文档)</span></p>
<h3>批量生成pyc文件</h3>
<p><span style="FONT-FAMILY: Courier New">一般来说，我们的工程都是在一个目录下的，一般不会说仅仅编译一个py文件而已，而是需要把整个文件夹下的py文件都编译为pyc文件，python又为了我们提供了另一个模块：compileall 。使用方法如下：</span></p>
<p>&nbsp;</p>
<p><span style="FONT-FAMILY: Courier New">import compileall</span></p>
<p><span style="FONT-FAMILY: Courier New">compileall.<strong>compile_dir(r'H:\game')</strong></span></p>
<p><strong></strong></p>
<p><span style="FONT-FAMILY: Courier New">这样就把game目录，以及其子目录下的py文件编译为pyc文件了。嘿嘿，够方便吧。来看下<strong>compile_dir</strong>函数的说明：</span></p>
<p>&nbsp;</p>
<p><span style="FONT-FAMILY: Courier New">compile_dir(dir[, maxlevels[, ddir[, force[, rx[, quiet]]]]])</span></p>
<p><span style="FONT-FAMILY: Courier New">dir 表示需要编译的文件夹位置</span></p>
<p><span style="FONT-FAMILY: Courier New">maxlevels 表示需要递归编译的子目录的层数，默认是10层，即默认会把10层子目录中的py文件编译为pyc</span></p>
<p><span style="FONT-FAMILY: Courier New">ddir 英文没明白，原文：it is used as the base path from which the filenames used in error messages will be generated。</span></p>
<p><span style="FONT-FAMILY: Courier New">force 如果为True，则会强制编译为pyc，即使现在的pyc文件是最新的，还会强制编译一次，pyc文件中包含有时间戳，python编译器会根据时间来决定，是否需要重新生成一次pyc文件</span></p>
<p><span style="FONT-FAMILY: Courier New">rx 表示一个正则表达式，比如可以排除掉不想要的目录，或者只有符合条件的目录才进行编译</span></p>
<p><span style="FONT-FAMILY: Courier New">quiet 如果为True，则编译后，不会在标准输出中，打印出信息</span></p>
<p><span style="FONT-FAMILY: Courier New">(来自python2.5文档)</span></p>
<br>
<h3>总结</h3>
<p>通过上面的方法，可以方便的把py文件编译为pyc文件了，从而可以实现部分的源码隐藏，保证了python做商业化软件时，保证了部分的安全性吧，继续学习下，看怎么修改opcode。</p>
</div>
<img src ="http://www.cppblog.com/vibilin/aggbug/95368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vibilin/" target="_blank">vibilin</a> 2009-09-05 13:38 <a href="http://www.cppblog.com/vibilin/archive/2009/09/05/95368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(摘录)用Swig让python使用C++库</title><link>http://www.cppblog.com/vibilin/archive/2009/09/05/95367.html</link><dc:creator>vibilin</dc:creator><author>vibilin</author><pubDate>Sat, 05 Sep 2009 05:25:00 GMT</pubDate><guid>http://www.cppblog.com/vibilin/archive/2009/09/05/95367.html</guid><wfw:comment>http://www.cppblog.com/vibilin/comments/95367.html</wfw:comment><comments>http://www.cppblog.com/vibilin/archive/2009/09/05/95367.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vibilin/comments/commentRss/95367.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vibilin/services/trackbacks/95367.html</trackback:ping><description><![CDATA[<pre>原文地址: <a href="http://www.woodpecker.org.cn/obp/pages.wiki.woodpecker/lilin(2f)swig(2d)simple(2d)test/revisions/00000001">http://www.woodpecker.org.cn/obp/pages.wiki.woodpecker/lilin(2f)swig(2d)simple(2d)test/revisions/00000001<br></a></pre>
<pre>
<pre>
<pre>
<pre>= 用Swig让python使用C++库 =</pre>
<pre>== 起因 ==
原以为用不到swig了。以前没有专门看过，只做过个小例子。后来不玩不玩又都忘了。前几天玩pyopengl，发现一个很奇怪的事情。原始的意图是准备不开窗口，直接在一张位图中离屏渲</pre>
<pre>染3D图形。可惜OpenGL中没有定义标准的离屏渲染函数，离屏渲染的工作被扔到各个系统中分别实现。OpenGL有几种主要实现，在X系统上是GLX，在windows系统上是WGL，在Apple上是</pre>
<pre>AGL，它们各自都有支持离屏渲染的API，而且各个系统不一样。OS2上的OpenGL实现PGL仿佛不叫离屏渲染，叫&#8220;获得上个缓冲区&#8221;，大概和离屏渲染差不多。OS2我大概八辈子用不到了。</pre>
<pre>主要看看GLX和WGL大概差不多了。GLX一支持，一大片的系统都支持了。够了。但是着手在WGL的试验中，我发现一个很奇怪的问题。用C++的代码写出的函数没问题。但是仿写成python总</pre>
<pre>是会出现莫名错误。也不知道到底是什么原因，哪块出了问题也实在不好找（因为涉及到pyopengl，pywin32，PIL3个库，PIL可能在这里不是必要的）。后来一想，算了，既然C++不会错，</pre>
<pre>我就包装一个C++的库好了。把离屏渲染的准备工作和扫尾工作放到库中，用Python调用这个库进行初始化离屏渲染的上下文环境和删除上下文环境的工作。具体的OpenGL绘制放到Python</pre>
<pre>中调用(或者全部放到C++中，由Python计算控制视点变化和状态改变？)</pre>
<pre>== 一个最简单的绑定 ==
既然考虑到用绑定了，问题就来了。绑定到底怎么玩？虽然以前玩过，但是一知半解，只是随便玩玩，现在却一点也想不起来怎么弄的了。绑定C++到Python有很多的选择，到底用哪种？
我想起GDAL。这个库就是C++写的，但是通过swig绑定到了Python。现在我都用Python来调用GDAL了。是否可以参考它呢？
研究了一下，发现GDAL的Python绑定比较庞大（本来就比较庞大，它一个库就包含了一大堆的子库，通过静态和动态链接搞在一起，形成一个大库）。研究了一个下午，终于有点懂了（其实</pre>
<pre>可以借鉴一下pyshapelib这样比较小的库，结构比较简单）。它用到的主要是两个东西。一个swig，一个是标准库distutils，swig用来绑定，distutils用来统一编译和安装。
既然用到了这两个东西，我们就来研究一下吧。</pre>
<pre>学习swig可以到[http://www.swig.org/ swig的主站]，里面的[http://www.swig.org/doc.html Document]有很多文档，比较可看的有</pre>
<pre>[http://www.swig.org/Doc1.1/HTML/Contents.html 这个]和[http://www.swig.org/papers/PyTutorial98/PyTutorial98.pdf 这个]，</pre>
<pre>还有一个[http://www.swig.org/tutorial.html 快速教程]，不过通过一个下午的研究发现可以不用太花力气在swig的使用和编译链接上面。swig主要要学的只是&#8220;.i&#8221;文件的写法，其</pre>
<pre>他编译啊什么的都可以放到setup.py中，不过这就是distutils的任务了。</pre>
<pre>学习distutils直接就进入[http://docs.python.org/ Python的官方文档]中察看。[http://docs.python.org/dist/dist.html Distrbuting Python Modules]是教如何</pre>
<pre>写setup.py文件的。它教了我们如何写setup.py文件，然后把我们的模块编译打包。现在仿佛应用最广的就是这种方式。
废话不多说。开始吧！我们做个最简单的。</pre>
<pre>首先要建立一个库，这一定是最先要做的。因为绑定绑定，首先要有个东西来绑，没有要绑的东西，还叫什么绑定！
建库方法可以随便，只要能生成动态链接库就Ok（swig有两种绑定方式，一种静态链接，一种动态链接。静态链接是直接链接到Python核心中去，成为内建模块形式，这一般是不用的，现在</pre>
<pre>又不是非常时期，不需要打入敌人内部:)。一种是动态链接，形成一个模块，然后用的时候用import导入，这是一种我们最熟悉的形式，而大多数的情况下都是这么做的）。</pre>
<pre>我在windows下测试，所以我用vs建立了一个dll工程。我不是一个狂热的控制台一族，我还是喜欢IDE形式的。当然你如果在linux下，可以用anjuta，kdevelop同样建立一个工程，或者</pre>
<pre>你喜欢单用一个文本工具一个字一个字得敲以显示超牛的技术风格，没问题。我们只要能生成库就好。下面说明的都是在windows下的试验过程。</pre>
<pre>建立成dll动态链接库工程!TestModlue，然后把什么预编译头编译开关去掉，删掉那个什么stdafx.h 和 stdafx.cpp，把!TestModule.cpp清空。</pre>
<pre>在!TestModule.cpp中写几个函数吧！
{{{
#include &lt;iostream&gt;
using namespace std;
void __declspec(dllexport)PrintHello(void)
{
cout&lt;&lt;"helloworld"&lt;&lt;endl;
}
int __declspec(dllexport)Add(int i,int k)
{
return i+k;
}
}}}
----
好了，定义了两个函数，一个打印&#8220;helloworld&#8221;一个做加法，这里需要注意的是_ _ declspec(dllexport)。这是函数导出的标志。一定要加这个。不然链接不会出现lib文件。函数导出</pre>
<pre>有两种方法，一种是这个，一种是def模块定义文件，我用前一种只是因为我习惯用那个。不过看起来def的定义方式更容易后期修改。你可以试一试。</pre>
<pre>好了，生成工程。我有一步修改工程属性的步骤。修改了生成dll和lib的路径和名称。默认的路径在Debug目录下或者在release目录下，名称和工程名相同。同名的dll到后面和swig生成的</pre>
<pre>py文件有冲突。所以我把生成目录转移到工程目录下，把lib的名称改成TestModuleD.lib，把dll名称改成TestModuleDCpp.dll，之所以转移目录是为了后面指定目录路径不会太深。在工</pre>
<pre>程名后面加D说明是Debug版本，为了和Release版本区分，你也可以不加，不过后面指定lib的名称时需要注意。dll文件名加Cpp后缀是为了和py文件定义的模块区分。</pre>
<pre>这样我们就有了可以使用的C++库了。
为了把C++绑定到python，要写一个.i文件。跳出!TestModule文件夹，建立一个!TestModule.i的文件，然后写一下给swig用来转化的接口。
{{{
%module TestModule
%{
#include &lt;iostream&gt;
using namespace std;
void PrintHello(void);
int Add(int i,int k);
%}
void PrintHello(void);
int Add(int i,int k);
}}}
----
还是很简单的。%module后面跟模块名。下面%{ %}中间的内容是需要包含的头文件和函数定义。下面是函数的定义声明。
现在可以用swig生成绑定的cpp文件了，不过等等，不要像swig教程那样编译链接，虽然可以这样做，但是我们有distutils了！有炮不用，用鸟枪？
要用distutils，就要建立一个setup.py文件。这个有这个文件，我们就可以简单地使用python setup.py build 和python setup.py install来编译链接，并且安装模块到python系</pre>
<pre>统库中。一切其他的事情都不用你管。</pre>
<pre>建立一个setup.py。
写setup.py 其实很简单，需要的就只有一行（其他别看那么多，全部都是服务于那一行）。
最简单的setup.py 看[http://docs.python.org/dist/setup-script.html 这里]，不过这还不是最简单的，最简单的是这样！
{{{
from distutils.core import setup
setup(name='TestModule wrap',
version='1.0',
ext_modules=[],
)
}}}
----
当然这样什么用都没有。
我们要编译一个模块，就要在setup中的ext_modules参数的列表中添加模块定义，这个模块定义是需要用到Extension类的。模块的定义看起来像这样：
{{{
TestModule_module = Extension('_TestModule',
sources = ['TestModule_wrap.cpp'],
include_dirs = [],
libraries = ['TestModuleD'],
library_dirs = ['./TestModule/'],
extra_link_args = [],
)
}}}
----
第一个参数是模块名，这里是编译出来后的模块名，这个模块名最好不要和原来的模块同名，一般是在前面加个"_"，但是也不要把模块名乱改，不然生成的绑定cpp链接后init方法认不到。</pre>
<pre>sources参数是指定要编译的源文件。这个注意，源文件是经过swig转化后的cpp文件，一般是模块名加个后缀_wrap。include_dirs是指定编译时需要额外包含头文件的路径。libraries</pre>
<pre>是指定要链接时需要用来链接的库，这里因为需要链接c++的库，所以要将TestModuleD.lib链接进来，这里因为上面在编译链接时有改lib的名称，所以这里用的是TestModuleD，如果你没</pre>
<pre>有改，就要用!TestModule，反正要跟lib去掉扩展名后的名称一样。library_dirs指定查找libraries中库所在的路径。最后一个是而外链接标记，不管它。</pre>
<pre>最后经过整理，排版，最后的setup.py是这样：
{{{
from distutils.core import setup,Extension
include_dirs = []
libraries = ['TestModuleD']
library_dirs = ['./TestModule/']
extra_link_args = []
TestModule_module = Extension('_TestModule',
sources = ['TestModule_wrap.cpp'],
include_dirs = include_dirs,
libraries = libraries,
library_dirs = library_dirs,
extra_link_args = extra_link_args,
)
setup(name='TestModuleD wrapper',
version='1.0',
#py_modules=["TestModule"],
ext_modules=[TestModule_module],
)
}}}
----
好了。所有工作做完，现在编译链接。
一般来说，编译链接的工作放到一个makefile中进行，不过这里要进行的工作很少，不用那么兴师动众吧！写个批处理文件就好。
建立一个runsetup.bat，输入下列内容：
{{{
@echo off
swig -c++ -python -modern -new_repr -I. -o TestModule_wrap.cpp TestModule.i
python.exe setup.py build
pause
}}}
----
这里看到只作了两件事，一个是swig生成绑定的cpp，一个是用setup.py 进行编译。
好了，运行这个runsetup.bat吧!
{{{
running build
running build_ext
building '_TestModule' extension
D:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\cl.exe /c /nologo /Ox
/MD /W3 /GX /DNDEBUG -Id:\python24\include -Id:\python24\PC /TpTestModule_wrap.
cpp /Fobuild\temp.win32-2.4\Release\TestModule_wrap.obj
TestModule_wrap.cpp
D:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\link.exe /DLL /nologo
/INCREMENTAL:NO /LIBPATH:./TestModule/ /LIBPATH:d:\python24\libs /LIBPATH:d:\py
thon24\PCBuild TestModuleD.lib /EXPORT:init_TestModule build\temp.win32-2.4\Rele
ase\TestModule_wrap.obj /OUT:build\lib.win32-2.4\_TestModule.pyd /IMPLIB:build\t
emp.win32-2.4\Release\_TestModule.lib
正在创建库 build\temp.win32-2.4\Release\_TestModule.lib 和对象 build\temp.win
32-2.4\Release\_TestModule.exp
请按任意键继续. . .
}}}
----
好了生成成功！我们可以看到生成一个build文件夹，生成的东西放在build文件下的lib.win32-2.4目录下，是一个_!TestModule.pyd的文件。
现在我们试试看python能否正确调用：
建立一个bin文件夹，把生成的_!TestModule.pyd文件和!TestModule.py文件还有那个C++生成的TestModuleDCpp.dll拷贝进来，然后在这个bin目录下运行python交互解释器。
{{{
&gt;&gt;&gt; import TestModule
&gt;&gt;&gt; dir(TestModule)
['Add', 'PrintHello', '_TestModule', '__builtins__', '__doc__', '__file__', '__n
ame__', '_newclass', '_object', '_swig_getattr', '_swig_repr', '_swig_setattr',
'_swig_setattr_nondynamic', '_swig_setattr_nondynamic_method', 'new', 'new_insta
ncemethod']
&gt;&gt;&gt; TestModule.PrintHello()
helloworld
&gt;&gt;&gt; TestModule.Add(1,100)
101
&gt;&gt;&gt; TestModule.Add(77,100)
177
&gt;&gt;&gt;
}}}
----
It work！运行正常！
不过可以看到dir(!TestModule)会有一堆乱七八糟的东西出来。这是怎么回事？
{{{
&gt;&gt;&gt; import _TestModule
&gt;&gt;&gt; dir(_TestModule)
['Add', 'PrintHello', '__doc__', '__file__', '__name__']
&gt;&gt;&gt; _TestModule.Add(77,100)
177
&gt;&gt;&gt; _TestModule.PrintHello()
helloworld
&gt;&gt;&gt;
}}}
----
试试这个！果然清爽很多。</pre>
<pre>因为生成!TestModule是swig的工作，而真正的绑定是在_!TestModule中做的。我们完全可以不要swig生成的py文件，自己写一个。导入_!TestModule中所有的原生态的东西。甚至可以</pre>
<pre>对_!TestModule进行包装！正像GDAL中做的一样，_gdal.dll只是包装了c的绑定，gdal.py没有采用swig生成的那个，而是自己写了一个，在新的gdal.py中对_gdal.dll中的C函数进行</pre>
<pre>包装，形成C++类。而不是直接在Swig的i文件中包装成python类。</pre>
<pre>&nbsp;</pre>
</pre>
</pre>
</pre>
<img src ="http://www.cppblog.com/vibilin/aggbug/95367.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vibilin/" target="_blank">vibilin</a> 2009-09-05 13:25 <a href="http://www.cppblog.com/vibilin/archive/2009/09/05/95367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>