如何编写同时在Python 2和Python 3中工作的代码?
我维护的Django网站目前使用Python2.7,但我知道我必须在几个月内将其升级到Python3。如果我现在正在编写必须在Python 2中工作的代码,那么如果我知道Python 3中的语法将是什么,是否有一种类似Python的方式来编写它,这样它也可以在Python 3中工作而不做任何更改?理想情况下,我希望代码即使在升级后也能继续工作,而不需要更改它,但我可以很容易地在代码库中找到我在哪里完成了这项工作,这样我就可以在有时间时更改代码。下面是我所说的一个例子:如何编写同时在Python 2和Python 3中工作的代码?,python,django,python-3.x,Python,Django,Python 3.x,我维护的Django网站目前使用Python2.7,但我知道我必须在几个月内将其升级到Python3。如果我现在正在编写必须在Python 2中工作的代码,那么如果我知道Python 3中的语法将是什么,是否有一种类似Python的方式来编写它,这样它也可以在Python 3中工作而不做任何更改?理想情况下,我希望代码即使在升级后也能继续工作,而不需要更改它,但我可以很容易地在代码库中找到我在哪里完成了这项工作,这样我就可以在有时间时更改代码。下面是我所说的一个例子: # Python 2 us
# Python 2 uses 'iteritems'
def log_dict(**kwargs):
for key, value in kwargs.iteritems():
log.info("{0}: {1}".format(key, value))
# Python 3 uses 'items'
def log_dict(**kwargs):
for key, value in kwargs.items():
log.info("{0}: {1}".format(key, value))
您可以导入未来的包
from future import ....
嵌套_作用域2.1.0b1 2.2 PEP 227:静态嵌套作用域
发电机2.2.0a1 2.3 PEP 255:简单发电机
分部2.2.0a2 3.0 PEP 238:更改分部运算符
绝对输入2.5.0a1 3.0 PEP 328:输入:多行和绝对/相对
with_语句2.5.0a1 2.6 PEP 343:“with”语句
打印功能2.6.0a2 3.0 PEP 3105:使打印成为功能
unicode_literals 2.6.0a2 3.0 PEP 3112:Python 3000中的字节文字有一些建议方法。随着时间的推移,情况发生了变化,这些文档也发生了变化,因此直接访问源代码是值得的(特别是如果您在编写本答案一两年后阅读本答案)
尤其值得一读尼克·科格兰(Nick Coghlan)的《Python 3问答》(Python 3 Q&A)一书
追溯到2018年初:
未来主义
目前的官方建议是:
- 只担心支持Python 2.7
- 确保您具有良好的测试覆盖率(可以提供帮助;
)pip安装覆盖率
- 了解Python2和Python3之间的区别
- 使用(或)更新您的代码(例如,
)pip-install-future
- 用于帮助确保您的Python3支持不会倒退(
)pip安装pylint
- 用于找出哪些依赖项阻止您使用Python 3(
)pip安装caniusepython3
- 一旦依赖项不再阻止您,请使用持续集成来确保与Python 2和3兼容(可以帮助测试多个版本的Python;
)pip install tox
- 考虑使用可选的静态类型检查,以确保您的类型使用在Python2和Python3中都有效(例如,使用检查Python2和Python3下的类型)
print
函数调用),futurize
和modernize
(和sixer
)之外,还需要它
六
该文档旨在帮助人们在不久的将来不可逆转地过渡到Python 3。如果您打算长期使用双版本代码,那么最好遵循前面的建议,这些建议主要围绕使用而不是futurize。Six涵盖了这两种语言之间的更多差异,还使您可以编写明确的代码,使其成为双版本,而不是在2.7中运行时尽可能接近Python 3。但缺点是,您实际上在做两个端口:一个是从2.7到基于6的双版本代码,然后是从3.x-only-six代码到3.x-only“本机”代码
2to3
最初推荐的答案是使用,一个可以自动将Python2代码转换为Python3代码的工具,或者指导您这样做。如果希望代码同时在这两种环境中工作,则需要交付Python 2代码,然后在安装时运行2to3
,将其移植到Python 3。这意味着您需要以两种方式测试代码,并且通常会对其进行修改,使其在2.7中仍然有效,但在2to3之后在3.x中也有效,这并不总是容易实现的。这对于大多数非平凡的项目来说是不可行的,因此核心开发人员不再推荐它,但它仍然内置在Python2.7和3.x中,并得到更新
2to3上还有两种变体:自动将Python2.7代码移植到使用六个版本的双版本代码,并允许您在安装时编写Python3代码和自动移植回2.7。这两种方法都流行了一段时间,但似乎不再被广泛使用;现代化和未来化分别是它们的主要继承者
针对你的具体问题,
kwargs.items()
- 2to3可以在3.x上安装时自动将
更改为iteritems
items
- futurize可用于执行上述任一操作
- six将允许您编写
,这将在2.7中编写six.iteritems(kwargs)
,在3.x中编写iteritems
items
- six还允许您编写
,这将在2.7中实现six.viewitems(kwargs)
(与3.x中的viewitems
功能相同,而不仅仅是类似)items
- modernize and sixer将自动将
更改为kwargs.iteritems()
six.iteritems(kwargs)
- 3to2将允许您编写
,并在2.x上安装时自动将其转换为kwargs.items()
viewitems
- mypy可以验证您只是将结果用作一般iterable(rathe)
import six for key, value in six.iteritems(dict): log.info("{0}: {1}".format(key, value))
from __future__ import absolute_import, unicode_literals
isinstance(sth, six.string_types) six.iteritems(dict) @six.add_metaclass(Meta)
# models.py # -*- coding: UTF-8 -*- from __future__ import unicode_literals from django.db import models from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class NewsArticle(models.Model): title = models.CharField(_("Title"), max_length=200) content = models.TextField(_("Content")) def __str__(self): return self.title class Meta: verbose_name = _("News Article") verbose_name_plural = _("News Articles")
from django.utils.six import iteritems d = {"imported": 25, "skipped": 12, "deleted": 3} for k, v in iteritems(d): print("{0}: {1}".format(k, v))
try: article = NewsArticle.objects.get(slug="hello-world") except NewsArticle.DoesNotExist as exc: pass except NewsArticle.MultipleObjectsReturned as exc: pass
from django.utils import six isinstance(val, six.string_types) # previously basestring isinstance(val, six.text_type) # previously unicode isinstance(val, bytes) # previously str isinstance(val, six.integer_types) # previously (int, long)
from django.utils.six.moves import range for i in range(1, 11): print(i)