data-directed与message passing,及OO

SICP第二章提到了data-directed programming和message passing,及它们的关系。正好最近客户那边重构代码天天提data driven,而且message passing这个词一下让我想到了Smalltalk和OO,所以就把头绪理一下。

考虑一个非常常见的场景:你有两个不同的数据结构,它们其上各自有一组类似但不相同的操作。显然这些操作是针对于各自的具体数据类型的。现在的要求是:

  1. 提供一组通用操作作为这两个数据结构的外部接口。
  2. 要考虑将来加入新的具体数据类型对系统的影响。
  3. 要考虑将来加入新的操作对系统的影响。(感觉OO老是故意回避这个问题。事实上接口变动是很正常很频繁的。当然我设计模式学艺不精,请指正。)

 

按照OO和设计模式的思路,我们应该使用adapter设计模式。Adapter要实现某抽象数据类型,而这个抽象数据类型就是外部接口。client code仅使用此抽象数据类型,这样第一个要求就满足了。至于新加入的具体数据类型,大不了再加adapter就是,对现有代码无需做改动。不过加入新的操作就很麻烦了,每个具体数据类型都要改,抽象数据类型也要改。当然如果新加入的操作对每个具体类型都一样,可以放进抽象数据类型里。

在语言的实现方面,这需要是一个dynamic dispatch/binding的机制。Java和C++都采用了虚函数表,将对于抽象数据类型的操作在运行时映射到具体数据类型。Smalltalk和Python则采用了完全的动态也就是运行时查找,基本就是大查哈希表。

好吧现在我们剥掉OO那个外壳,回到Scheme的朴素思路。还是得有个抽象数据放在这儿当接口,在Scheme中没有object这个把数据和操作绑一起的东西,这个抽象数据其实就是一组通用操作。但现在语言不再自觉地替我们做这些dispatch,或者说将抽象映射到具体的工作了,我们得自己动手了。所以首先最弱智的我们可以用那一手generic operations with explicit dispatch:我们定义通用操作,然后在在其中显式的判断具体数据类型,再调用具体的操作。这一手确实把client code隔离开了,也就是满足了第一条,但显然每加一个具体数据类型都得在每个通用操作里加一个判断分支,没有满足第二条。这就是书中说的not additive。不过如果加一个操作的话还是挺方便的哈,老代码基本不用改。

第二手是用data-directed。其实质就是把那些判断也就是dispatch拿出来放一表或者说registry里。此表以同一具体数据类型的不同操作为列,以不同具体类型的相同操作为行(看SICP 181页或者自己画一个)。表中放的是某一具体数据类型的具体操作。通用操作不再充斥着写死的判断语句,而是接受操作名称作为参数,并从具体数据中获得类型信息,然后查表取得针对具体数据类型的操作并执行之。这样dispatch就成隐式的了,就是一个查表的过程。

所谓的data driven跟这个差不多就一个意思:把代码和数据严格分开,用数据驱动程序,新具体数据类型加进来的时候代码就不用改了。这样的代码就是additive的。详见ESR的Unix编程艺术和Wikipedia。

最后说message passing。它与generic operations with explicit dispatch是相对的。GOED是在operations里包含dispatch,其实质是,每一个operation其实就是data-directed里面的那个表里的一行,根据传入的数据类型自行解决dispatch问题。MP则是,把这个表分割成列,每个data type根据传入的operation自行解决dispatch问题。

在Scheme里表现出来就是,将数据表示为过程(带状态的过程,也就是闭包),其参数是operation。过程内部显式判断operation名称,再做或者调用具体操作。这其实已经是典型的OO特征了。显式判断operation名称就相当于调用实例方法啊!!!相比data-directed,显然是写死了。像Java这种OO语言呢里那些方法名不就是写死的么?当然动态语言除外。Python有个dict大家都知道。所以Scheme这里(object 'method)就相当于Java里object.method()啊!只不过这里dispatch是我们自己写的!而且这个dispatch不是动态的。

哈,object果然就是闭包!

然后SICP里为了提供Scheme风格的数据抽象,对MP也写了个apply-generic函数。据此我们可以再把那些通用操作写出来。比如

(define (real-part z) (apply-generic 'real-part z))

当新的具体数据类型加入时,我们需要写新的过程即可。但加入新操作就费劲了。注意MP和上面OO的局限多么的一致!其实它们的内在就是一样的!

基本思想是,如果我们把数据(具体数据类型)和代码(操作)视作正交,那么GOED和MP各自按住了一个,或者说基于其中一个做模块化针对另一个做dispatch所以遇到它们dispatch的那个东西要增加或者减少时,它们划分好的模块就被切了,怎么整都不爽。(想想那个表在按列切的时候多了一行,或按行切的时候多了一列,等于跨了已划分好的模块。)data-directed则是按照这两个维度做模块化,切得更细,需要两个坐标(具体数据类型,操作)才能找到目标。用这个思想看2.76那道题,一目了然。

私以为经验的重要性大致等同于知识,但二者皆低于思想。

favor composition over inheritance?

无数设计书都叫嚣着这个标题,是为什么呢?

我觉着是因为大多数OO语言里,继承关系是不能在运行时动态改变的,但composition就可以啊,君不见那么多的Java getters/setters。

但如果继承关系能在运行时改变呢??比如Python这样的,且不说把基类换掉这种极端做法了,就说运行时可以override父类方法,我觉得就避免了Java中继承带来的问题啊。这样的话,Strategy模式还有意义么?有么??没有么??

说的抽象一点,如果语言本身不够灵活,你就只能利用它有限的一点灵活度,来尽量避免一些僵硬的设计。但如果语言足够灵活呢?我倒不是说这一定是好事。因为语言灵活,实现一个设计就会有好多灵活的方案,尤其像老潘这样喜欢玩儿数据和代码互换的,准保写出代码来让人丈二和尚。

所以说Java的方案是:语言僵死,然后拿一堆设计模式来进补。。。这样倒也好,适合工业大规模开发哈。但这样的话Java程序员就特别容易把事情想死,以为天下就只有这一种办法了。所以如果你是个Java程序员,你一定要小心脑电路栓塞或者冠状思想硬化这样的毛病。我见过的不在少数。

我现在特别庆幸我是先学动态语言,后学设计模式。。。

常用Python周边

常用库及工具:

wxPython http://www.wxpython.org

SciPy http://www.scipy.org/

NumPy http://numpy.scipy.org/

ipython http://ipython.scipy.org    Windows下需要PyReadline支持 http://ipython.scipy.org/moin/PyReadline/Intro

setuptools http://peak.telecommunity.com/DevCenter/setuptools    必须得说说这个。这东西是Python标准的distutils的一个enhancement,支持egg包格式。与此捆绑的一个easy_install是用来“下载,构建,安装,管理”Python包的。感觉其角色和Debian的apt差不多。

如果你去看setuptools的安装说明,它会把你指向easy_install的安装说明,而后者又会把你指到pypi去。。。我不知道维护者知不知道什么叫用户体验。。。而且不得不说网页排版太差了。。。让人没有阅读的欲望。

在Windows平台下应该是提供exe格式的installer,双击执行即可。问题在于,有时候installer会滞后一点。比如此时是09年6月11日,最新版setuptools是0.6c9,installer只支持到Python2.5,而我机器上是2.6,这个版本提供的安装文件是egg格式,但是没有setuptools又不能安装egg格式。。。死循环。所以提供了一个ez_setup.py来解决这个问题。。。直接运行该脚本即可完成setuptools的安装。但很闹心的是他们没把这事在显眼的地方说明。。。还得赖排版。

py2exe http://www.py2exe.org/

pyinstaller http://www.pyinstaller.org/

pywin32 http://sourceforge.net/projects/pywin32/

几个IDE(都比不上Vim顺手):

PyDev (Eclispse)

PyScripter

Komodo

SPE

Eric

 

wxPython wrappers

Dabo

PythonCard

Wax

functional风格的多重循环

受Scheme启发得来的Python版。。。

map(
    lambda i: map(
        lambda j: (i, j),
        range(100, 110)),
    range(0, 9))

说白了就是像map,filter这种东西本身就隐含了循环的成分。集大成者list comprehension更是如此。 

Python的语言内置AOP

在语言级别支持AOP就是爽!不过这里面的规则超级复杂啊啊啊啊啊啊!!!记不住只好写下来。要理解这些,首先要明白Python中的type和class(new-style class已经和type统一了)自身也是在程序空间中可以被操纵的object。

主要手段有:

__get__和__set__(其实还有__delete__)

__getattr__(类似method missing)

__setattr__(虽然和上边那个长得像,但其实这俩很不一样,不放一起)

__getattribute__(和__setattr__一样都是终极大法)

1. __get__和__set__ 是定义在descriptor(http://docs.python.org/tut/node18.html)的class/type中的。

Any new-style object that defines the methods __get__(), __set__(), or __delete__(). When a class attribute is a descriptor, its special binding behavior is triggered upon attribute lookup. Normally, writing a.b looks up the object b in the class dictionary for a, but if b is a descriptor, the defined method gets called. Understanding descriptors is a key to a deep understanding of Python because they are the basis for many features including functions, methods, properties, class methods, static methods, and reference to super classes.

先说说怎么正确定义descriptor。下文说的class都是new-style的。那些descriptor的特殊方法(__get__, __set__, __delete__)必须定义为class/type object attribute,之后通过实例化得到这些方法的instance object,才被视作descriptor。换句话说,假如我就搞一个object出来,在上面直接安一个__get__,此object将不被视作descriptor(所以,定义了这三个方法的class/type object本身并不是descriptor。上面的英文描述并不完全准确。我太刨根问底儿了)。

光有了descriptor还不行,descriptor只有作为class/type object attribute才起作用。直接在某object上安一个descriptor,是达不到目的的。

这些都做好了之后,才是怎么引发那三个特殊方法。如果前面做的都对,那么从在instance object上对descriptor做reference, bind/rebind, 和unbind操作,会分别引发__get__, __set__, 和__delete__。此外,在class/type object上对descriptor做的reference操作也会引发__get__。

规则好复杂。。。龙与地下城的感觉又回来了,口胡。

2. __getattr__,__setattr__,以及__getattribute__
这三个也是必须定义为class/type object attribute,通过实例化得到这些方法的instance object,当其attribute被bind/rebind时,会被__setattr__拦截。当instance attribute被reference并且没找到时,触发__getattr__。

__getattribute__则拦截所有instance attribute reference,并且优先于__getattr__。后面我会说为什么。

3. 如果1和2同时出现。。。然后我们从instance object去bind/rebind或者reference那个descriptor的attribute:
bind/rebind时,__setattr__优先;reference时,既然有了descriptor,那肯定不会attribute找不到,因此__getattr__不会被调。但是__getattribute__仍优先于descriptor的__get__。

其实所谓谁优先什么的,嘿嘿,__getattribute__和__setattr__都是定义在built-in的object里面的。实际上descriptor包括__getattr__那些小把戏就是它们俩实现的。。。所以你把它们俩override以后,descriptor当然就不管用了。不信你在你的__getattribute__/__setattr__里面调object的那个,descriptor就又会冒出来了。。。看我下面最后一个测试代码,里面有。 

也就是说只有__getattribute__和__setattr__是需要解释器特殊支持的,别的都可以用这俩实现。上面我说的那些优先级根本不用记。只要规则记住了就能用好这些。 

强烈建议Guido把__setattr__改成__setattribute__。。。现在这样不是添乱麽! 

测试代码:

test_descriptor.py test_getsetattr.py test_getsetattr_and_descriptor.py

Python import到底干了些什么

http://effbot.org/zone/import-confusion.htm

虽然这里讲了一些,但是要想完全明白,就得理解Python module也是object:和所有的instance,class/type一样。另外还要明白,当一个Module被import时,其中的statement被执行。

关于虚函数,多重继承,Python中的super

继承和多重继承(http://en.wikipedia.org/wiki/Multiple_inheritance)是language features,是面向对象技术为了使程序向现实贴近的一个手段。不过在面向对象理论中多重继承也不是必须的,基本上所有可以用多重继承实现的,都可以通过单继承来work around。这句话更多来自于实践而不是严格的理论。

从继承的实现角度来说,当子类对象被初始化时,实际上有一个父类对象也被初始化并且组合到子类对象中。这种来自继承的组合和普通的组合的不同之处主要在于,我们是仅仅需要使用父类提供的功能,还是说也需要使用父类提供的接口。

有继承就有多态。C++为了实现多态引入了late/dynamic/runtime binding(Connecting a function call to a function body is called binding):编译时,编译器不知道通过一个基类引用调用的某函数,具体调用的到底是那段代码。所以传统的非OOP编译器的early/static binding机制就行不通了。C++中实现late binding的手段是虚函数。Python和Java中所有的函数/方法实际上都是虚的。所以说虚函数只是实现多态的一个(并不唯一的)技术手段。

具体的实现机制是,编译器为每一个包含虚函数的类(包括通过继承得来虚函数的类)都生成一段分配空间并初始化VTABLE(就是所谓的虚函数表)的代码。于是在运行的时候内存中就会有这样的VTABLE,它们包含了该类中所有虚函数的实现代码的地址。这是非常合乎逻辑的,因为同一个具体类的所有对象,其虚函数实现都是一样的,所以一个类只需要一个虚函数表。

接下来,每当编译器在源代码中看到new一个对象的时候,都会做一些小偷小摸的事:它会生成一些额外的代码,在对象中在分配空间给一个指针成员变量VPTR(对程序员是不可见的!!!),通常被放在对象内存布局最开始的地方,也就是this指针指向的地址。这个VPTR指向本对象的类所属的VTABLE。这部分代码被十分自然的放在构造器中,所以运行时new一个对象出来,对象被初始化后,这个VPTR就已经ready好了。明白了吧!这就是为什么如果程序员不写构造器,编译器会搞个默认构造器出来。Java中也是这样。

但是VTABLE的地址是在运行时才available的。初始化VPTR这段代码必须在运行时获得本对象的类型信息才能知道正确的VTABLE是哪个!所以编译器确实是插入了类型信息到对象中的。在运行时的时候可以获得。题外话:这也是RTTI的本质。

前面的说法有一个hole,就是如果一个对象多重继承怎么办?这样对于同一个对象就会有多个VTABLE。最直觉也是最家常的做法,就是搞多个VPTR出来,也是都放在对象头部。但这时就需要pointer fixups即thunks(http://en.wikipedia.org/wiki/Virtual_table),即当用不同的类型去引用该对象时,使用不同的VTABLE。

接下来的事情就简单了:当通过一个基类指针调用虚函数时,不管对象的具体类型是啥,总能找到正确的VTABLE,通过其找到被调用函数的正确地址,并调用之。

从语言使用者的角度来看,virtual只是一个关键字。使用者实际上不需要知道虚函数是怎么实现的。但由于虚函数调用会有额外的开销,有心的程序员会去研究它的实现并且在适当的时候才使用。

所谓纯虚函数,就是只有声明没有实现的函数。包含至少一个纯虚函数的类就是抽象类。

多重继承引入了二义性问题。最典型的菱形问题http://en.wikipedia.org/wiki/Diamond_problem。

C++使用虚函数来解决多态调用,使用了虚基类来解决菱形问题。

Python则规定了一个特别的method resolution order来摒除二义性:http://en.wikipedia.org/wiki/Diamond_problem。

Python中的super http://fuhm.net/super-harmful/ 

程序语言的类型系统

类型系统的本质是,赋予计算机中那些莫名二进制数据一些意义。它定义了这些东西代表了啥,并且规定了在特定类型上能做哪些操作。编译器/解释器可以检查对象的类型,也可以不检查;可以在编译时检查,也可以在运行时检查。

  1. object/value 对象/值
    • a. 程序员在程序空间中可操纵的东西
    • b. 对应一些内存空间
    • c. 下文中的“对象”和“值”可互换。一般我会用“对象”
  2. class 类
    • a. 类只不过是用户定义类型罢了
    • b. 实例的类型就是对应的类
    • c. OO中的继承经常基于类。但Self和JavaScript则是基于prototype,比较另类
    • d. 用户定义类型是一个语言feature。要提供这个feature,不一定非得提供类机制。比如JavaScript就是用函数定义不同的对象,用prototype实现继承(代码重用)。
  3. type 类型
    • a. 每一个对象都有一个或多个类型(面向对象语言一般都支持一个对象有多个类型,尤其是用户定义类型即class)
    • b. 类型这个东西在某些语言中,程序员可以操纵,即它们也是对象。在另一些语言中则不是
    • c. 前一种情况的例子包括Python, Java。不过Java只为操纵类型提供了很弱的支持,并且primitive类型无法操纵(虽然有wrapper class)。我勉强把Java归为这一类。Python中的类型则是第一级(first-class)公民。你可以写return int这样的语句
    • d. C属于后一种情况。你可以传递一个类型为某struct的值,而不是这个struct本身。
    • e. 当类型也是对象的时候,会引发一个新的问题:由于对象必有类型,所以类型对象(包括类)也有类型,对吧?(Java里的类不是对象,但可以用对象表示)
    • f. 那么类型的类型是什么呢???Python里有个type类,可谓是元类型。事实上Python允许你通过创建type类的instance创建新类型。哦my god。使用这种方式创建出的类型和用户定义类型class应该是没啥区别。所有类型对象,包括class,都是type class的实例。实例的类型当然就是对应的class了,所以Python中,类型的类型就是这个type class。
    • g. 接着说Python。type class也是个class啊,class在Python里也是第一级公民,也是对象。(咱就说new-style class吧,不要讨论old-style的了。)那type class的类型是什么呢?所有的class的类型都是type class,所以它自己也不例外。。。我们会在Java中看到类似的情况。
    • h. Java里面的类不是对象,本质上是不可操纵的。Java使用java.lang.Class的实例来表示class。你可以通过这些实例获得一些类型信息,还可以用它们创建出对应它们所表示class的实例。对应每一个类,都可以获得对应的java.lang.Class实例。当然对于java.lang.Class这个类本身,也可以获得其对应的java.lang.Class实例。

关于Python iterators和generators

其实这也没啥新鲜的,问题是我老忘,每次都现查。

当执行for i in o时,实际上是调用了iter(o),也就是o.__iter__,返回一个iterator对象。所谓的iterator对象,就是有个next()方法的对象(归功于duck typing,我们不需要一个iterator类型,__iter__和next也不一定要定义在类里,而是可以后“粘”上)。next方法的convention是,每执行一次就返回下一个值(因此它要自己记录状态,通常是在iterator对象上记录),直到没有值的时候raise StopIteration。

class MyClass():
data1 = 1
data2 = 2
data3 = 3
def __iter__(self):
# in reverse order
return MyIterator(self.data3, self.data2, self.data1);

class MyIterator():
index = 0
def __init__(self, data1, data2, data3):
self.data = (data1, data2, data3)
def next(self):
if self.index > len(self.data) - 1:
raise StopIteration()
else:
temp = self.data[self.index]
self.index += 1
return temp

for i in MyClass():
print i

从duck typing的角度来说,generators也是iterators,因为它们自己不仅有__iter__方法,还有next方法!所以上例还可以(无比容易的)写成这样:

class MyClass():
data1 = 1
data2 = 2
data3 = 3
def __iter__(self):
# in reverse order
for d in (self.data3, self.data2, self.data1):
yield d

for i in MyClass():
print i

wxPython的事件模型

真是麻烦啊wxPython。它的event模型有点特别,跟其他的GUI toolkit相比,将事件和widget绑定的时候,可以选择不将事件绑定到触发事件的那个widget上,而是绑定其他的widget(典型情况是包含触发事件widget的某个container)上,差点把我绕里。

举个例子来说,一个frame包含一个panel包含一个button,经典的GUI教学案例。我可以把“点击button”这个事件绑定到frame上并给一个handler。靠,真变态。这之后就靠事件传播规则来把事件传到frame上,调用这个handler。

wx.EVT_*是binders,它们是wx.PyEventBinder的实例,提供specific event types,将specific event types和handler,widget绑定在一起。用binder来表示特定的事件类型,这很特别。wx.PyEventBinder本身提供Bind方法。

wx.Event是各种事件对象的base class。

wx.wxEVT_*对应C++版本中的宏定义,通过int类型定义了一些event type。在wxPython中很少用到。我把它们和wx.EVT_*比较了一下,有一些是不同的。可以用如下code对比:

import wx

def lstMemberStartsWith(obj, startsWith):
	return [str for str in dir(obj) if str.startswith(startsWith)]

for str in lstMemberStartsWith(wx, 'EVT_'):
	print str
for str in lstMemberStartsWith(wx, 'wxEVT_'):
	print str[2:]

wx.EvtHandler类提供了Bind方法,调用它负责将特定的事件类型(binder)handler调用Bind的实例关联起来。由于每个wx widget类都继承了EvtHandler,这就意味着,在某个widget上调用Bind就是在其上绑定了相应的handler这个widget不一定是触发事件的widget!如果不是,Bind方法中要传入相应的source参数,指明触发事件的widget。

接下来就该说说事件的传播规则了。我只说说大体情况。

我们先定义什么叫做在一个widget上绑定了某个事件的handler:从代码上来看,就是调用了这个widget的Bind方法绑定了handler和特定的事件类型(binder)。 

当一个事件被触发,wxPython从source,也就是触发事件的widget开始,沿着container hierarchy向上,反复执行下列操作(除非事件传播结束):

(1)判断该widget上是否绑定了这个事件的handler。即,之前有没有在这个widget上调用Bind绑定相同类型的事件handler。如果是就执行(2),否就执行(3)。

(2)执行该handler。如果在这个handler里没调用Skip,或者事件不可传播,事件传播结束。

(3)如果事件不可传播,事件传播结束。

作为额外的奖励,如果事件不可传播,显然只有source的handler有可能会被执行。如果此handler里执行了Skip,或source没有handler,那么wxPython会额外的check一次App对象有没有相应handler。并执行之。

By default, only instances of wx.CommandEvent, or any subclass
thereof, propagate up the container hierarchy. All other events do not.

我没有讨论widget被disable的情况,我也没有讨论在search container hierarchy之前,class hierarchy先被search。详见wxPython in Action。我只是觉得那个图并不清晰才用自己的话做了一个等价的描述。无论怎么说,我还是觉得这个体系比较复杂,真正写程序的时候,大部分时间用不着记那么多东西,遇到特殊情况偶尔查一查就行了。

关于JavaScript,肆 (vs Python and Scheme)

JS的对象模型无疑是简单而高效的。从某种意义上来看,简直就是以函数为核心。从面向对象的角度来讲,JS是prototype-based,Python是class-based,Scheme这方面我还未学到啦~我就知道Lisp有个CLOS,还没玩过。

1. 值有类型,变量没有(dynamic typing)。JS中的变量只是名字,这和Scheme一样。Python应该也是,我不是百分百确定。

class C():
pass

D = C
c = C()
C = 1
print C # 1
del C
print D # __main__.C, though C was already deleted from context.
print c # <__main__.C instance at 0x00B44940>
print c.__class__ # __main__.C

2. JS数据类型有numbers, strings, booleans, null, undefined, objects(arrays), functions。new运算符后面可以随便跟个函数,这会创建一个新对象。在此函数内部随便干什么都行,只是这时候this引用的是刚创建的这个对象。

function f() { this.a = 2 }
f.a = 1              // f的property
alert(f)
alert(f.a)           // 1
f = new f()          // 创建对象,覆盖名字f
alert(f)             // 新对象
alert(f.a)           // 执行f()时加上的property a, 值为2

被创建的对象(objects/arrays, functions)可以随意在运行时添加properties(其内部实现应该是hashtable)。如果一个property类型为函数,并且从该对象被调用,函数中的this就是此对象:

function f() { alert(this.a) }
o = Object()
o.a = 'a in the new object'
o.method = f
o.method()             // 'a in the new object'
f2 = o.method          // f2 is equal to f, just a plain function
f2()                   // "this" refers to window object now, so "a" is not defined

如果你在全局scope定义一个函数,这个函数可被视为global object的一个方法。所以在JS中function和method真的是没什么本质区别的。 

Python的数据类型见这里:http://docs.python.org/ref/types.html

下面这段代码很能说明Python中function和method还是有区别的:

class C():
""" A dummy boring class. """

C.g = lambda self: 'method'

# unbound method
print C.g
print C.g(C())

# bound method
c = C()
print c.g
print c.g()

# changed method, still bound
c.__class__.g = lambda self: 'changed method'
print c.g
print c.g()

# function
c.g = lambda : 'function'
print c.g
print c.g()

PyQt初览

为了攻读Qt我都被逼无奈去看万恶的C++的代码啦!我这麽一Python程序员。。。

当我看到这个语句,我本能的感觉到了问题,出离愤怒了。。。

emit dataChanged(index, index);

靠!搜了一下果然,是用preprocesser处理的:

http://doc.trolltech.com/3.3/signalsandslots.html

http://sigslot.sourceforge.net/

不消说,我讨厌这种繁复的东西。

其他值得注意的有,对于大多数的widgets来说,是不需要MVC这样的复杂设计的。table和tree特殊,就跟Java Swing中是一样的。model index可以看成是从model中取得数据的“坐标”,也跟Swing中差不多。

http://doc.trolltech.com/4.1/model-view-programming.html

MVC将视图和数据模型分开,一个好处就是一个数据模型可以对应多个不同的视图。

QString与Python string的转换是透明的。

期待Python 3.0

去年就在Guido的一篇帖子中读到Python对字符串编码的不良支持将在Python 3中彻底解决。今天读Programming Python第三版前言,Guido又提到这个事儿,并且承认这是他当年的设计失误,但改动这个基本上会带来翻天覆地的变化(IO库要重写!),所以一直拖着,拖着,终于不能忍了。。。因为这是无数bug的根源啊啊啊啊啊啊啊。。。

这可真是个天大的好消息。 记得去年我倒腾Zope的时候就被这个搞得死去活来的。靠。当时我就纳闷,干嘛不把string内部都搞成Unicode,再来个byte array不就齐了,就跟Java里一样。结果今天一看,Guido就准备这么干。。。我真是太高兴了~~~

Python与Lisp的最大不同在哪里呢??语法,是的。而Lisp特别的语法带来了一些特别的feature。也就是说虽然都可以metaprogramming,但Lisp代码由于本身就是语法树,其结构化的本质可以使metaprogramming容易得多。我还没看On Lisp那书,但我有一种预感,就是Paul会好好利用这个做足文章的。。。 

DirCompare in Python?

I've been evaluating PyQt last weekend, and kicked a start on evaluating wxPython. Both of them seem to have native L&F. Very good. Qt is claimed to have a more advanced event model (signal/slot), but to install Qt, PyQt, MinGW together is somewhat a little complex. It also worried me on the deployment of my software. On the contrary, wxPython is much more comfortable.

I've had enough for Tkinter. Many people seem to agree, too. See what Guido van Rossum said:

wxPython is the best and most mature cross-platform GUI toolkit, given a number of constraints. The only reason wxPython isn't the standard Python GUI toolkit is that Tkinter was there first.

So funny! Another (not so friendly but quite with the personal landmark) comment:

"Why the hell hasn't wxPython become the standard GUI for Python yet?"

-- Eric S. Raymond
This reminds me of the time when I first learned Python. I was inspired and encouraged by one of Eric's essays.
Enough of talking. In my mind this software should include the following features:
1. paned window with trees simultaneously expanding/folding in each pane
2. persistent sessions
3. synchronized scrolling 
4. file/folder name filters
5. highlights
6. binary/CRC/timestamp as comparing criteria
7. sorting
8. tabs for multiple sessions of comparison
9. invoking file comparison tools (e.g., PSPad)
10. FTP support (so make your dir interface general!)
 
Note: I made a huge breakthrough just now (2007 Dec 10 1:18am) !
分页共2页 1 2 下一页 最后一页