2019独角兽企业重金招聘Python工程师标准>>>

为Python3作准备

在开始添加Python 3的支持前,为了能够尽可能地顺利过度到Python 3,你应该通过修改对2to3来说很难苦的东西来给你的代码做一些准备。即使你现在不打算迁移到Python 3,有一些事你也可以现在就做,这些事在一些情况下它们甚至会加快你的代码在Python 2下的运行。

你可能想要读在用现代的用句来改善你的代码 上包含许多其他一些你能够用到你的代码中的改进的章节。

在Python 2.7下运行

这个过程的第一步是让你的代码在Python 2.6或者2.7下运行。在这里你用的是什么版本不重要,但是明显最后的Python 2是很有意义的,所以如果你能用Python 2.7的话,就用吧。

大多数代码不用修改就可以直接运行,但是从Python 2.5到2.6有几个变化。在Python 2.6 as 和 with 是关键字,所以如果你使用这些作为变量就需要修改他们。最简单的办法是在变量的尾部加上下划线。

>>> with_ = True
>>> as_ = False

你还需要摆脱字符串异常。 使用字串符来抛出异常已经不被推荐很长时间了,主要是因为他们非常不灵活,例如你不能继承他们。

>>> raise "Something went wrong!"
Traceback (most recent call last):
...
Something went wrong!

在Python 3字符串异常已经完全消失了。 在Python 2.6中你不能发出他们,但是为了向后兼容你还是可能捕捉到他们。在一些情况下你需要在你的代码中移除所有字符串异常的使用并且在做任何事之前先让他在Python 2.6下运行。

>>> raise Exception("Something went wrong!")
Traceback (most recent call last):
...
Exception: Something went wrong!

下一步是在Python 2.6和Python 2.7下用 -3选项来运行你的代码。这个选项会对那个在Python 3中不支持及2to3不能转换的部分发出警告。它主要针对的是那么已经被弃用很久的和有新替代方式的部分,或者要被从标准库中移除的模块。例如对Classic Mac OS的支持已经被移除了,现在只支持OS X并且因为这个原因对Classic Mac OS特殊特性的支持模块已经被移除了。

你会得到一些下面列出的这么变化以及一些被重组库的警告(但不是全部)。 库的重组变化是很简单的,不需要解释,警告会提醒你新模块名字。

当除以整数时用//代替/

是Python 2除两个整数将反回两个整数。那意味着5除以2将返回2。

>>> 5/2
2

但是,在Python 3这会返回2.5。

>>> 5/2
2.5

今天在Python 2中的除尘运行符相信整除相除返回的是整数。但是使用2to3自动转换时会不知道操作数的类型是什么 ,因些它不知道除法操作是不是除以整数。 因此它不会在这里做任何转换。这意味着如果你正在使用旧的整数除法操作,你的代码在Python 3下会有很多错误。

由于这个变化从Python 2.2就开始计划了,Python 2.2及之后的版本都包含一个叫着浮点除法的使用两个斜线的新操作。 即使有浮点数,它也总是返回整数。任何你真的想要使用返回完整数字的浮点除法的地方,都应该把除尘操作改成浮点除法操作。

>>> 5//2
2
>>> 5.0//2.0
2.0

通常情况下,Python 2的除法操作是不必要的。解决这个问题的最常见的方法是把其中一个整数转换成浮点数或者在其中一个数字上添加一个小数点。

>>> 5/2.0
2.5
>>> a = 5
>>> b = 2
>>> float(a)/b
2.5

但是,有一个更合适的办法,那就是允许使用Python 3的行为。这是通过从Python 2.2开始可能使用的__future__ import 做到的。

>>> from __future__ import division
>>> 5/2
2.5

虽然把除号前的一个操作数中转换成浮点数能很好地工作,但是在Python 3中不是没有必要的并且使用__future__ import就可以避免它。

使用带 -3 选项的Python 2.6运行如果你用旧的整数除法的话就会提醒。

使用新式的类

在Python 2有两种类,“旧式”和“新式”。“旧式”的类已经在Python 3中移除了,因此即使没有显示地定义所有的类也都是object(对象)的子类。

在新旧的类上有一些不同,但是只有很少的情况下他们才会在Python 3下产生问题。如果你使用了多重继承可能会因为不同解决顺序遇到一些问题。[4]

如果你用了多重继承,因此你应该在添加Python 3支持前切换成新式类。 通过确保所有的对象是object(对象)的子类来做到这个。并且你可能必须要在类定义中改变列出的超类顺序。

单独的二进制数据和字符串

在Python 2,用str对象来保存二进制数据和ASSCII文本,然而保存在unicode的文件数据比可以保存在ASCII的需要更多字符。在Python 3,取代str和unicode对象的是你可以用bytes对象保存二进制数据并且使用str对象保存不管是不是Unicode的所有类型文本数据。即使有明显的差异,Python 3的str类型或多或少类似于Python 2的unicode类型,而bytes类型颇为类似Python 2的str类型。

为这个作准备的第一步是确保没有在二进制和文本数据上使用相同的变量名。在Python 2这不会给你带来麻烦,但是在Python 3会的,因此尽可能保证二进制数据和文本是分开的。

在Python 2上,“t”和“b”文件模式标识志可以改变在一些平台(例如Windows)上换行是如何处理的。但是这些标志在Unix上不会产生不同作用,因此为Unix开发的很多项目往往会乎略那些标志并且在文本模式打开二进制文件。然而在Python 3 这些标志也决定了当你从文件中读数据返回的结果是bytes对象还是unicode对象。虽然文本标志是默认的,但是一定要添加上它,因为那表示本文模式是有目的的并不只是你忘记加标志。

带上-3选项执行Python 2.6是不会提醒这个问题的,因为Python 2根本没有办法知道数据是文本还是二进制数据。

当排序时,使用key来代替cmp

在Python 2带排序方法可以带一个cmp参数,这个参数是一个比较两个值并返回-1,0或者1的函数。

>>> def compare(a, b):
...     """Comparison that ignores the first letter"""
...     return cmp(a[1:], b[1:])
>>> names = ['Adam', 'Donald', 'John']
>>> names.sort(cmp=compare)
>>> names
['Adam', 'John', 'Donald']

从Python 2.4开,.sort()和新的sorted()函数一样(见使用sorted()代替.sort()),sorted()可以带一个返回排序键值的函数作为key参数。

>>> def keyfunction(item):
...     """Key for comparison that ignores the first letter"""
...     return item[1:]
>>> names = ['Adam', 'Donald', 'John']
>>> names.sort(key=keyfunction)
>>> names
['Adam', 'John', 'Donald']

这使用真情更容易并且执行起来更快。当使用cmp参数时,排序比较每一对值,因此对每个项目都要多次调用比较函数。一个更大的数据集合将要为每一个项目调用更多次的比较函数。使用key函数,排序使用保存每一个项目的键值并比较他们来取代,因些每一个项目只调用一次key函数。因为这个原因比较大数据集合时会快很多。

key函数往往可以用如此简单地用lambda来替代:

>>> names = ['Adam', 'Donald', 'John']
>>> names.sort(key=lambda x: x[1:])
>>> names
['Adam', 'John', 'Donald']

Python 2.4 还引入了reverse参数。

>>> names = ['Adam', 'Donald', 'John']
>>> names.sort(key=lambda x: x[1:], reverse=True)
>>> names
['Donald', 'John', 'Adam']

在你排序的是几个值时,使用key跟使用cmp相较起来差距不是很明显。比方说,我们想要首先按名字的长度排序并且相同长度的按字母排序。 用key函数做这个并不显而易见,但是解决方法通常先按次要求排序之后再按另一个要求排序。

>>> names = ['Adam', 'Donald', 'John']
>>> # Alphabetical sort
>>> names.sort()
>>> # Long names should go first
>>> names.sort(key=lambda x: len(x), reverse=True)
>>> names
['Donald', 'Adam', 'John']

这个可以工作是因为从Python 2.3开始采用了timsort排序算法[1]。这是一个平稳的算法,这意味着如果比较的两个项目是相同的它将会保留他们原来的顺序。

你也可以写一个能返回结合了两个键值的值的key函数并且一气呵成地完成排序。令人惊奇的是这不一定会更快,你需要测试下哪一个解决方案在你的情况下会更快,这取决于数据和key函数。

>>> def keyfunction(item):
...     """Sorting on descending length and alphabetically"""
...     return -len(item), item
>>> names = ['Adam', 'Donald', 'John']
>>> names.sort(key=keyfunction)
>>> names
['Donald', 'Adam', 'John']

key参数在Python 2.4引入的,所以如果你想要支持Python 2.3就不能用它了。如果你想要用key函数做很多的排序,最好的办法是为Python 2.3实现一个简单的在Python 2.4及之后版本的sorted()函数并且用那个来替代内置的sorted()。

>>> import sys
>>> if sys.version_info < (2, 4):
...    def sorted(data, key):
...        mapping = {}
...        for x in data:
...            mapping[key(x)] = x
...        keys = mapping.keys()
...        keys.sort()
...        return [mapping[x] for x in keys]
>>> data = ['ant', 'Aardvark', 'banana', 'Dingo']
>>> sorted(data, key=str.lower)
['Aardvark', 'ant', 'banana', 'Dingo']

Python 2.4现在已经有5年那么老了,所以你不太可能需要支持Python 2.3。


警告

使用-3选项运行Python只会在你显示地使用cmp参数时警告你:

>>> l.sort(cmp=cmpfunction)
__main__:1: DeprecationWarning: the cmp argument is not
supported in 3.x

但是如果像下面这样使用将不警告:

>>> l.sort(cmpfunction)

所以这个语法可能会漏网之鱼。在Python 3运行这些代码时,在这些情况下你会得到一个TypeError: mustuse keyword argument for key function 。


在Python 2.7和Python 3.2及后面的版本有一个函数可以通过一个包装类把比较函数转换成key函数。它是很聪明的,但反而会让比较函数更慢,因此这只是最后的手段。

>>> from functools import cmp_to_key
>>> def compare(a, b): return cmp(a[1:], b[1:])
>>> sorted(['Adam', 'Donald', 'John'], key=cmp_to_key(compare))
['Adam', 'John', 'Donald']

使用丰富的比较运算符

在Python 2最常见的支持对象比较和排序的方式是实现一个使用内置cmp()函数的__cmp__()方法,像这样类就可以根据姓氏排序了:

>>> class Orderable(object):
...
...     def __init__(self, firstname, lastname):
...         self.first = firstname
...         self.last = lastname
...
...     def __cmp__(self, other):
...         return cmp("%s, %s" % (self.last, self.first),
...                    "%s, %s" % (other.last, other.first))
...
...     def __repr__(self):
...         return "%s %s" % (self.first, self.last)
...
>>> sorted([Orderable('Donald', 'Duck'),
...         Orderable('Paul', 'Anka')])
[Paul Anka, Donald Duck]

然则,你可以拥有类似colors这样的即不能比较大小但可以比较是否相等的类,因此从Python 2.1开始也支持一个比较操作符对应一个方法的丰富的比较方法。他们是 __lt__ 对应 <,__le__ 对应<=, __eq__ 对应==, __ne__ 对应!=, __gt__对应> ,以及 __ge__ 对应 >=。

同时拥有丰富的比较方法和__cmp__()方法违反了只有一种方式来实现比较的原则,所以在Python 3对__cmp__()的支持已经被移除了。因此对Python 3,如果你的类需要被比较的话,你必须要所有的丰富比较操作符。 没有必要在开始支持Python 3做这个,但做这些是一种体验。

相对棘手的

写比较方法会相当棘手,因为你可能需要处理不同类型的比较。如果比较函数不知道如何与其他对象比较时会返回NotImplemented常量。返回的NotImplemented 可以作为一个Python的比较标志来让Python偿试反向比较。所以如果你的__lt__()方法返回NotImplemented那么Python会偿试用调用其他类的__gt__()方法来代替。


注意

这意味着你永远都不应该在你的丰富比较方法中调用其他类的比较操作!你会找到几个转换大于(就像self.__gt__(other))成返回other < self的丰富比较助手的例子。但是你调用other.__lt__(self)却会返回NotImplemented而不是再次偿试self.__gt__(other)并且无限递归。


一旦你理解了所有案例,实现一个好的正解运行的丰富比较操作集不会困难,但是掌握那些却不是完全不重要。你可以用许多不同的方式做它,我的首选方式是这样混合,这样就可以同时在Python 2和Python 3很好地工作。

class ComparableMixin(object):def _compare(self, other, method):try:return method(self._cmpkey(), other._cmpkey())except (AttributeError, TypeError):# _cmpkey not implemented, or return different type,# so I can't compare with "other".return NotImplementeddef __lt__(self, other):return self._compare(other, lambda s, o: s < o)def __le__(self, other):return self._compare(other, lambda s, o: s <= o)def __eq__(self, other):return self._compare(other, lambda s, o: s == o)def __ge__(self, other):return self._compare(other, lambda s, o: s >= o)def __gt__(self, other):return self._compare(other, lambda s, o: s > o)def __ne__(self, other):return self._compare(other, lambda s, o: s != o)

前面提到的Ptyhon 3.2的functools.total_ordering()类装饰器也是一个很好的解决办法,并且它同样可以用复制并用在其它版本上。但是因为它用的类装饰器,所以不能在Python 2.6以下版本使用。

使用前面的混合,你需要实现返回能一反被比较的对象键值的_cmpkey()方法,类似于比较时用的key()函数。实现真情可以类似这样:

>>> from mixin import ComparableMixin
>>> class Orderable(ComparableMixin):
...
...     def __init__(self, firstname, lastname):
...         self.first = firstname
...         self.last = lastname
...
...     def _cmpkey(self):
...         return (self.last, self.first)
...
...     def __repr__(self):
...         return "%s %s" % (self.first, self.last)
...>>> sorted([Orderable('Donald', 'Duck'),
...         Orderable('Paul', 'Anka')])
[Paul Anka, Donald Duck]

如果对象比较时没有实现 _cmpkey()方法或者前面的混合使用self._cmpkey()的返回值返回的值不能被比较,前面的混合将会返回NotImplemented。 这意味着每一个对象都要有一个能返回能合其他有能返回一个元组的_cmpkey()的对象比较的_cmpkey()。以及最重要的是如它不能被比较,如果其他对象如何去比较两个对象,运算符会备用地寻问其他对象。这样你就拥有了一个有最大机会进行有意义比较的对象。

实现 __hash__()

在Python 2,如果你想实现 __eq__() 你也需要重写__hash__()。这是因为两个对象比较赶快相等也需要相同的哈希值。 如果对象是可变的,你必须要把设置__hash__成None来把它标定成可变的。这意味着你不能把它用作字典的键值, 这很好,只有不可变对象可以做字典键。

在Python 3,如果你定义了__eq__(), __hash__ 会被自动设置成, 并且对象变成不能被哈希。所以对于Python 3,除非是一个不变的对象或者你想把它作为一个键值,你都不需要重写__hash__()。

被__hash__()返回的值需要是一个整数,两个比较相等的对象一定有相同的哈希值。它必须在对象的整个存活期内保持不变,这也是为什么可变对象为什么必须设置__hash__ = None来标记它们是不能哈希的。

如果你使用前面提及的实现比较运算符的_cmpkey()方法,那么不实现__hash__()是很容易的:

>>> from mixin import ComparableMixin
>>> class Hashable(ComparableMixin):
...     def __init__(self, firstname, lastname):
...         self._first = firstname
...         self._last = lastname
...
...     def _cmpkey(self):
...         return (self._last, self._first)
...
...     def __repr__(self):
...         return "%s(%r, %r)" % (self.__class__.__name__,
...                                self._first, self._last)
...
...     def __hash__(self):
...         return hash(self._cmpkey())
...
>>> d = {Hashable('Donald', 'Duck'): 'Daisy Duck'}
>>> d
{Hashable('Donald', 'Duck'): 'Daisy Duck'}

这个类属性按照惯例使用前导的下划线来标记成内部使用,但他们不是传统意义上的不可变。如果你想要一个在Python中真正的不可变类,最简单的办法就是继承collections.namedtuple,但那超出了本书的范畴。

确保你没有用任何一个被移除的模块

很多在标准库中的模块在Python 3中已经被丢弃了。他们中的多大数是对不提供被新模块支持的更好接口的旧系统的特别支持。

如果你使用了一些更常用的模块,使用-3选项运行Python 2.6 将会警告你。你使用了那些Python 2.6不会发出警告的模块是相当不可能的,但是如果你正在或者计划同时支持Python 2和Python 3,如果有话你必须要尽可能替换成现在对应的。

被移除的模块 上有一个被移除模块的列表。

测试覆盖率和tox

有一个好的测试集对任何项目都是有价值的。当你添加Python 3支持时,测试可以把这个过程加快很多,因为你需要一遍又一遍地执行测试而手工测试要花掉大量时间。

用更多的测试来提高测试覆盖率总是一个好主意。最流行的获得你的模块的测试覆盖率的Python 工具是Ned Batchelder的coverage 模块。[2] 许多像zope.testing、nose及py.test这样的测试运行框架都包含对coverage模块的支持,所以你可能已经安装了。

如果你在开发一个支持所有版本Python的模块,为所有这些版本执行测试迅速会变成一个令人厌烦的工作。为了解决这个Holger Krekel制作了一个叫做tox[3]的工具,这个工具会为每一个你想要支持的版本安装虚拟环境并且使用一个简单的命令对所有这些版本执行你的测试。它似乎是一件小事,它是的,但是它会增加一点点更愉快的体验。如果你计划同时支持Python 2和Python 3,你应该试试看。

可选:在字典中使用迭代器方法(iterator-methods)

从Python 2.2开始内置的字典类型有iterkeys()、 itervalues() 、 iteritems() 方法。他们产生的数据像是keys()、values() 和items()产生的,但他们返回的不是列表而是迭代器(iterator),在使用巨大的字典时这可以节省时间和内存。

>>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'}
>>> dict.keys()
['Donald', 'John', 'Adam']>>> dict.iterkeys()
<dictionary-keyiterator object at 0x...>

在Python 3标准的keys(),、values() 和 items()返回和迭代器非常类似的字典视图。因为不久之后在这些方法中的迭代器变量要被移除。

2to3 会把迭代器方法的使用转换成标准方法。通过明确地使用迭代器方法,你表明不需要一个列表,这对2to3的转换是有帮助的,否则为了安全要用alist(dict.values())来取代dict.values()调用。

Python 2.7也有像.viewitems()、.viewkeys() 和 .viewvalues()这样可用的新视图迭代器,但因为他们在更早的Python版本中不存在所以他们只有在你打算放弃Python 2.6及更早的版本时才有用。

还要注意的是如果你的代码依靠返回的列表,那么你可能误用未知的字典。例如:下面的代码,你事实上不能确定每次的键的顺序是相同的,因为这个原因你可能无法预测该代码的行为。这在调试时会带来一些麻烦。

>>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'}
>>> dict.keys()
[0]'Donald'

记住,如果你想要遍历字典就使用for x in dict,这样在Python 2和Python 3中都会自动使用迭代器。

>>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'}
>>> for x in dict:
...     print '%s + %s == True' % (x, dict[x])
Donald + Daisy == True
John + Yoko == True
Adam + Eve == True

附注

[1] http://en.wikipedia.org/wiki/Timsort
[2] https://pypi.python.org/pypi/coverage
[3] http://testrun.org/tox/latest/
[4] 见 http://www.python.org/download/releases/2.2.3/descrintro/#mro

在湖闻樟注:

原文http://python3porting.com/preparing.html

引导页Supporting Python 3:(支持Python3):深入指南

目录Supporting Python 3(支持Python 3)——目录

转载于:https://my.oschina.net/soarwilldo/blog/506415

Supporting Python 3(支持python3)——为Python 3做准备相关推荐

  1. Supporting Python 3(支持python3)——常见的迁移问题

    2019独角兽企业重金招聘Python工程师标准>>> 常见的迁移问题 如果你按照该建议来确保你的代码使用Python 2.7 - 3来运行没有警告,一些现在会遇到的简单错误都是可以 ...

  2. sqap不支持python3吗_Supporting Python 3(支持python3)——欢迎来到Python 3

    标签: 欢迎来到Python 3 On Christmas Day 1999 I sat down to write my first piece of software in Python. My ...

  3. python不支持什么编程,python解释器在语法上不支持什么编程方式-Python教程

    python诠释器正在语法上没有支持甚么编程形式 python诠释器正在语法上没有支持[天然言语]. python诠释器正在语法上支持的编程形式有:语句.面向进程.面向工具. (保举:编程教程) 天然 ...

  4. python不支持_为什么 Python 不支持函数重载?而其他语言大都支持?

    唉,觉得真的没必要,在python下边咋谈都觉得有理啦.用惯了,觉得怎么玩都ok. 而且有或者没有,不是从是不是能实现最终的功能上来讲,而是使用的时候,看起来在某方面是不是更便利或者更容易理解或者是更 ...

  5. python不支持下标访问,Python支持使用字典的“键”作为下标来访问字典中的值。...

    Python支持使用字典的"键"作为下标来访问字典中的值. 答:√ 对Watson-Crick的DNA模型叙述正确的是( ) 答:配对碱基之间形成氢键 67.2018年,中国PC搜 ...

  6. Python3 实例--Python 平方根(**,cmath,复数的i和j部分)

    #Python3 实例--Python 平方根 print("Python3 实例--Python 平方根") #求正数的平方根 num = float(input()) num_ ...

  7. 让python cookie支持特殊字符

    让python cookie支持特殊字符 先对cookie做下简单的介绍 cookie的作用: tcp协议能够通过三次握手建立连接,client发送的多次请求能够通过句柄维护同一个连接.可是http协 ...

  8. python安装教程-CentOS 8 Python 安装教程

    Python是世界上最受欢迎的编程语言之一. 凭借其简单易学的语法,Python是初学者和经验丰富的开发人员的流行选择. 与其他Linux发行版不同,CentOS 8默认不安装Python. 如您所知 ...

  9. 学python可以干嘛-学习Python可以做什么

    Python技术可做web开发.Python技术可做数据分析.Python技术可做人工智能.将Python用于机器学习,流行的Python机器学习库和框架,其中最流行的两个是scikit-learn和 ...

最新文章

  1. java把信息存到文件里,Java 如何将字符串信息直接写保存到文本文件?
  2. JDBCUtils工具类编写
  3. 作业27-登录之后更新导航
  4. android开发java环境_搭建Android开发环境 - Android - mobile - JavaEye论坛
  5. P6240 好吃的题目(分治+背包)
  6. leetcode 810. 黑板异或游戏
  7. LeetCode-35 搜索插入位置
  8. bzoj4772 显而易见的数论
  9. 从石头剪刀布浅谈算法的作用
  10. 改善你的生活品质,可以从一块小小的单片机开始
  11. Atitit 技术学习 的方法总结 attilax总结 目录 1.1. 跨框架 1 1.2. 跨语言学习法 1 1.3. 概念学习法 1 1.1 在比较中学习 多语言  2 1 1.3 .2 在历史
  12. 数据挖掘——数据预处理
  13. 【Kotlin -- 知识点】学习资料
  14. 周记20180413
  15. 使用网络模拟器 Packet Tracer和交换机的端口配置与管理及Telnet远程登陆配置
  16. 小程序-手写签名(附代码)
  17. 【结构化思考】边缘计算架构 3D模型参考
  18. 计算机代码rsi是什么意思,RSI指标计算(修正版)
  19. 高德导航免费,那他靠什么收入?
  20. 高德地图Javascript API设置域名白名单

热门文章

  1. python 高阶函数之 reduce
  2. Animator动画XML实现
  3. 移除VS解决方案中的TFS版本控制
  4. substring用法
  5. 解决同一页面中两个iframe互相调用jquery,js函数
  6. 第二阶段 工作总结 05
  7. IOS逆向【2】-cydia之开发者模式
  8. 超越kmeans:聚类算法概述
  9. Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File (四) —— ContentProvider...
  10. ant的设置properties