我维护的Django网站目前使用的是Python 2.7,但我知道我必须在几个月内将其升级到Python 3。如果我现在正在编写必须在Python 2中工作的代码,是否有一种 Pythonic 方式来编写它,这样如果我知道语法是什么,它也可以在Python 3中工作而不需要任何更改将在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))
答案 0 :(得分:8)
有official documentation建议如何做到这一点。随着时间的变化,文档已经发生了变化,因此值得直接转到源代码(特别是如果您在编写完成后的一两年内阅读了这个答案)。
还值得阅读Conservative Python 3 Porting Guide并浏览Nick Coghlan的Python 3 Q& A,尤其是this section。
从2018年初开始追溯:
目前的官方建议是:
pip install coverage
)pip install future
)pip install pylint
)pip install caniusepython3
)pip install tox
)注意最后一个建议。 Guido和另一个核心开发人员都大量参与领导大型团队将大型2.7代码库移植到3.x,并发现mypy非常有用(特别是在处理字节与unicode问题时)。事实上,这很大一部分原因是逐渐静态打字现在已成为该语言的官方部分。
您几乎肯定也想使用2.7中提供的所有future
语句。这是显而易见的,他们似乎忘记了将其从文档中删除,但是,除了让您的生活更轻松(例如,您可以编写print
函数调用),futurize
和{{1} }(以及modernize
和six
)需要它。
该文档旨在让人们在不久的将来不可逆转地过渡到Python 3。如果您计划长时间坚持使用双版本代码,那么可能会更好地遵循之前的建议,这些建议主要围绕使用six而不是futurize。六个涵盖了两种语言之间的更多差异,并且还使您编写了明确关于双版本的代码,而不是尽可能接近Python 3,同时仍在2.7中运行。但缺点是你有效地做了两个端口 - 一个从2.7到六个双版本代码,然后,从3.x-只有六个代码到3.x-only"天然"代码。
最初推荐的答案是使用2to3,这是一个可以自动将Python 2代码转换为Python 3代码的工具,或指导您这样做。如果您希望代码在中使用,则需要提供Python 2代码,然后在安装时运行sixer
以将其移植到Python 3.这意味着您需要测试代码两种方式,通常修改它,使其仍然在2.7中工作,但也可以在2to3之后的3.x中工作,这并不总是很容易解决。事实证明这对于大多数非平凡的项目来说都是不可行的,因此核心开发人员不再推荐它 - 但它仍然内置在Python 2.7和3.x中,并且正在进行更新。
2to3还有两个变体:sixer自动将Python 2.7代码移植到使用六个代码的双版本代码,3to2允许您编写Python 3代码和自动端口在安装时回到2.7。这些都很受欢迎,但似乎不再使用了很多;现代化和未来化分别是他们的主要继承者。
针对您的具体问题,
2to3
如果您不介意2.7中的次要性能成本,则可以同时使用这两种方法。kwargs.items()
更改为iteritems
。items
,其中six.iteritems(kwargs)
位于2.7,iteritems
位于3.x。items
,这将在2.7中执行six.viewitems(kwargs)
(这与viewitems
在3.x中的操作相同,而不仅仅是类似的。)< / LI>
items
更改为kwargs.iteritems()
。six.iteritems(kwargs)
并在安装时自动将其转换为kwargs.items()
。viewitems
或viewitems
会使代码仍然正确输入。 答案 1 :(得分:2)
您可以导入未来的包
from future import ....
nested_scopes 2.1.0b1 2.2 PEP 227:静态嵌套范围
发电机2.2.0a1 2.3 PEP 255:简单发电机
division 2.2.0a2 3.0 PEP 238:更改分部操作员
absolute_import 2.5.0a1 3.0 PEP 328:进口:多线和绝对/相对
with_statement 2.5.0a1 2.6 PEP 343:“with”Statement
print_function 2.6.0a2 3.0 PEP 3105:打印功能
unicode_literals 2.6.0a2 3.0 PEP 3112:Python 3000中的字节文字
答案 2 :(得分:2)
让您的Django项目与两个Python版本兼容,包括以下步骤:
在每个模块的顶部添加from __future__ import unicode_literals
,然后使用通常的引号,不带Unicode前缀用于Unicode字符串,b前缀用于字节串。
要确保值为bytestring,请使用django.utils.encoding.smart_bytes
函数。要确保值为Unicode,请使用django.utils.encoding.smart_text
或django.utils.encoding.force_text
函数。
在您的模型中使用__str__
方法而不是__unicode__
并添加python_2_unicode_compatible
装饰器。
# 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")
要遍历字典,请使用django.utils.six中的iteritems(),iterkeys()和itervalues()。看看以下内容:
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))
在捕获异常时,请使用as关键字,如下所示:
try:
article = NewsArticle.objects.get(slug="hello-world")
except NewsArticle.DoesNotExist as exc:
pass
except NewsArticle.MultipleObjectsReturned as exc:
pass
使用django.utils.six检查值的类型,如下所示:
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)
使用django.utils.six.moves中的范围,而不是xrange,如下所示:
from django.utils.six.moves import range
for i in range(1, 11):
print(i)
<强> Source link 强>
答案 3 :(得分:1)
除了从未来导入之外,还有六个项目旨在提供Python 2和Python 3之间的api兼容性:https://pypi.org/project/six/。
您的示例代码可以在版本2和3之间兼容:
import six
for key, value in six.iteritems(dict):
log.info("{0}: {1}".format(key, value))
仍有一些东西在2和3之间不兼容,就像f弦一样。
答案 4 :(得分:1)
有一些不同的工具可以帮助您确保编写python2 / 3兼容代码。
如果您有兴趣将python2代码移植到python3中,那么标准库附带的2to3程序将尝试将python 2程序转换为python 3。
https://docs.python.org/2/library/2to3.html
另一个很棒的工具是pylint。 pylint是一个python linter,可以在不修复问题的情况下向你描述问题。如果你在python3环境中pip install pylint,那么它将根据python 3的规则分析你的代码。如果你使用python 2安装pylint,它将使用python 2的规则执行相同的操作。
还有其他流行的和类似的工具,如flake8或autopep8,但我不熟悉它们足以宣传它们。
答案 5 :(得分:1)
添加到每个python2文件,这是第一行:
from __future__ import absolute_import, unicode_literals
使用下面的字符串,迭代,元类,...
isinstance(sth, six.string_types)
six.iteritems(dict)
@six.add_metaclass(Meta)
依此类推six reference