编写兼容Python2.6与Python3.0的程序


类型:Python,创建时间:Jan. 1, 2012, 5:32 p.m.

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/64/。

想在2.6版本尽量利用3.0的特性,要求写出的代码尽可能地在3.0版本下也可用,或者只要稍微改动就可以。目前我想到的几个点:

  • 使用3.0的print()函数。

    在py2.6里可以简单使用print的函数形式:

    #这样可以,但是这毕竟不是函数,任何其它形式都会报错,比如:
    print("fish is here")
    
    #报错,print仍然是一个语句,而不是函数
    print("fish is here", file=sys.stderr)
    

    只要在程序最开始的地方加上:

    from __future__ import print_function
    

    就OK了

  • 以3.0的形式写字符串,不加任何前缀即是unicode字符串,加前缀"b"是bytes类型

    py3k重命令了字符串类型,并且改动了它们的记法。括号内是python的版本。重命令的类型:

    str(2.6) => bytes(3.0)
    unicode(2.6) => str(3.0)
    

    改动的字符串记法:

    "fish is here"(2.6) => b"fish is here"(3.0)
    u"fish is here"(2.6) => "fish is here"(3.0)
    

    为了在py2.6程序里使用py3k的写法,可以在程序最开始的地方加上:

    from __future__ import unicode_literals
    

    碰到需要bytes的地方注意加b前缀。貌似在2.6版本里,struct.pack()第一个参数不能是unicode,这应该是个BUG。另外,包(package)的__init__.py里,__all__列表的元素必须是bytes类型,不知道这是BUG还是特性(2.6里测试是这样的)。

  • 有时候需要判断字符串的类型,看看它到底是unicode字符串还是bytes类型。因为在py3k里没有unicode类型,所以在2.6里要:

    try:
        str=unicode
    except NameError:
        pass
    
    if isinstance(s, str): #unicode字符串
        pass #blahblah
    
  • 使用3.0的io库。io库提供了一个更合理的file类型。在py2.6代码里使用io.open()来代替open()。 使用io.BytesIO()来代替StringIO.StringIO()

  • 不使用old-style class的特性。只要重载了__attribute__()之类的特殊方法,就要考虑是否应该继承自object

  • 所有异常继承自BaseException

  • 注意///的差别

    from __future__ import division
    
  • 捕获异常到某个变量,使用as关键字

    #在py3k里错误,在py2.6里不好的写法
    try:
        raise Exception
    except Exception, e:
        pass
    
    #良好的写法
    try:
        raise Exception
    except Exception as e:
        pass
    
  • 捕获多种类型的异常

    try:
        io.open(filename,"wb").write(b"haha")
    except (IOError, OSError):
        pass
    

    我自己经常搞错,呵呵。不能写成:

    try:
        io.open(filename, "wb").write(b"haha")
    except IOError, OSError:
        pass
    

    这种写法在py2.6里面是指捕获IOError类型的异常,并赋值给OSError这个变量。

  • py3k里没有xrange()函数,可以统一用range(),或者

    try:
        range=xrange
    except NameError:
        pass
    
  • py3k对很多本来返回list的函数进行了处理。原本在py2.x里返回list对象的函数,现在可能返回的是只支持迭代操作的对象。比如dict.keys()现在返回的是一个dict_keys类型的对象。

    >>> d={"fish":26}
    >>> d.keys()
    dict_keys(['fish'])
    >>> d.keys()[0]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'dict_keys' object does not support indexing
    

    想要像以前那样子操作它,最简单的办法是:

    >>> list(d.keys())[0]
    'fish'
    

    如果编写了某些类似于模拟dict.items(), dict.keys(), dict.values()的函数,比如:

    def items():
        return [("id", self.id)]+self.value.items()
    

    现在要改写成:

    def items():
        yield ("id", self.id)
        for item in self.value.items():
            yield item
    
  • 迭代子(iterator)的next()函数在py3k里被改成了__next__(),可以使用内置的next()函数

    next(itor) 相当于itor.next()或者itor.__next__()

  • 绑定对象的im_self函数现在是__self__im_func改名__func__

  • 有些模块的名字已经改了,在import的时候要注意:

    try:
        import xmlrpclib
    except ImportError:
        import xmlrpc.client
    

    被改名的模块:

    _winreg
    winreg
    
    ConfigParser
    configparser
    
    copy_reg
    copyreg
    
    Queue
    queue
    
    SocketServer
    socketserver
    
    markupbase
    _markupbase
    
    repr
    reprlib
    
    test.test_support
    test.support
    

    被重新归类的模块:

    dbm (anydbm, dbhash, dbm, dumbdbm, gdbm, whichdb).
    html (HTMLParser, htmlentitydefs).
    http (httplib, BaseHTTPServer, 
        CGIHTTPServer, SimpleHTTPServer, Cookie, cookielib).
    tkinter (all Tkinter-related modules except turtle). 
    urllib (urllib, urllib2, urlparse, robotparse).
    xmlrpc (xmlrpclib, DocXMLRPCServer, SimpleXMLRPCServer).
    

更详细的信息应该看一看py3k的"what's new"

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/64/。


胡小来(Sept. 18, 2014, 8:04 p.m.)

搜来搜去就您说的靠谱。


何不来发表一下您对本文的看法(使用Markdown语法,分段空两行):