(翻译)为什么Python的字符串是不可变对象

翻译自http://effbot.org/pyfaq/why-are-python-strings-immutable.htm

不可变对象是指一旦生成后不能被改变的对象。他经常与可变对象相提并论。比如Python的tuplelist,它们的行为有点类似。但是可以list对象内添加新的元素。而tuple则不可以。Python的字符串被设计成不可变对象有几个优点。

首先,可以提高性能。假定字符串是不可变对象,意味着字符串具有确定而不可变的存储需求,解释器可以为它分配一段固定的内存。这也是为什么要分为tuplelist两种类型的原因之一。让字符串成为不可变对象还可以让解释器重用该对象。比如,CPython在实现中预先申请了所有的单字节节符,并且,如果字符串运算后的结果与运算前相同,就会返回运算前的字符串(*)。

另外,在Python里,字符串就像数字一样,被认为是表示思想的最基本的材料。在Python里,没有任何方法可以把数字8变成其它东西,同样,也没有办法把"eight"这个字符串变成其它东西。

另外,使用不可变数据结构(immutable data structures),比如字符串,元组,强调了一种无副作用的编程方式。采用这种编程方式所编写的函数接收输入的参数,进行运算,然后返回一个容纳结果的新对象。而不是修改输入的参数,使它变成运算结果。当然了,有时候必须做一些有副作用的操作,比如修改对象的状态。这种事最好仔细考虑一番,因为无副作用的编程哲学能够让你的代码更加模块化,使其独立于其它代码。

类型:Python,创建时间:Jan. 1, 2012, 4:50 p.m. 点击查看完整内容。。。
Qt窗口的删除、析构

Qt内关于窗口的删除是一个复杂的事情。接下来详细地讲一讲。

当我们创建一个窗口时,如果这个窗口是一个顶级窗口,准确地说是w.windowFlags().testFlag(Qt.Window) is True的窗口,Qt会自动附加一个Qt.WA_QuitOnClose属性。它的意思是,窗口被关闭了,qApp.lastWindowClose信号会被触发。

默认情况下,qApp接收到此信号后会退出程序,这个行为相当于在初始化程序时:

qApp.lastWindowClosed.connect(qApp.quit)

要修改这个默认行为,可以设置QApplicationquitOnLastWindowClosed属性,比如:

qApp.setQuitOnLastWindowClosed(False)

通常,用户可以点击窗口的关闭按钮关闭窗口,或者当系统关闭的时候,窗口也会被关闭。我们可以通过重载窗口的closeEvent()函数控制关闭动作,比如询问用户要不要保存文档,或者只是最小化窗口。下面是一个例子:

def closeEvent(self, event):
    if self.maybeSave():
        self.saveSettings():
        event.accept()
    else:
        event.ignore()

我们也可以在菜单里设置一个退出命令,点击它的时候调用窗口的close()函数即是关闭窗口。

关闭窗口之后,通过我们还要手动销毁这个窗口,释放它所占用的内存。对此Qt提供了一个简便的办法,只要将窗口的Qt.WA_DeleteOnClose属性设置为true,Qt会自动帮我们销毁这个窗口。

类型:Python,C++ & Qt4,创建时间:Jan. 1, 2012, 4:42 p.m. 点击查看完整内容。。。
使用M2Crypto加密数据

本文是一个M2Crypto的基础教程。介绍了如何使用M2Crypto完成以下任务:

  1. 如何使用MD5、SHA1等消息散列算法。
  2. 使用对称加密算法加密数据。
  3. 生成RSA密钥
  4. 生成DSA密钥
  5. 载入DSA密钥与RSA密钥
  6. RSA类型的操作——使用RSA加密、解密、签名、认证;保存RSA密钥
  7. 一个小型的CA,电子证书。

研究M2Crypto时参考了OpenSSL的源代码,因此本文对学习OpenSSL的同学或许也有些参考意义。

类型:Python,创建时间:Jan. 1, 2012, 4:35 p.m. 点击查看完整内容。。。
聊聊Pyhon的encodings

本文讨论python各种奇形怪状的编码:

  • base64
  • bz2
  • hex
  • idna
  • punycode
  • quopri
  • raw_unicode_escape
  • rot_13
  • string_escape
  • undefined
  • unicode_internal
  • utf_8_sig
  • uu
  • zlib
类型:Python,创建时间:Jan. 1, 2012, 9:15 p.m. 点击查看完整内容。。。
使用QToolBox时,pyuic4的一个编译错误

有时候QtDesigner生成的窗口如果包含QToolBox,使用pyuic4编译的时候会出错。经过调查,我发现主要是因为QtDesigner在创建QToolBox的页时会自动生成页面的名字。可能是因为翻译的问题,页面的名字是中文的(page变成)。页面的名字会编译成python的变量名。在python3k之前,使用中文作为python的变量名显然是不合适的。所以会提示错误。

如果发现pyuic4发生错误,可以试着加入调试选项:pyuic4 -d main.ui。 可以解决很多像QToolBox那样的错误。

类型:Python,C++ & Qt4,创建时间:Jan. 1, 2012, 2:54 p.m. 点击查看完整内容。。。
使用Qt实现从资源管理器中拖动文件到应用程序的功能

我是EditPlus和FileZilla的用户。这两个软件都支持文件拖拽。我们可以从资源管理器中拖动一个文件到EditPlus的窗口,EditPlus就会自动打开这个文件。或者拖到FileZilla的远程窗口内,FileZilla就会自动将文件上传到服务器。我经常希望自己的软件也支持拖动的操作。

作为基本功能,大多数操作系统都各自定义了拖放所需的通信协议。Qt对此做了一个很好的封装。基本的流程是:当用户从其它应用程序拖动文件、文本、图像等到Qt程序时,Qt会自动产生一个DrapEnterEvent,应用程序可以判断数据是不是可以处理的。如果能处理,就反馈给Qt,这样的话,用户就会看到一个接受拖放的标识。待用户放开鼠标时,Qt再产生一个DropEvent。最后应用程序从事件中取出数据进行处理。

那么应用程序如何判断程序是否可以处理拖放的数据呢?很简单,数据中包含了MIME类型。MIME类型是一个用于标识二进制数据类型的Internet标准。很多朋友可能习惯通过文件的扩展名来判断数据类型,但是扩展名只有短短的几个字符,常常有歧义。通常,如果应用程序要处理文本,就判断MIME类型是不是"text/palin",如果要处理JPEG图像,就判断MIME类型是不是"image/jpeg",等等。同一段数据通常包含多种MIME类型,比如拖动富文本时,数据中可能包含纯文本、HTML文本、图像。应用程序可以选择处理所支持的数据格式。

具体如何处理从资源管理器拖动的文件呢 ?请点击查看正文。

类型:Python,C++ & Qt4,创建时间:Jan. 1, 2012, 8:52 p.m. 点击查看完整内容。。。
禁止python在运行的时候自动生成.pyc文件

有没有觉得,有时候python的.pyc文件很讨厌呢?虽然它们能加快加载速度,但是看见那一堆堆的文件,又不能放到SVN里,实在是很烦。

现在python2.6新增了一个特性,只要把环境变量PYTHONDONTWRITEBYTECODE设置为x,就不再会生成.pyc文件了。在windows下修改环境变量的方法是:右键点击“我的电脑” ->属性->高级->环境变量。在Linux下可以修改~/.profile

export PYTHONDONTWRITEBYTECODE=x
类型:Python,创建时间:Jan. 1, 2012, 2:40 p.m. 点击查看完整内容。。。
datetime和date类型与字符串之间的互转

不说什么,看代码:

#从sqlite3模块抄过来的东西,用于datetime,date到字符串之间的相互转换
def adapt_date(val):
    return val.isoformat()

def adapt_datetime(val):
    return val.isoformat(" ")

def convert_date(val):
    return datetime.date(*map(int, val.split("-")))

def convert_timestamp(val):
    datepart, timepart = val.split(" ")
    year, month, day = map(int, datepart.split("-"))
    timepart_full = timepart.split(".")
    hours, minutes, seconds = map(int, timepart_full[0].split(":"))
    if len(timepart_full) == 2:
        microseconds = int(timepart_full[1])
    else:
        microseconds = 0

    val = datetime.datetime(year, month, day, hours, \
            minutes, seconds, microseconds)
    return val
类型:Python,创建时间:Jan. 1, 2012, 2:33 p.m. 点击查看完整内容。。。
Python的XMLRPC简介及小技巧

我觉得,SimpleXMLRPCServer模块可以说是Python的招牌菜。

编写客户端提交数据到服务器处理是程序员最常碰到的几个问题之一。各种不同的语言对此都有相应的解决方案。比如Unix下,C程序员们可以用SUNRPC,Java程序员则使用RMI来处理。大多数语言还都可以使用Web Service或者ICE。它们的使用方法类似,编写一个接口定义文件,用一个工具处理并生成代码,加入到工程中,最后编译生成目标文件运行。有用过这类工具的朋友们,脑子里应该都会闪出一个字——烦!真的是谁用谁知道。

Python同样也提供了一个基于XMLRPC的解决方案,不过用法很简单:

  1. 首先,就像在编写普通的程序那样子编写服务器。
  2. 接着使用SimpleXMLRPCServer模块运行XMLRPC服务器,在其中注册服务器提供的函数或者对象。
  3. 最后,在客户端内使用xmlrpclib.ServerProxy连接到服务器,想要调用服务器的函数,直接调用ServerProxy即可。

本文介绍xmlrpc的基本用法以及几个注意事项。

类型:Python,创建时间:Jan. 1, 2012, 2:30 p.m. 点击查看完整内容。。。
Python的__del__()方法

简而言之,__del__方法相当于其它语言里的析构函数。不过,由于Python的一些特性,在使用__del__需要注意一些问题:

  1. 因为垃圾收集机制处理循环引用(A使用B,B又使用了A)的时候总不尽如人意。 所以__del__并不总是会被调用。

  2. __del__可能在Python退出的时候被调用。此时很多变量都已经被释放, 所以__del__对外部的依赖要尽可能的小。

  3. __del__函数内不能引入新的模块。

  4. Python提供了try/finally这样的语法,一些资源释放的东西,最好还是放在finally块内解决,而不是放到__del__方法内。

类型:Python,创建时间:Jan. 1, 2012, 12:31 p.m. 点击查看完整内容。。。
最讨厌Python 2.x的编码问题了

本粗人在写程序的时候习惯这样子:

print "中文"

显然,这个写法是不具可移植性的。因为,在windows下默认编码是gbk,在很多linux下面默认编码是utf-8。如果源代码的编码是gbk,在windows下可以工作得很好,如果源代码是utf-8,在windows下会打印出一堆乱码。好一点的作法是

print u"中文"

这样的话,在不论源代码的编码是什么,也不论操作系统是什么,都可以打印出正确的字符。可是如果这样子就麻烦了:

name=third_party_module.get_system_username()
print u"你好,%s"%name

third_party_module.get_system_username()返回一个当前登录的用户真实名字。但是他返回的到底是什么编码的我们不知道。因为unicode()函数默认使用当前系统的默认编码。倘若name的编码与当前系统默认的一样,一切OK。如果不一样,问题就麻烦了。幸好返回时使用的一般是当前系统默认编码。

最麻烦的:

name=third_party_module.get_system_username()
client_socket.send(name)

字符串被发送到网络上,到达运行着不同操作系统的远程主机上。远程的主机怎么知道这是什么编码呢?最近经常遇到这样的问题,实在很郁闷。

类型:Python,创建时间:Jan. 1, 2012, 12:29 p.m. 点击查看完整内容。。。
Python, Qt, Java的字符编码

首先定义只在本文中出现的两个基础概念:

  • 字节组。每8个位(bit)是为一个字节,多个字节成为一个字节组。
  • 文字串。现实语言的最小单位称为文字。一个英文字母就是一个文字,一个汉字也是一个文字,一个空格也是文字。

这两个概念是我生造的,主要是为了避免和现有的一大堆术语混起来。

文字串不能理解成字符串。倒是像圆这样的数学概念,比较抽象一些,只存在于头脑里。可以把文字串写在纸上,也可以记在计算机里,还可以读出来。字节组就是文字串在计算机中的表现形式。想一想,在纸上圆可以用一个公式来表达,也可以用一个图形来表达,还可以用文字描述它。文字串也一样,可以表述为多种字节组。众所周知,计算机只能处理数字,所以为了描述文字,最简单的方法就是把文字列出来,每个文字用一个数字表示。不同的国家有不同的方法来做这件事,比如美国人和英国人只有字母,还有一些符号,总共排了256个文字,叫做ascii码。中国的文字就比较多了 ,那个康熙大字典据说有40万,所以中国人排了gb2312标准,big5,gbk、还有gb18030,里面包含的文字总数各不相同。

  • 把文字串表述成字节组的方法就叫做字符编码。

显然,美国人的ascii码是不能表示中国文字的,因为它只能表示256个文字。虽然,中国的标准可以表示美国人的文字,但是却不能表示阿拉伯字符和其它一些像型文字。后来大家意识到这个问题是文化交流的障碍,于是就在一起制定了一个unicode标准。简而言之,unicode标准就是穷尽这个世界上所有的文字,给每个文字编一个数字。 和gb2312,gbk等一样,其实unicode也只是一种编码标准,只不过它能够表示所有的文字。从此,如果一个计算机系统想要支持多种语言文字,只要简单地支持unicode就可以了。在这种系统里,我们可以认为文字串就是unicocde字节组,unicode字节组就是文字串。

类型:Python,C++ & Qt4,Java,创建时间:Dec. 31, 2011, 4:52 p.m. 点击查看完整内容。。。
一个定时器,用来做类似于crontab的工作

前几天写一个后台程序,定时查看一个URL的修改,本来想用crontab做的,后来想想就算了,因为python写起来更简单。于是我就实现了一个每小时(可配置)醒过来做一次操作的小小工具类。与threading的Timer不一样的是,它是单线程的。

本文包含一段小脚本可以模拟crontab,定时在某个时间执行操作。

类型:Python,创建时间:Dec. 31, 2011, 9:50 a.m. 点击查看完整内容。。。
应用python编写shell脚本

本文介绍使用python编写shell脚本所使用的模块与注意事项。主要是os, os.path, shutil。除了这三个模块,可以对文件以及文件夹之外,还有另外一些模块关注系统的其它方面,比如密码,帐户信息。

使用os.environ可以访问环境变量。使用sys.argv可以获得用户传入的参数。

类型:Python,创建时间:Dec. 30, 2011, 9:03 p.m. 点击查看完整内容。。。
在多线程编程的时候如何让子线程退出

虽然python下推荐使用多进程编程,但是在一些场合下,多线程编程还是方便一些。

最近就经常写一些关于网络方面的命令小程序,一个线程不断地从命令行得到用户的输入并且向网络发送一些东西,另外一个线程则从网络接收数据并打印在屏幕上。平常结束一个程序的办法是调用exit()函数,不过对于多线程的网络程序似乎却有点难办,因为除了一条sys.exit()语句外,还需要结束从网络接收数据的后台线程。

不幸的是python没有提供结束线程的接口,不能像java或者c#那样,使用thread.interrupt() 就可以让线程结束。更糟糕的是,python调用系统函数的时候会被阻塞在内核,除非阻塞被解除,这个线程不再继续执行python解释器的代码了,所以interrupt()理论上根本无法实现。那要怎么退出线程呢?我的解决办法是:

设置一个名为exiting的全局变量,从网络读数据的线程以它为循环条件。如果要让这个线程退出,只需要改变一下exiting的值,接着发送一些信息让线程退出阻塞。 调用线程的join函数可以等待线程退出。

类型:Python,创建时间:Dec. 31, 2011, 1:48 a.m. 点击查看完整内容。。。
对函数式编程的看法

最近函数式编程概念很火,似乎每个“现代化”的编程语言都要沾一下函数式编程,才能称之为现代化的编程语言。java打算在jdk7中加入闭包,.net则早已支持lambda语句。至于python,ruby,javascript等这些过程化的编程语言则争先恐后地加入了函数式编程的战团,各展优雅。可是,函数式编程真的那么重要吗?

这些过程化编程语言通常提供了一些高阶函数,以及对于闭包的支持。比如python提供了

  • map(function, sequence) -> list
  • filter(function,sequence) -> list
  • reduce(function,sequence) -> result

这些函数在现有的过程式语言环境中基本上都是类似的实现。可以说,不过是语法糖而已,函数式编程擅长的并行计算不可能在这种过程式的编程语言中自动地获得。这种函数式编程有什么意义?更好看还是更简洁?难道还有其它的理由吗?

闭包是具有闭合作用域的匿名函数。他带来的优点和缺点在很多文章中已经被详加探讨。基本上,闭包在大多数过程化的编程语言里被实现为匿名类。说实话,自从用过闭包之后,我就很自觉地将闭包的思想用于软件设计。不过,毕竟只是语法糖,有也可以,没有也不是很遗憾。如同前所述的高阶函数,我认为,它对现有的过程化编程语言改进不是很大。怪不得之前有人建议在Python 3000里去掉lambda

类型:Python,随感,创建时间:Dec. 30, 2011, 7:22 p.m. 点击查看完整内容。。。
Python的startswith和endswith

做文本处理的时候经常要判断一个文本有没有以一个子串开始,或者结束。Python为此提供了两个函数:

S.startswith(prefix[, start[, end]]) -> bool

如果字符串`S以prefix开始,返回True,否则返回Falsestartend是两个可以缺省的参数。分别是开始比较的位置和结束比较的位置。这个函数也可以写成S[start:end].startswith(prefix)`。

S.endswith(suffix[, start[, end]]) -> bool

如果字符串Ssuffix结束,返回True,否者返回False。与startswith类似,这个函数也可以写成S[start:end].endswith(suffix)startend仍然是从左数起。

Python的这两个函数有个特别的地方——它的prefixsuffix参数不仅可以是字符串,还都可以是一个元组。只要其中一个成立,就返回True,也就是一种“或”的关系。比如:

if filename.endswith((".gif", ".jpg", ".tiff")):
    print "%s是一个图片文件"%filename

上面两行代码根据文件扩展名是否是gifjpgtiff之一来决定文件是不是图片文件。这个代码也可以写成:

if filename.endswith(".gif") or filename.endswith(".jpg") \
        or filename.endswith(".tiff"):
    print "%s是一个图片文件"%filename

不过这样比较麻烦。值得注意的是,不熟悉Python的朋友可能会忘了元组周围的括号。

类型:Python,创建时间:Dec. 31, 2011, 1:16 a.m. 点击查看完整内容。。。
百年语言(转载)

Paul Graham 2003年4月

(本文出自2003年Python大会上的一篇主题讲话)

很难预测人们的生活在一百年后会是什么样子,我们只能给很少的事物一个确切的预测。我们知道到那时候每个人都将驾驶气垫轿车,地方法规将对建造上百层的高楼无所制约,大部分时间都将日月无光,女人们都将精通武术(martial arts)……在这里,让我们把这幅图景的一个细节放大来看看:那时候人们用什么编程语言来写那些气垫轿车的控制软件呢?

这是一个值得思考的问题,其意义不在于我们一定要用这种语言,而是在于据此我们可以选择可能发展成那种语言的语言——如果我们够幸运的话。

类型:Python,创建时间:Dec. 30, 2011, 7:08 p.m. 点击查看完整内容。。。
Python的exit函数

在2.5版本之前的Python,输入exit或者quit会得到这样一条说明:

>>> exit
'Use Ctrl-D (i.e. EOF) to exit.'

尝试着输入exit()或者quit(),python报错。

>>> exit()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: "str" object is not callable

很多新手都碰到这个问题。似乎exitquit的存在只是为了告诉别人不要用exitquit。这可真是奇怪的逻辑。

新版本的Python改了这种做法。输入exit()quit()就如我们所料地退出了Python。这也意味着很多情况下我们不用再导入sys这个模块了

Python2.5之前的程序:

>>> import sys
>>> sys.exit()

现在:

>>> exit()

不过需要注意的事,脚本里面并不能应用exit()函数。它只存在于解释器内。这个改动主要是为了方便*nix程序员的使用。Windows程序员都喜欢直接关闭窗口吧。

类型:Python,创建时间:Dec. 31, 2011, 1:03 a.m. 点击查看完整内容。。。
Python的条件运算符

长期以来Python世界的人一直在争论是否应该拥有条件运算符。它可以用来处理根据条件返回不同的值。比如:

if contition:
    x=true_value
else:
    x=false_value

他们虽然讨论了很久,但是一直没什么结果。在python-dev邮件列表和comp.lang.python新闻主上甚至为此举行了一次投票。最后的结果是大家都同意应该添加条件运算符,可是它的语法形式是什么还是没有结果。主要的争论是C语言的cond ? true_v : false_vif cond then true_v else false_v到底挑哪个好。

最终,Guido van Rossum(Python语言的作者)挑了这样一个令人意想不到的语法形式:

true_value if condition else false_value

它的意义是:如果condition为真,计算并返回true_value,并跳过false_value的计算,否则计算并返回false_value。它完全等价于完整形式。

这个表达式的优先级是最低的,但是它优于=号。因此以下这两种形式是一样的

level = 1 if logging else 0
level = (1 if logging else 0)

不过笔者认为后一种形式看起来比较好一点,而且可以免去记忆运算符优先级的麻烦。

类型:Python,创建时间:Dec. 30, 2011, 6:59 p.m. 点击查看完整内容。。。