专栏首页快乐学Python写Python 2/3兼容代码

写Python 2/3兼容代码

为什么兼容?

正如Flask和Jinja2的作者所说,大部分需求来自用户。原因是大多数开发人员(一些好的第三方库和框架,例如请求,Flask ......)认为3不够好(没错),没有迁移的愿望,所以驱使他们迁移大多数它来自用户的请求。

我听说分布式版本控制系统Mercurial(CPython用来管理代码)尚未迁移到3。

据说Python的未来不一定是CPython(有人说是PyPy),但CPython的未来肯定是3,所以如果你使用CPython,你就可以迁移。

如果你有一天写图书馆怎么办?

嗯,我纯粹是胡说八道,信不信由你。

一些辅助

在迁移过程中,我们可以考虑一些项目(提供兼容性),它们可以提醒我们哪里不兼容以及如何去做(但不要太依赖)。

  • 2to3 Python 2.x 自带。
  • six 推荐(无相关依赖,单个文件)。
  • future
  • modernize 构建于 2to3 之上。
  • pies 由于这个库,tox测试失败了(#19),但现在似乎已经解决了。 常见问题
  • print 语句被 print() 方法替代。 不过如果你不支持 2.5- ,那么这应该不是问题。兼容 2.6 可以这样: from __future__ import print_function 2.7直接支持此功能;如果你想在2.5中支持这个功能,你可以使用上面提到的 six
  • 一些不再返回列表(lists)的著名的 API 。

关于测试

在进行任何迁移之前,请确保测试用例(当然,首先你的项目得有测试用例)可以正常工作并且有意义。标准库迁移中的许多问题来自于没有意识到迁移到python3时,测试用例的行为也会发生变化。

字符串处理

Python 3中最大的变化无疑是Unicode接口的变化。不幸的是,这些变化在某些地方非常痛苦,并且在整个标准库中也处理不一致。大部分时间移植显然都会浪费在这个主题上。这个主题本身就是一篇完整的文章,但这里有一个快速的备忘单:

  • 'foo'总是指我们称之为实现的本地字符串。这是用于标识符,源代码,文件名和其他低级函数的字符串。另外在2.x中,只要它仅限于ASCII字符,就可以作为Unicode字符串中的文字。 此属性对于统一代码库非常有用,因为Python 3的一般趋势是在以前不支持它的某些接口中引入Unicode,但从不反过来。由于本机字符串文字“升级”为Unicode但仍然在2.x中支持Unicode,因此该字符串文字非常灵活。 例如,datetime.strftime函数在Python 2中严格不支持Unicode,但仅在3.x中支持Unicode。因为在大多数情况下2.x上的返回值是ASCII,只有这样的东西在2.x和3.x中才能很好地工作: >>> u'<p>Current time: %s' % datetime.datetime.utcnow().strftime('%H:%M') u'<p>Current time: 23:52' 传递给strftime的字符串是本机的(因此2.x中的字节和3.x中的Unicode)。返回值是本地字符串,仅限ASCII。因此,在2.x和3.x上,一旦字符串格式化,它将是Unicode字符串。
  • u'foo'总是指Unicode字符串。许多库已经在2.x中拥有非常出色的Unicode支持,因此对于许多人而言,字面意思不应该令人惊讶。
  • b'foo'总是指能够容纳任意字节的东西。由于2.6实际上没有像Python 3.3那样的字节对象,并且Python 3.3缺少实际的字节串,因此这个文字的实用性确实有点受限。当与在2.x和3.x上具有相同接口的bytearray对象配对时,它立即变得更有用: >>> bytearray(b' foo ').strip() bytearray(b'foo') 因为它也是可变的,所以修改原始字节非常有效,你可以通过将最终结果再次包装在bytes()中,将它简单地转换为更常规的东西。

除了这些基本规则之外,我还将text_type,unichr和string_types变量添加到我的兼容性模块中,如上所示。有了这些可用的重大变化是:

  • isinstance(x,basestring)变为isinstance(x,string_types)
  • isinstance(x,unicode)变为isinstance(x,text_type)。
  • isinstance(x,str)意图捕获字节变为isinstance(x,bytes)或isinstance(x,(bytes,bytearray))。

我还创建了一个implements_to_string类装饰器,它可以帮助用unicodestr方法实现类:

if PY2:
    def implements_to_string(cls):
        cls.__unicode__ = cls.__str__
        cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
        return cls
else:
    implements_to_string = lambda x: x

我的想法是你只需要在2.x和3.x上实现str并让它返回Unicode字符串(是的,在2.x中看起来有点奇怪),装饰器会自动将它重命名为unicode以获取2.x并添加一个str调用unicode并将返回值编码为utf-8。过去,这种模式在2.x模块中非常普遍。例如Jinja2和Django使用它。

以下是用法示例:

@implements_to_string
class User(object):
    def __init__(self, username):
        self.username = username
    def __str__(self):
        return self.username

字典

Python 3中更令人讨厌的变化之一是对字典迭代器协议的更改。在Python 2中,所有字典都有key(),values()和items()返回列表,iterkeys(),itervalues()和iteritems()返回迭代器。在Python 3中,没有任何一个存在。相反,它们被替换为返回视图对象的新方法。

keys()返回一个键视图,其行为类似于某种只读集,values()返回只读容器和iterable(不是迭代器!)和items()返回某种只读集 - 喜欢对象。与常规集不同,它也可以指向可变对象,在这种情况下,某些方法在运行时会失败。

从积极的方面来看,很多人都错过了观点不是迭代器,因此在很多情况下你可以忽略它。 Werkzeug和Django实现了一堆自定义字典对象,在这两种情况下都决定忽略视图对象的存在,让keys()和朋友返回迭代器。

由于Python解释器的限制,这是目前唯一明智的做法。它有一些问题:

  • 事实上,视图本身并不是迭代器,这意味着在一般情况下,您创建一个临时对象是没有充分理由的。
  • 由于解释器的限制,内置字典视图的类似集合的行为无法在纯Python中复制。
  • 实现3.x的视图和2.x的迭代器意味着很多代码重复。

下面是遍历字典的例子:

if PY2:
    iterkeys = lambda d: d.iterkeys()
    itervalues = lambda d: d.itervalues()
    iteritems = lambda d: d.iteritems()
else:
    iterkeys = lambda d: iter(d.keys())
    itervalues = lambda d: iter(d.values())
    iteritems = lambda d: iter(d.items())

为了实现像对象这样的字典,类装饰器可以再次变得有用:

if PY2:
    def implements_dict_iteration(cls):
        cls.iterkeys = cls.keys
        cls.itervalues = cls.values
        cls.iteritems = cls.items
        cls.keys = lambda x: list(x.iterkeys())
        cls.values = lambda x: list(x.itervalues())
        cls.items = lambda x: list(x.iteritems())
        return cls
else:
    implements_dict_iteration = lambda x: x

在这种情况下,你需要做的就是将keys()和friends方法实现为迭代器,其余的自动发生:

@implements_dict_iteration
class MyDict(object):
    ...

    def keys(self):
        for key, value in iteritems(self):
            yield key
    
    def values(self):
        for key, value in iteritems(self):
            yield value
    
    def items(self):
        ...

我会阅读所有的评论,所以无论你有什么想要说的,或者是想要分享的,甚至是问题之类的,都可以在下面留言。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • celery时差问题解决方法

    请记得点赞和分享这篇文章让更多的人看到它!另外,记得关注我的简书号马哥学Python,这样你就不会错过任何有价值的文章!

    马哥Python
  • Pycharm代码注释支持Google风格

    在设置中找到 Tools > Python Integrated Tools 下拉选择Docstring format为Google

    马哥Python
  • pyspark稠密向量(DenseVector)和稀疏向量(SparseVector)

    DenseVctor :稠密向量 其创建方式 Vector.dense(数据)

    马哥Python
  • 13个web安全-python网络测试工具+白帽黑客必备,大神整理,小白可以先收藏起来

    Python有很多完善可用的库,这里面就包含渗透测试工具,所以广大白帽黑客大多都使用Python语言,这样更有利于利用Python提供的这些库参与漏洞研究、逆向...

    用户1682544
  • 想学习Python爬虫,但是找不到电子书或者不知道找什么资料

    第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、i...

    云飞
  • 如何在macOS上安装Python 3并设置本地编程环境

    Python是一种多功能编程语言,可用于许多不同的编程项目。1991年首次出版,其名称灵感来自英国喜剧组织Monty Python,开发团队希望使Python成...

    藕丝空间
  • 日本的Python团队,竟将真实项目的经验总结写进这本书,附电子版

    这本书是来自真正的Python开发现场,名字叫**《Python开发实战》,它原本是日本公司用于培养Python新人所作,但却成为了Python开发者学习的经典...

    Python编程大咖
  • Python工资水平怎么样 学Python都有哪些优势

    Python工资水平怎么样?学Python都有哪些优势?现在AI人才缺口大,各大企业都在争抢这方面的人才。而Python又是AI只能和大数据的第一编程语言,因此...

    一墨编程学习
  • 这项决定你晋升速度的技能,80%的人都忽略了

    现在的职场竞争越来越激烈,不学上一两门新技能,保持自己知识更新,很容易被年轻后辈超越。有些人选择学一门外语,有些人选择学习职场上为人处事的能力。

    叫我龙总
  • Python 新手玩家都应该知道的编程技巧 !

    大佬 ,我是一个小白 ,很想学习 Python ,它能做些什么啊 ?您这边有什么好的入门建议吗 ?

    小小詹同学

扫码关注云+社区

领取腾讯云代金券