如何编写同时在Python 2和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

我维护的Django网站目前使用Python2.7,但我知道我必须在几个月内将其升级到Python3。如果我现在正在编写必须在Python 2中工作的代码,那么如果我知道Python 3中的语法将是什么,是否有一种类似Python的方式来编写它,这样它也可以在Python 3中工作而不做任何更改?理想情况下,我希望代码即使在升级后也能继续工作,而不需要更改它,但我可以很容易地在代码库中找到我在哪里完成了这项工作,这样我就可以在有时间时更改代码。下面是我所说的一个例子:

# 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下的类型)
注意最后一个建议。Guido和另一个核心开发人员都积极参与领导大型团队将大型2.7代码库移植到3.x,并发现mypy非常有帮助(特别是在处理字节与unicode问题时)。事实上,这是渐进式静态输入现在成为语言正式部分的一个重要原因

几乎可以肯定的是,您希望使用2.7中提供的所有语句。这很明显,他们似乎忘记了将其从文档中删除,但是,除了让您的生活更轻松(例如,您可以编写
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将允许您编写
    six.iteritems(kwargs)
    ,这将在2.7中编写
    iteritems
    ,在3.x中编写
    items
  • six还允许您编写
    six.viewitems(kwargs)
    ,这将在2.7中实现
    viewitems
    (与3.x中的
    items
    功能相同,而不仅仅是类似)
  • modernize and sixer将自动将
    kwargs.iteritems()
    更改为
    six.iteritems(kwargs)
  • 3to2将允许您编写
    kwargs.items()
    ,并在2.x上安装时自动将其转换为
    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)