星期四, 八月 31, 2006

Python Doc Digest

输入一个文件结束符(UNIX kbdCtrl+D,Windows上是Ctrl+Z)解释器会以0值退出。如果这没有起作用,你可以输入以下命令退出:‘import sys; sys.exit()’。


启动解释器的第二个方法是‘python -c command [arg] ...’,这种方法可以在命令行中直接执行语句,赞同于Shell的-c 选项。因为Python 语句通常会包括空格之类的特殊字符,所以最好把整个语句用双引号包起来。


有些Python 模块也可以当作脚本使用。它们可以用‘python -m module [arg] ...’调用,这样就会像你在命令行中给出其完整名字一样运行源文件。


注意‘python file’和‘python <file’是有区别的。对于后一种情况,程序中类似于调用input() 、raw_input() 这样的输入请求,来自于确定的文件。因为在解析器开始执行之前,文件已经完全读入,所以程序指向文件尾。在前一种情况(这通常是你需要的)它们来自于任何联接到Python 解释器的标准输入,无论它们是文件还是其它设备。 


使用脚本文件时,经常会运行脚本然后进入交互模式。这也可以通过在脚本之前加上-i 参数来实现。(如果脚本来自标准输入,就不能这样运行,与前一段提到的原因一样。)


调用解释器时,脚本名和附加参数传入一个名为sys.argv 的字符串列表。没有给定脚本和参数时,它至少也有一个元素:sys.argv[0] 此时为空字符串。脚本名指定为‘-’(表示标准输入)时,sys.argv[0] 被设定为‘-’,使用-c 指令时,sys.argv[0] 被设定为’-c’。使用-m module 参数时,sys.agv[0]被设定为指定模块的全名。-c command 或者-m module 之后的参数不会被Python 解释器的选项处理机制所截获,而是留在sys.argv 中,供脚本命令操作。


------------------------------------------------------------------------------------

Python 的源文件可以通过编码使用ASCII 以外的字符集。最好的做法是在#! 行后面用一个特殊的注释来定义字符集。

# -*- coding: encoding -*-

根据这个声明,Python 会尝试将文件中的字符编码转为encoding 编码。并且,它尽可能的将指定的编码直接写成Unicode 文本。在Python 库参考手册中codecs 部分可以找到可用的编码列表(根据经验,推荐使用cp-936 或utf-8 处理中文)

------------------------------------------------------------------------------------

执行附加的启动文件,可以在全局启动文件中加入类似以下的代码:

import os

filename = os.environ.get(’PYTHONSTARTUP’)

if filename and os.path.isfile(filename):

    execfile(filename)

----------------------------------------------------------------------------------- 

循环可以有一个else 子句;它在循环迭代完整个列表(对于for)或执行条件为false (对于while)时执行,但循环被break 中止的情况下不会执行。以下搜索素数的救命程序演示了这个子句:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...              print n, ’equals’, x, ’ ’, n/x
...              break
...     else:
...          # loop fell through without finding a factor
...          print n, ’is a prime number’
...
2 is a prime number
3 is a prime number
4 equals 2 2
5 is a prime number
6 equals 2 3
7 is a prime number
8 equals 2 4
9 equals 3 3

-----------------------------------------------------------------------------------

执行函数时会为局部变量引入一个新的符号表。所有的局部变量都存储在这个局部符号表中。引用参数时,会先从局部符号表中查找,然后是全局符号表,然后是内置命名表。注意的是,全局参数虽然可以被引用,但它们不能在函数中直接赋值(除非它们用global 语句命名)。

------------------------------------------------------------------------------------

文档字符串的格式:
第一行应该是关于对象用途的简介。简短起见,不用明确的陈述对象名或类型,因为它们可以从别的途径了解到(除非这个名字碰巧就是描述这个函数操作的动词)。这一行应该以大写字母开关,以句号结尾。

如果文档字符串有多行,第二行应该空出来,与接下来的详细描述明确分隔。接下来的文档(从第三行开始)应该有一或多段描述对象的调用约定、边界效应等。

Python 的解释器不会从多行的文档串中去除缩进,所以必要的时候应当自己清除缩进。这符合通常的习惯。第一行之后的第一个非空行决定了整个文档的缩进格式。(我们不用第一行是因为它通常紧靠着起始的引号,缩进格式显示的不清楚。)留白“相当于”是字符串的起始缩进。每一行都不应该有缩进,如果有缩进的话,所有的留白都应该清除掉。留白的长度应当等于扩展制表符的宽度(通常是8 个空格)。

以下是一个多行文档字符串的示例:

>>> def my_function():
...     """Do nothing, but document it.
...
...     No, really, it doesn’t do anything.
...     """
...     pass
...
>>> print my_function.__doc__
Do nothing, but document it.

No, really, it doesn’t do anything.





星期二, 八月 29, 2006

wxPython Digest

Introduction

wxPython is a GUI toolkit for the Python programming language. It allows Python programmers to create programs with a robust, highly functional graphical user interface, simply and easily. It is implemented as a Python extension module (native code) that wraps the popular wxWidgets cross platform GUI library, which is written in C++.

Like Python and wxWidgets, wxPython is Open Source.

wxPython is a cross-platform toolkit.
 

wxWidgets is a C++ framework providing GUI (Graphical User Interface) and other facilities on more than one platform.

wxWidgets was originally developed at the Artificial Intelligence Applications Institute, University of Edinburgh, for internal use, and was first made publicly available in 1992. Version 2 is a vastly improved version written and maintained by Julian Smart, Robert Roebling, Vadim Zeitlin, Vaclav Slavik and many others.

 

Here are some of the benefits with wxPython:

1. Low cost (free, in fact!)
2. You get the source.
3. Available on a variety of popular platforms.
3. Works with almost all popular C++ compilers and Python.
4. Over 50 example programs.
5. Over 1000 pages of printable and on-line documentation.
6. Includes Tex2RTF, to allow you to produce your own documentation in Windows Help, HTML and Word RTF formats.
7. Simple-to-use, object-oriented API.
8. Flexible event system.
9. Graphics calls include lines, rounded rectangles, splines, polylines, etc.
10. Constraint-based and sizer-based layouts.
11. Print/preview and document/view architectures.
12. Toolbar, notebook, tree control, advanced list control classes.
13. PostScript generation under Unix, normal MS Windows printing on the PC.
14. MDI (Multiple Document Interface) support.
15. Can be used to create DLLs under Windows, dynamic libraries on Unix.
16. Common dialogs for file browsing, printing, colour selection, etc.
17. Under MS Windows, support for creating metafiles and copying them to the clipboard.
18. An API for invoking help from applications.
19. Ready-to-use HTML window (supporting a subset of HTML).
20. Dialog Editor for building dialogs.
21. Network support via a family of socket and protocol classes.
22. Support for platform independent image processing.
23. Built-in support for many file formats (BMP, PNG, JPEG, GIF, XPM, PNM, PCX).


wxPython Overview

To set a wxPython application going, you will need to derive an App class and override App.OnInit.

Instances of Dialog can also be used for controls, and they have the advantage of not requiring a separate frame.

You never draw directly onto a window. Instead, you use a device context (DC). DC is the base for ClientDC, PaintDC, PostScriptDC, MemoryDC, MetafileDC and PrinterDC. If your drawing functions have DC as a parameter, you can pass any of these DCs to the function, and thus use the same code to draw to several different devices. You can draw using the member functions of DC, such as DC.DrawLine and DC.DrawText. Control colour on a window (Colour) with brushes (Brush) and pens (Pen).

 


星期日, 八月 13, 2006

Lame 参数详解

LAME 命令行格式: lame [选项] <输入档名> [<输出档名>]



输入选项

-r
指定输入档案是 Raw PCM 格式 (.PCM 档案)。

-x
强迫来源档案的位元组顺序 (byte-order) 调换(压缩跨平台的档案时使用)。如果压缩出来的档案是杂音,可以试试看这一个选项。

-s sfreq
指定来源档案的取样频率(单位为 KHz),预设为 44.1 KHz。只有在输入档案为 PCM 格式时才需要使用,其余情形会自动从档头判断。

--mp1nput
输入的档案是 MP1 格式(利用内建的 mpglib 解码成 Wave 档案,下同)。

--mp2input
输入的档案是 MP2 格式。

--mp3input
输入的档案是 MP3 格式。



操作模式(Operational)选项

-m mode
mode : (s) Stereo  (j) Joint Stereo  (f) Force ms_stereo  (m) Mono
当使用的 VBR 品质为 4-9 或者 CBR <= 160 Kbps 时,预设为 Joint Stereo。其余时则预设为 Stereo。
Stereo: 单纯双声道立体声模式,在此种压缩模式中,LAME 将不会计算双声道之间的资料相关性,但是会协调分配双声道的资料流量,自动分配较多的 Bit 给复杂的声道使用。
Joint Stereo: 在这个压缩模式下,LAME 会利用双声道之间的资料相关性进行演算。左右声道资料类似时,会利用 M/S (Mid/Side) 编码技术,计算中央声道 (L+R) 和两侧声道差异 (L-R) 的值,并且会分配较多的 Bit 给中央声道,增加资料记录频宽。
不当的使用 Joint Stereo 模式可以造成人耳可辨的压缩失真,太多在 Stereo <-> Joint Stereo 之间的切换也不好听。为了决定何时要在两种模式之间切换,LAME 使用了比 ISO 文件复杂许多的演算法进行判别,因此可以放心使用 Joint Stereo 模式。
Force: 强迫所有的 frame 全部使用 M/S Joint Stereo 编码,速度可以增快很多,但是不建议使用。
Mono: 单声道编码模式。

-a
将立体声档案混音成单声道後编码。混音的演算法是将左右声道加总後,衰减 6dB。
这个选项只有在输入档案为 PCM 格式时才需要,其他情形会从档头自动判断。
要将立体声的 PCM 档案编码成单声道时,可以使用 "-m s -a",对於 WAV 或 AIFF 档案,不管是立体声或是单声道输入, 使用 "-m m" 都会编码成单声道模式。

-d
允许各声道有不同的编码区块模式 (block type)

-S
不要印出执行进度和 VBR Bitrate 使用率图表

--disptime seconds
每隔 seconds 秒印出一次执行进度

--freeformat
编码成 Free-Format 格式的档案。
下了此参数後,可以使用 -b 参数,指定压缩成任何 8 以上的整数 Bitrate,但是大部分的 Player 均不支援这种格式的播放。
支援的 Player 列表:
FreeAmp: 440 Kbps max
in_mpg123: 560 Kbps max
l3dec: 310 Kbps max
LAME: 560 Kbps max

--decode
输入 MPEG 或 OGG 档案,解码成 Wave 档。

-t
解码成 Wave 档案时,不写入档头资讯。

--comp arg
自动调整 Bitrate,尽量接近 arg 的压缩百分比率。

--scale arg
将输入的声音资料,放大 arg 倍之後编码。

--athonly
只使用 ATH (Absolute Threshold of Hearing) 作遮罩运算。
这个选项使 LAME 忽略掉心理音响学模型 (Psycho-acoustic Model) 的输出结果,只采用绝对听觉底限作压缩的判断。
在高 Bitrate 的情形下或许可以增进音质,或者是拿来进行实验。

--noath
不使用 ATH 进行遮罩运算,只使用心理音响模型。

--athlower arg
将 ATH 的底限降低 arg dB

--raise-smr arg
0 <= arg <= 1 。调节 SMR 参数。

--short
使用短区块。

--noshort
不使用短区块。

--voice
(实验性)人声编码模式。

--preset type  
使用一些预设的编码参数,可以输入 --preset help 获得详细资料。



CBR选项

-h
高品质模式,启动某些可以增进品质的运算,但是会降低一些速度(建议使用)。
这个选项在 VBR 压缩模式中是预设开启的。

-f
快速模式,但会降低品质(预设)。
杂音成型 (Noise Shaping) 将会被关闭,但是心理音响模型还是会被用来计算 Bit 分配和 Pre-Echo 侦测。

-b bitrate
指定压缩的 Bitrate。可以使用的 Bitrate 如下:
MPEG Layer 1 (32K, 44.1K, 48K): 32 40 48 56 64 80 96 112 128 160 192 224 256 320
MPEG Layer 2 (16K, 22.05K, 24K): 8 16 24 32 40 48 56 64 80 96 112 128 144 160
预设值 MPEG1 为 128Kbps,MPEG2 为 80Kbps。
不管做哪一种设定,为了节省空间,被判断为静音的部分,会自动采用最小的 Bitrate。



ABR选项

--abr bitrate
不指定品质,而指定想要的平均 Bitrate,LAME 会自动使用不同的 frame 大小。 bitrate 许可的范围是 4-310,可以使用这个范围内的任一个整数。
同时可以使用 -b 和 -B 指定变动范围的上下限,像是 lame --abr 123 -b 64 -B 192 a.wav a.mp3 将会限制可以使用的 frame 大小范围在 64-192 Kbits 之间。
使用 -B 是不建议的。 一个 128Kbps 的 MP3 资料流,因为可以使用 Bit Reservoir,所以有一些 frame 其实可以使用到与 320Kbps frame 相同大小的空间。 VBR 并不使用 Bit Reservoir,因此需要允许使用 320Kbps 大小的 frame 才能达到与 CBR 相同的弹性。



VBR选项

-v
使用 VBR 模式。

--vbr-old
使用旧的 VBR 压缩演算法。

--vbr-new
使用新的 VBR 压缩演算法(预设)。

-V quality
指定 VBR 的压缩品质,范围为 0-9 (数字越小品质越高),预设值为 4。

-b bitrate
指定 Bitrate 变动的下限,预设为 32 Kbps。

-B bitrate
指定 Bitrate 变动的上限,预设为 320 Kbps。

-F
强迫遵守 -b 的限制,适合无法解码低 Bitrate 的 MP3 随身听使用。
不启动这一个选项,当输入为类比静音(强度低於 ATH)时,将会忽略最低 Bitrate 的限制,使用压缩格式中的最低 Bitrate。

-t
不写入 Xing VBR 档头,会造成时间总长显示不正确以及搜寻功能失效等问题。

--nohist
压缩档案时,不显示 VBR Bitrate 使用率图表。



MP3 Header/Stream选项

-c
标记档案为有版权 (copyrighted)。

-o
标记档案为非原始档 (non-original)。

-p
错误保护,会在每个 frame 占用 16bit 进行 CRC 编码(可能减损音质)。

--nores
不使用 Bit Reservoir 功能(会严重减损 CBR 音质)。



滤波器(Filter)选项

-k
保留住所有频率。
压缩时,为了把资料流量保留给最重要的频段,提高压缩出来的品质,LAME 会自动随着所选用的 Bitrate 指定高低通滤波器的数值。 如果把频宽设定的比预设值还要大的话,可能会造成无法预期的鸣响以及失真,小心使用!

--lowpass freq
设定低通滤波器的起始点为 freq KHz
高於这个频率的声音会被截除。

--lowpass-width freq
设定低通滤波器的宽度,预设为 15% 的 --lowpass 频率。

--highpass freq
设定高通滤波起始点为 freq KHz
低於这个频率的声音会被截除。

--highpass-width freq
设定高通滤波器的宽度,预设为 15% 的 --highpass 频率。

--resample freq
输出档案的取样频率。

--cwlimit freq
计算音色到指定的频率。





 

星期六, 八月 12, 2006

Python 连接嵌入式(Embedded)FireBird 数据库

版本————
数据库:Embedded FireBird 2.0 beta 3
驱动:KInterbasDB 3.2 for Win

1、将下载的KInterbasDB 驱动解压,运行其.exe安装程序,默认会安装到Python 的“lib\site-packages\kinterbasdb”目录下。

2、在上面提到的“kinterbasdb”目录下创建一个目录“embedded”,然后解压下载来的Embedded FireBird,复制“fbembed.dll”、“firebird.msg”、“ib_util.dll”3个文件到“embedded”目录下——如果需要用到ASCII 外的字符集的话,同时复制“intl”子目录(里面包含“fbintl.dll”文件)到“embedded”目录下)。

3、将“embedded”目录下的“fbembed.dll”文件改名为“fbclient.dll”。
最终的目录结构如下:
Python 的安装目录\
  Lib\
    site-packages\
      kinterbasdb\
        embedded\
          fbclient.dll
            firebird.msg
            ib_util.dll
            intl\            //如果需要的话
              fbintl.dll


4、完成,运行KInterbasDB-Base 的Python 程序就可以连接目标数据库了。

5、Example

import datetime, decimal, os.path, string, sys

import kinterbasdb
kinterbasdb.init(type_conv=200)               #注[1]
# This program never imports mx.DateTime:
assert 'mx' not in sys.modules

def test():
    dbFilename = r'D:\temp\test-deferred.fdb'
    prepareTestDatabase(dbFilename)

    # Connect with character set UNICODE_FSS, to match the default character
    # set of the test database.

    con = kinterbasdb.connect(dsn=dbFilename,
        user='sysdba', password='masterkey', charset='UNICODE_FSS'
    )
    cur = con.cursor()

    # Create a test table.
    cur.execute("""
        create table test (
            a numeric(18,2),
            b date,
            c time,
            d timestamp,
            e varchar(50), /* Defaults to character set UNICODE_FSS. */
            f varchar(50), /* Defaults to character set UNICODE_FSS. */
            g varchar(50) character set ASCII
        )
    """)
    con.commit()

    # Create an input value for each field in the test table.
    aIn = decimal.Decimal('4.53')
   
    # Notice that the DB API date/time constructors in kinterbasdb generate
    # datetime-based objects instead of mx-based objects because of our earlier
    # call to kinterbasdb.init(type_conv=200).

    bIn = kinterbasdb.Date(2004,1,4)
    assert isinstance(bIn, datetime.date)
    cIn = kinterbasdb.Time(16,27,59)
    assert isinstance(cIn, datetime.time)
    dIn = kinterbasdb.Timestamp(2004,1,4, 16,27,59)
    assert isinstance(dIn, datetime.datetime)

    eIn = u'A unicod\u2211 object stored in a Unicode field.'
    fIn = 'A str object stored in a Unicode field.'
    gIn = 'A str object stored in an ASCII field.'

    print '-' * 70
    inputValues = (aIn, bIn, cIn, dIn, eIn, fIn, gIn)
    reportValues('In', inputValues)
    cur.execute("insert into test values (?,?,?,?,?,?,?)", inputValues)
    print '-' * 70
    cur.execute("select a,b,c,d,e,f,g from test")
    (aOut, bOut, cOut, dOut, eOut, fOut, gOut) = outputValues = cur.fetchone()
    reportValues('Out', outputValues)
    print '-' * 70

    # Notice that all values made the journey to and from the database intact.
    assert inputValues == outputValues

def reportValues(direction, values):
    for (val, c) in zip(values, string.ascii_lowercase[:len(values)]):
    varName = c + direction
    print '%s has type %s, value\n %s' % (varName, type(val), repr(val))

def prepareTestDatabase(dbFilename):
    # Delete the test database if an old copy is already present.
    if os.path.isfile(dbFilename):
        conOld = kinterbasdb.connect(dsn=dbFilename,
            user='sysdba', password='masterkey'
        )
        conOld.drop_database()
    # Create the test database afresh.
    kinterbasdb.create_database("""
        create database '%s'
        user 'sysdba' password 'masterkey'
        default character set UNICODE_FSS
        """ % dbFilename
    )

if __name__ == '__main__':
test()



注[1]:
KInterbasDB 为了向后兼容,默认情况下是使用“mx.DateTime”模块,但“mx.DateTime”被明确地表示是不必的!在Python 标准库里的“datetime”模块会更容易使用,完全可以代替“mx.DateTime”!如果用“datetime”代替默认的mx.DateTime”,只需要简单地修改代码:

    “
import kinterbasdb
替换为
    “import kinterbasdb; kinterbasdb.init(type_conv=200)


有一点值得提下,如果你用的Python 版本是低于2.4的话,你还是需要“mx.DateTime”模块的,可以到以下网址下载:http://www.taniquetil.com.ar/facundo/bdvfiles/get_decimal.html









 

星期五, 八月 11, 2006

[转]Python Is Not Java

原文:

I was recently looking at the source of a wxPython-based GUI application, about 45.5KLOC in size, not counting the libraries used (e.g. Twisted). The code was written by Java developers who are relatively new to Python, and it suffers from some performance issues (like a 30-second startup time). In examining the code, I found that they had done lots of things that make sense in Java, but which suck terribly in Python. Not because "Python is slower than Java", but because there are easier ways to accomplish the same goals in Python, that wouldn't even be possible in Java.

So, the sad thing is that these poor folks worked much, much harder than they needed to, in order to produce much more code than they needed to write, that then performs much more slowly than the equivalent idiomatic Python would. Some examples:

A static method in Java does not translate to a Python classmethod. Oh sure, it results in more or less the same effect, but the goal of a classmethod is actually to do something that's usually not even possible in Java (like inheriting a non-default constructor). The idiomatic translation of a Java static method is usually a module-level function, not a classmethod or staticmethod. (And static final fields should translate to module-level constants.)

This isn't much of a performance issue, but a Python programmer who has to work with Java-idiom code like this will be rather irritated by typing Foo.Foo.someMethod when it should just be Foo.someFunction. But do note that calling a classmethod involves an additional memory allocation that calling a staticmethod or function does not.

Oh, and all those Foo.Bar.Baz attribute chains don't come for free, either. In Java, those dotted names are looked up by the compiler, so at runtime it really doesn't matter how many of them you have. In Python, the lookups occur at runtime, so each dot counts. (Remember that in Python, "Flat is better than nested", although it's more related to "Readability counts" and "Simple is better than complex," than to being about performance.)

Got a switch statement? The Python translation is a hash table, not a bunch of if-then statments. Got a bunch of if-then's that wouldn't be a switch statement in Java because strings are involved? It's still a hash table. The CPython dictionary implementation uses one of the most highly-tuned hashtable implementations in the known universe. No code that you write yourself is going to work better, unless you're the genetically-enhanced love child of Guido, Tim Peters, and Raymond Hettinger.

XML is not the answer. It is not even the question. To paraphrase Jamie Zawinski on regular expressions, "Some people, when confronted with a problem, think "I know, I'll use XML." Now they have two problems."

This is a different situation than in Java, because compared to Java code, XML is agile and flexible. Compared to Python code, XML is a boat anchor, a ball and chain. In Python, XML is something you use for interoperability, not your core functionality, because you simply don't need it for that. In Java, XML can be your savior because it lets you implement domain-specific languages and increase the flexibility of your application "without coding". In Java, avoiding coding is an advantage because coding means recompiling. But in Python, more often than not, code is easier to write than XML. And Python can process code much, much faster than your code can process XML. (Not only that, but you have to write the XML processing code, whereas Python itself is already written for you.)

If you are a Java programmer, do not trust your instincts regarding whether you should use XML as part of your core application in Python. If you're not implementing an existing XML standard for interoperability reasons, creating some kind of import/export format, or creating some kind of XML editor or processing tool, then Just Don't Do It. At all. Ever. Not even just this once. Don't even think about it. Drop that schema and put your hands in the air, now! If your application or platform will be used by Python developers, they will only thank you for not adding the burden of using XML to their workload.

(The only exception to this is if your target audience really really needs XML for some strange reason. Like, they refuse to learn Python and will only pay you if you use XML, or if you plan to give them a nice GUI for editing the XML, and the GUI in question is something that somebody else wrote for editing XML and you get to use it for free. There are also other, very rare, architectural reasons to need XML. Trust me, they don't apply to your app. If in doubt, explain your use case for XML to an experienced Python developer. Or, if you have a thick skin and don't mind being laughed at, try explaining to a Lisp programmer why your application needs XML!)

Getters and setters are evil. Evil, evil, I say! Python objects are not Java beans. Do not write getters and setters. This is what the 'property' built-in is for. And do not take that to mean that you should write getters and setters, and then wrap them in 'property'. That means that until you prove that you need anything more than a simple attribute access, don't write getters and setters. They are a waste of CPU time, but more important, they are a waste of programmer time. Not just for the people writing the code and tests, but for the people who have to read and understand them as well.

In Java, you have to use getters and setters because using public fields gives you no opportunity to go back and change your mind later to using getters and setters. So in Java, you might as well get the chore out of the way up front. In Python, this is silly, because you can start with a normal attribute and change your mind at any time, without affecting any clients of the class. So, don't write getters and setters.

Code duplication is quite often a necessary evil in Java, where you must often write the same method over and over with minor variations (usually because of static typing constraints). It is not necessary or desirable to do this in Python (except in certain rare cases of inlining a few performance-critical functions). If you find yourself writing the same function over and over again with minor variations, it's time to learn about closures. They're really not that scary.

Here's what you do. You write a function that contains a function. The inner function is a template for the functions that you're writing over and over again, but with variables in it for all the things that vary from one case of the function to the next. The outer function takes parameters that have the same names as those variables, and returns the inner function. Then, every place where you'd otherwise be writing yet another function, simply call the outer function, and assign the return value to the name you want the "duplicated" function to appear. Now, if you need to change how the pattern works, you only have to change it in one place: the template.

In the application/platform I looked at, just one highly trivial application of this technique could have cut out hundreds of lines of deadweight code. Actually, since the particular boilerplate has to be used by developers developing plugins for the platform, it will save many, many more hundreds of lines of third-party developer code, while simplifying what those developers have to learn.

This is only the tip of the iceberg for Java->Python mindset migration, and about all I can get into right now without delving into an application's specifics. Essentially, if you've been using Java for a while and are new to Python, do not trust your instincts. Your instincts are tuned to Java, not Python. Take a step back, and above all, stop writing so much code.

To do this, become more demanding of Python. Pretend that Python is a magic wand that will miraculously do whatever you want without you needing to lifting a finger. Ask, "how does Python already solve my problem?" and "What Python language feature most resembles my problem?" You will be absolutely astonished at how often it happens that thing you need is already there in some form. In fact, this phenomenon is so common, even among experienced Python programmers, that the Python community has a name for it. We call it "Guido's time machine", because sometimes it seems as though that's the only way he could've known what we needed, before we knew it ourselves.

So, if you don't feel like you're at least ten times more productive with Python than Java, chances are good that you've been forgetting to use the time machine! (And if you miss your Java IDE, consider the possibility that it's because your Python program is much more complex than it needs to be.)


译:

最近我一直在看一个基于wxPython的GUI应用程序代码,大概45.5KLOC的左右,而且这还不包括它所用到的库(如Twisted)。代码是由那些对Python比较生疏的Java的开发者写的,所以它存在很严重的性能问题(如三十秒的启动时间)。在检查代码的时候,我发现他们写了很多在Java中能讲得通但是对Python来说去却是很难接受的东西。并不是因为“PythonJava慢”,而是因为在Python中有更方便的方法去完成同样的目标,甚至是在Java中不可能的事情。
所以,令人难过的事就是这些家伙事倍功半,写的那些代码比本应合乎用Python语言实现的慢很多。下面,让我们来看一些例子:

Java中的静态方法不能翻译成Python的类方法。哦,当然,他多多少少也能产生同样的效果,但类方法的目的实际上是做一些通常在Java中甚至都不可能的事情(如继承一个非默认的默认函数)。Java静态方法惯用的翻译通常翻译成一个模块级的函数,而不是一个类方法或静态方法。(并且静态常量应该翻译成模块级常量.)
这不是性能上的问题,但是一个Python程序员如果想调用Foo.someMethod,他要是被迫采用像JavaFoo.Foo.someMethod的方式去做的话,那么他就会被逼疯的。有一点一定要注意:调用一个类方法需要一个额外的存储空间,而调用静态方法或函数就不需要这样.
对了,还有就是这些Foo.Bar.Baz的属性链也不是自己就能数出来的.在Java中,这些带点的名称是有编译器来查找的,运行的时候并不会去考虑一共有多少.而在Python中,查找的过程是在运行时进行的,所以要包括每个点.(在Python中,要记住一点,"平铺的结构比嵌套的要好",尽管相对于从性能方面来说,可能它更多涉及的是"可读性"和"简单要比复杂好".)

要使用switch语句吗?Python翻译将是一个哈希表,不是一堆if-then语句。要使用在Java中不是switch语句而且还有字符串参与了的一堆if-then语句吗?它将仍然是一个哈希表。Python字典是用在我们所了解的领域中认为是最佳性能之一的哈希表来实现的。你自己所写的代码也不会比这个再好了,除非你是Guido、Tim Peters和Raymond Hettinger的私生子,而且还是遗传增强了的。

XML不是答案。它也不是一个问题。现在用正则表达式来解释Jamie Zawinski,“一些人,当他遇到一个问题的时候,就会想‘我知道,我要用XML.’那么他们就有两个问题了。”
相对于在Java中来说这是个不同的情况,因为比起Java代码,XML是灵活而且有弹性的。但比起Python的代码来,XML就是一个船锚,一个累赘。在Python中,XML是用来协同工作的,而不是你的核心功能,因为你不需要那么做。在Java中,XML可能是你的救世主,因为它让你实现了特定领域的语言并且“不用编码”就提高你的应用程序的适应性。在Java中,避免编码是一个很大的优势,因为编码意味着重新编译。但在Python中,通常是,写代码比写XML更简单。还有就是Python处理代码要比处理XML快很多很多。(不仅仅是这个,你必须写XML处理代码,同时Python就已经为你写好了.)
如果你是一个Java程序员,你并不能利用本能知觉来考虑你是否要在你的Python核心应用中使用XML作为一部分。如果你不是因为信息交互的原因去实现一个已经存在的XML标准或是建立某种输入、输出格式或者建立某种XML编辑器或处理工具,那么就不要这么做。根本不要去这么做。甚至连想都不要想。现在,丢掉那个XML模式然后把你的手解放出来吧!如果你的应用程序或者平台要被Python开发者使用,他们只会感谢你不要在他们的工作中添加使用XML的负担。
这里唯一的例外是如果你的客户(your target audience)确确实实因为某些原因而需要使用XML。就好像,他们拒绝学习Python但如果你使用XML他们就给你付钱,或者你打算给他们一个很棒的能编辑XML的GUI,还有就是这个XML的GUI是另一个人写的,同时你得到免费使用的权利。还有一些很少见的架构上的原因需要用到XML。相信我,它们不会应用到你的程序中去的。如果有疑问,对一个资深的Python开发员解释你的用例。或者,如果你脸皮厚而且不介意被人嘲笑的话,试试向一个Lisp程序解释你的程序为什么要用XML!

Gettersetter是恶魔。我应该说它是恶魔,是魔鬼!Python对象不是Java Bean。不要写什么gettersetter,而是还把它们内置在“属性”里面。它直到你能证明你需要比一个简单访问复杂一点的功能时才有意义,要不然,不要写gettersetter。它们是CPU时间的浪费,更要紧的是,它们还是程序员宝贵时间的浪费。不仅仅对于写代码和测试的人,对于那些要阅读和理解它们的人也是。
在Java中,你必须使用gettersetter,因为公共字段不允许你以后改变想法再去使用gettersetter。所以,在Java中你最好事先避开这些"家务杂事".在Python中,这样做很傻,因为你可以以一个普通特性开始并可以在任何时间改变你的想法,而不用影响到这个类的任何客户。所以不要写gettersetter方法。

代码重复在Java中通常来说就是一场不可避免的灾祸,你必须经常反复地写同一个方法而只有一点点的变化(通常是这是因为静态类型约束)。在Python中这样做是没有必要的也是不值得的(除了极少数一些特定的场合需要内联一些要求性能的函数)。如果你发现自己一遍一遍在写同样的代码而且变化很少,你就需要去学一下闭包。他们实际不并是那么可怕。
这就是你要做的。你写了一个包含了函数的函数。这里内部的函数就是你要一遍遍写的函数的模版,但是在里面加入了针对不同情况的函数要使用变量。外部的函数需要刚刚提高的那种变量作为参数,并且将内部的函数作为结果返回。然后,每次你要写另一种略微不同的函数的时候,你只要调用这个外部的函数,并且把返回值赋给你要让“重复”函数出现的名字。现在,如果你需要改变这个工作方式,你只需要改变一个地方:这个模版。
在我所看过的应用程序/平台中,只有一个很微不足道的程序使用了这个技术,它去掉了数百行重负的代码。实际上,因为开发者使用了特别的样板文件来为这个平台开发插件,所以这会节省很多很多第三方开发人员的代码,同时也使那些程序员要学习的东西变得简单了。

这只是Java->Python思维方式转变的冰山一角而已,现在我能正确的转变而不用去钻研程序的细节。本质上,如果你曾经用过一段时间Java,而且对Python比较陌生,那么你不要太相信自己的本能。你的本能已经被Java调节,而不是Python。向后退一步来说,最重要的是不要再写这么多代码了。
为了这样做,让自己觉得更加需要Python。假装好像Python是可以做任何你想做的魔棒,而你无须出一点力。问一下,“Python怎样解决我的问题?”还有“Python语言的哪个特点和我的问题最相似?”如果对于你需要的东西其实已经有了某种固定形式,那么你绝对会感到惊讶的。事实上,这种现象实在是太普遍了,甚至即使在很有经验的Python程序员中也会出现,以至于Python社区中给这种现象起了个名字。我们称之为“GUIDO的时间机器”,因为在我们自己还没有掌握它之前,通常看上去要得到我们所需要的东西好像那是唯一的方法。

所以,如果你在使用Python时候不能感到比使用Java要至少多出10倍的生产力话,你就最好做一下改动,你是不是忘记使用time machine!(chances are good that you've been forgetting to use the time machine)(同时如果你还怀念你的Java IDE,你可以这样想:因为你写的Python程序比他所需要的要复杂得多.)

译者注:Phillip J. Eby的blog在国外软件开发领域影响很大,广为流传的两篇文章是:“Python Is Not Java”与“Java is not Python, either...”,我会陆续为大家奉上。Phillip J. Eby还是一位多面手,除了在Python上的深厚造诣外,在心理学研究上也颇有建树,著作有《You, Version 2.0》

注:
原文的链接:http://dirtsimple.org/2004/12/python-is-not-java.html
翻译来自CSDN,链接:http://blog.csdn.net/pjeby/archive/2006/08/06/1028334.aspx

星期四, 八月 10, 2006

[修]关于Java 类的载入

构造类的时候可以动态地载入内存,这样可以从一定程度上提高效率,所有类的载入都需要有ClassLoader 。

显式的动态载入类有两种方法:一种是Class.forName() ,一种是ClassLoader.loadClass() ,它们得到的都是Class 对象,也就是相应类的元数据,然后调用newInstance() 方法就可以构造这个类的对象了。这两种方法的原理都是用一个现有的ClassLoader来载入类。我们可以直接使用默认的ClassLoader 来载入类,也可以实现自己的ClassLoader(URLClassLoader 就是一个例子) 来载入。

对于默认的ClassLoader 载入类:JVM 有三个ClassLoader,分别是Bootstrap Loader(这个装载器是C++ 写的,直接与JVM 打交道),ExtClassLoader,AppClassLoader。默认情况下先请求AppClassLoader 来搜索class 文件,AppClassLoader 再向其parent 类ExtClassClassLoader 请求,ExtClassClassLoader再请求其parent“类”Bootstrap Loader 搜索。如果父装载器在其搜索路径里找不到class文件就返回再由子装载器搜索,如果一直到AppClassLoader 找不到就会抛出NoClassDefFoundError。这就是ClassLoader Hierarchy,简而言之就是所有转载请求一直发送到Bootstrap Loader,然后找不到才由子装载器装载——这是一个安全的类载入机制!!确保Java 核心类的载入,而不会被不法的类“冒名顶替”!!

下面介绍3个转载器的搜索路径:
Bootstrap Loader:System.getProperty("sun.boot.class.path") 得到,默认是jre/classes。修改的方法是执行程序的时候使用java -Dsun.boot.class.path=yourPath XXX
ExtClassLoader:System.getProperty("java.ext.dirs") 得到,默认是jre/lib/ext。修改的方法是执行程序的时候使用java -Djava.ext.dirs=yourPath XXX
AppClassLoader:System.getProperty("java.class.path") 得到,默认就是环境变量中的CLASSPATH。修改方法一个是修改环境变量,一个执行程序的时候使用java –classpath yourPath XXX

了解了这些以后我们就可以对类的装载有一定了解,以后放置第三方jar包的时候就能清楚放在什么目录最合适。还有值得一提的是,JDBC驱动和JNDI的类装载涉及ContexClassLoader,它跟上面的原理是不一样的。