Tchaikov’s Journal

July 26, 2006

IPython 和 Emacs (续)

Filed under: Emacs, Python

上次对 Liu Jin 的 ipython.el patch 的修改被 Fernando 接受了。最新的版本在这儿可以找到。

July 9, 2006

为什么 Python 没有 /* */

Filed under: Python

熟悉一门计算机编程语言的朋友,在学习新的编程语言时,都会自觉或不自觉地希望在新的语言中找到原来那门语言中对应的元素。很自然地,学过 C 的同学,在看待 Python 时,也会这样做。

初看之下,Python 里缺少块注释(block comment)。Python 语言里用#来标志一行里,注释的开始。如果要多行注释?这简单,每一行都用“#”打头不就得了。

有的朋友就感到很困惑,有的朋友觉得这样很累,这几篇 post 已经有好几年历史了。但是,由它带出的一些思考在今天还是有它的意义的。thread 中的发言可以分作两派:

  • 一派就事论事,提供了一些不错的解决办法
  • 用“#”注释 code block 的每一行
  • 把 code block 放在if 0: ... 的缩进块里
  • ”“”或者'''的 triple quotes 把要注释的代码包起来。不过这种办法必须要注意 triple quotes 的缩进,必须服从上下文。另外,被注释的代码不能含有 triple quote(s)。
  • 因为任何像样的编辑器都应该有块注释的功能。就算它没有,也应该能够进行配置,使它能容易地帮助用户加入希望的注释标记。Emacs: C-c # 就是块注释,C-u C-c # 就是取消块注释
  • vim 也有很多办法,可以帮我们注释大块的代码。用 google 大仙能找到大把的方案。
  • 另一派则试图说服楼主,让他理解,每一行都用“#”自有它的好处,而块注释标记则有其让人难堪的时候
    • 如果使用“/**/”来注释大块的源程序,对于浏览代码的程序员来说,看到一行代码就无法快速的知道,这行代码到底是不是注释,只能让程序员“上下而求索”了。不过这个问题对于有良好的语法着色的编辑器来说是不成问题的。
    • 而且“/**/“没有办法包含另一个”/**/“ block。相信大家在写 C++程序的时候肯定碰到过很多这种情况吧。因为里面的“*/“会和外面的”/*“配对,使里面的“*/”之后的代码没有被注释起来。
    • 而且,对于各种没有语法分析功能的文本分析程序(比如 grep,简单的 perl 程序)来说,“/**/”的出现对他们统计和处理代码都带来了挑战。它们无法仅仅根据一行是不是“#”(对于C/C++是“//”)开头的,来判断这一行是不是注释。

    看完两边的发言。掩卷沉思。有两个结论:

    1. 嗯,C++ 引入“//”的确是有它的理由啊。
    2. 生子当如孙仲谋,写程序当用 Emacs/VIM。
    附带着说一句,Emacs 里 python-mode 的缩进可以用 C-c > 和 C-c < 来成块地控制。

    July 5, 2006

    Python 的点点滴滴——?:

    Filed under: Python

    Python 可以用 [false_exp, true_exp][bool_exp] 的方式模拟 C 语言里的a?b:c三目操作符。对于那些不在乎没有被取值,但是也同样被 evaluate 的 {true|false}_exp 来说,是相当合适的。

    但是今天碰上了一件怪事,是在运行那个用 wxPython 写的演示程序时,Python2.3 说:

    return (val/2, [0,1][val > self.upper or val < self.lower])
    TypeError: list indices must be integers
    

    开始的时候打开 ipython (Python2.4)又试了一下,好的啊。再用 Python2.3 试,也没问题。晕菜了,把[0,1][val > self.upper or val < self.lower]改成[0,1][int(val > self.upper or val < self.lower)]。终于闭嘴了,清静了。不过还是不知道他为什么会这样。

    July 3, 2006

    SWIG 和指针

    Filed under: Python, C++

    演示平台需要使用C语言实现的算法。算下来比较成熟的绑定工具就数 SWIGBoost.Python 了。SWIG 是一个通吃型的杀手程序,从 Python 到 Mzscheme 都照顾到了。wxWidgetsPython 绑定就是用的 SWIG。 Boost.Python 则专注于 C++ 和 Python 的互通。考虑到安装的便捷程度和易用性,我还是用了 SWIG。以前在 Windows 上花半个小时,用 jam 编译 Boost,太可怕了。而且这种高阶的 meta programming 不是所有人都能够接受的。嗯,还是 SWIG。SWIG 有直接可以在 Windows 安装的预编译版本,十分方便。用起来也简单。照着这里写一个 interface file:

    module example
    %include \"typemaps.i\"
    int foo(int n, float* OUTPUT, float* OUTPUT);
    

    swig 的文档说的非常明白了。在这里,n是输入参数,两个OUTPUT是用来作为输出的指针。example是 Python 看到的模块名。要编译生成一个 example 模块:

    swig -python example.i # 生成 example_wrap.c 和 example.py
    gcc -c -fPIC example.c # 编译 foo() 实现所在的源文件
    gcc -c -fPIC example_wrap.c -I /usr/include/python2.3
    ld -shared example.o example_wrap.o -o _example.so
    

    这时候,example模块只包含了两个文件:example.py 和 _example.so。在 Python 里照常用就可以了。

    In [1]: import example
    In [2]: example.foo(1)
    Out[3]: [4, 1.0, 2.0]
    

    可以注意到,SWIG 把两个输出参数和返回值并成一个 list 返回了。不知道调用的代价会不会很高,先试试看吧。

    June 5, 2006

    Python, OpenGL 和 Profile

    Filed under: Python

    平台,平台,演示平台。虽然有一万分的不愿意,我还是耐着性子研究如何画界面。而磁敏曲线的界面天生就没有吸引力。先不管这么多吧,从头开始。为了获得和平台图形界面相同的感官效果,我们就用 WxWidget。为了让开发简单快捷,跨平台,语言就用 Python。哈哈!
    WxWidget 画界面应该没有问题。但是我们这里有两个难关。

    • 动态的磁敏波形
    • 以后可能加入的同步视频演示窗口
    对 wxWidget 比较熟悉的朋友应该立即能找到对应的模块,前者是 wx.lib.plot 或者重量级的 matplotlib。后者则是 wx.media 或者 pymedia。接下来把它们简单比较一下。 先说 matplotlib。不久之前,我对 matplotlib 做过测试,显示效果虽然不错,但是 FPS 很低。用 GTK+ 前端的话,只有 40fps,而且 CPU 占用率达到了 100%。如果要平滑地显示磁敏曲线,这样的性能明显是不可接受的。wx.lib.plot 是 wxPython 的嫡系正宗,但是很久没有更新了,而且它的绘图方式应该是用操作系统的 GDI,想来速度不会很好。回头再测试一下。 在搜索相关资料的时候,我发现有人 和我有相同的问题,不过时间已经是 6 年之前了。thread 的主人原来的程序是使用 Tkinter 的,他希望能在1/10秒里画1000条线,但是在他的P166机器上速度太慢了。thread 里有人做了实验,用 Windows NT, P200, 64M 的机器,使 wxPython 跑出了 0.18s 画 1000 条线的成绩。这位仁兄同时还建议试试 OpenGL 和双缓存的方法,以提高速度。还是在这个帖子里。也有人建议楼主用 Canvas.coords 来移动线条,然后再 redraw 这些线。值得注意的是 Guido 叔叔也参与了讨论,他的方法是绕过 Python 的 Tkinter 绑定,直接用 Tcl 的语法调用 <widget>.tk.call(…), Guido 发现这样能让速度提升一个层次。也有人用 OpenGL 和 NumPy获得了每帧几百条线的成绩,但是不知道他每秒能有多少帧? 看完这个 thread,我立即试了一下 PyOpenGL。glut* 函数相当好用,只要在 glutIdleFunc 注册的函数里更新坐标就可以画出动画。没有几分钟我就凑出来个蠕动的 sine 曲线。但是问题在于 sine 曲线运动得太快了,刷新频率太高,同时 CPU 的使用率也是满档。然后把更新坐标的函数改成注册到 glutTimerFunc,就好了大半。接下来就是每秒多少帧和帧间移动多少的权衡了。总体效果还过得去,CPU占用率也很小。 wx.media 自己是无法完成解码的工作的。它借助平台相关的后端进行不同格式媒体文件的解读。Windows 上,后端可以用 wxMEDIABACKEND_DIRECTSHOW,GNU/Linux 上用 wxMEDIABACKEND_GSTREAMER。pymedia 也是一个前端,它自己无法播放视频文件。它借助 pygame 来做这件麻烦事。目前,Debian 还没有收录 pymedia,所以现在先考虑 wx.media。

    June 4, 2006

    Python 的点点滴滴 —— new style class 的 property

    Filed under: Python

    Python 2.2 引入了 new style class,让撰写 OO 的 class 更为简洁,有的方面可以用上更 pythonic 的解决方案。其中一个重要的改进就是增加了 properties 的设计。既然可以直接访问成员变量,为什么要这些累赘的 getter 和 setter 呢?
    要让访问对象的属性操作有更多的灵活性,直接访问对象的数据成员的办法明显有些襟。因此必须通过 getter 和 setter 来包装。因为有的属性无法与对象的成员变量直接对应。它(这类属性)有可能就是需要临时计算得到。比如一个有序队列的最大值。
    第一次看到 property 是在 Delphi (Object Pascal)里。语法和 Python 惊人的相似:

     Property Top    : Longint Index 1 read GetCoord write SetCoord;

    Python 里则是

    top = property(fget=get_coord, fset=set_coord)

    但是要注意的是,new style class 必须继承自 object,否则,当我们高高兴兴地使用 foo.top = 1 的时候,get_coord 是不会被调用的,虽然 Python 一声不吭地接受了你的 property 声明(它的确没有 warning,或者类似 property statement is only available in new style class 的错误警告)。但是,foo.top = 1,仅仅是执行了 foo.__dict__[’top’] = 1 的操作,不多也不少。

    June 1, 2006

    Python 的点点滴滴 — dict 和 %

    Filed under: Python

    这里看来的。print 竟然可以这样玩!

    dict = {\"name\": \"Guido\"}
    print \"Hello %(name)s!\" % dict
    

    可以注意到, %(name)s 中,name 是 dict 的 key,而 s 则是 format string。
    读过 OSCON 2005 Effective Python Programming slides 之后,我看到刚才链接里的文章把 Python 的 Template 模式玩到了新的高度,令人咋舌不已。感叹道:
    Python,你真是太邪门了!

    May 22, 2006

    IPython 和 Emacs

    Filed under: Emacs, Python

    IPython 真个是好东西,方便的“帮助模式”,以及 tab 补全。让我这种只会在 terminal 里输入 python 然后开打的人打开眼界!不过 IPython 虽好,用 Emacs 的人总喜欢尽可能地把所有的事情移到 Emacs 下去做。想想看,也是啊,Emacs 里的括弧对齐,方便的输入方式,在 ipython 里打了一会儿,就怀念起来了。你猜对了,接下来说的肯定是 IPython 提供的 py-shell。
    py-shell 是在 ipython.el 里提供的一个命令,让我们能使用在 Emacs 底下用上 ipython。先介绍一下 py-shell:
    py-shell 的 major-mode 是 Comint 模式(即 command interpreter 模式),在这个模式下用户的输入被 Emacs 收下,喂给指定的 command interpreter(本例中就是 ipython 了),而 interpreter 的输出则被打印到当前的 buffer 中。当然,实际的 Comint 比这个复杂得多,Comint 会过滤输入输出,加入自己的操作比如定义一些 key-map,让自动补全更 Emacs 化。把自动补全显示到专门的 *IPython Completions* buffer 中。
    我们都知道 Python 用缩进来控制程序的结构,如果缩进有问题的话,就无法写很多 Python 程序了。而 py-shell 的缩进却是有问题的。

    In [11]: def wc(file):
      ....: count = 0
      ....: for line in file:
      ....: count += 1
      ....: return count
    

    本来应该这样的:

    In [11]: def wc(file):
       ....:     count = 0
       ....:     for line in file:
       ....:         count += 1
       ....:     return count
       ....:
    

    为了解决缩进的问题,我和 ipython.el 斗争了一个晚上。首先求助于 google 大神,没找到有意义资料。接着在水木上发帖求助,好在有朋友也遇到了这个问题。而且有了补救的方法。他的办法就是在开启另外一个辅助的 python-mode buffer *IPython Indentation Calculation*,在那里面插入和 *Python* buffer 里相同的输入语句,让 python-mode 帮助我们计算缩进的字符串,然后再把这个字符串记下,待 comint 要输出的时候,再把缩进字符串加到输出的提示符后面。这个方法虽然有些绕弯,但是一下子也找不出什么更好的替代方案了。但是补救方法只是在 Windows 的 NTEmacs 上可行,在 Debian 的 Emacs 下却会不正常(第二版的设置根本就无效)。具体的修改:

    • 只是加入了一个 “RET” 的 key map—— ipython-newline-and-indent()
    • 负责更新 *IPython Indentation Calculation*
    • 以及抓取缩进字符串
  • 在 comint-output-filter-functions 加入了 ipython-indentation-hook()
    • 负责检查 ipython 的提示符
    • 并插入正确的缩进字符串
    • 清空缩进字符串

    Liu Jin 的办法是在 ipython-newline-and-indent() 抓取缩进字符串,这个办法可能会有 race condition 的问题。不知道是 ipython-newline-and-indent 先执行完毕,还是 ipython-indentation-hook 先执行。测试下来,ipython-indentation-hook 从没有等到过抓好的字符串。因此,我把抓取字符串的工作从 ipython-newline-and-indent() 移动到了 ipython-indentation-hook(),并作了一些小改动。看起来没问题了,但是用了一阵子,碰到了新的问题。照 Liu Jin 的提示,我在 py-python-command-args 中又加了 -noautoindent。现在看上去没问题了。先将就着使吧。

    Get free blog up and running in minutes with Blogsome
    Theme designed by Jay of onefinejay.com