文章信息
作者:史晨 Charles Shih
创建时间:2018年08月24日
问题描述
我之前写过一个测试脚本,解释器用的是Python2.7,在RHEL7上用起来是没有问题的。但是最近要在RHEL8上进行这个测试,RHEL8采用的Python解释器是Python3,因此我的脚本不能直接运行在RHEL8上。
现在的现实情况如下:
1. RHEL7默认只安装了Python2的解释器;
2. RHEL8默认只安装了Python3的解释器;
3. 脚本需要同时兼容Python2和Python3解释器,因为没必要维护两份代码;
4. 测试框架尽量保持简洁,尽量少改动被测试系统的软件部署,因此不会为RHEL7安装Python3也不会为RHEL8安装Python2;
出问题的语句最开始是:
if not isinstance(params['backend'], (unicode, str)):
经过2to3
(官方的代码升级工具)转换后变成了:
if not isinstance(params['backend'], str):
这是因为Python3当中没有Unicode这种类型。因此,如果2to3
不把unicode
优化掉的话,代码在Python3中就会直接报错。
但是经过转换的语句在Python2.7解释器下,当params['backend']
为Unicode时,就会出现误判(其实Unicode是可接受的),影响脚本的正确执行。
原理分析
因为Python3解释器对字符串的处理方式进行了调整(这是个典型的坑,Google上相关的文章很多,下面我也推荐了几篇)。
简单来说,它们的对应关系如下:
Python2 | Python3 |
---|---|
str | bytes |
unicode | str |
于是根据这个规则,原始的代码应该改为:
if not isinstance(params['backend'], (str, bytes)):
然而这样的语句在Python2中还是无法正确判断Unicode字符串(虽然Python2.7解释器支持bytes
类型,但是它等同于str
类型)。
关于unicode_literals
对于Python3字符串这个坑,官方有一些解决方案,比如:
# still running on Python 2.7
from __future__ import unicode_literals
这个语句可以让Python2.7解释器像Python3解释器一样的工作,但这并不能解决我的问题。因为我的需求是提供同时兼容两种解释器的代码。
关于-*- coding: utf-8 -*-
有些时候我们在Python代码的开头,添加下面的语句,似乎与这个问题有一定的联系:
# -*- coding: utf-8 -*-
然而事实上,这个操作只是为了告诉Python解释器,这个代码文件里用到的所有字符串都是以utf-8
编码的。这对于需要在代码中包含中文字符是很有帮助的,但对于提供同时兼容两种解释器的代码这件事来说,是一点儿忙也帮不上的。
解决办法
其实我们不妨换一个思路,不用去管一个字符串是unicode
还是str
亦或是bytes
,它是什么都好,只要它还是个字符串就行。这就是我解决问题的思路,按照这个思路我把代码改成了:
if type(params['backend']) not in (type(u''), type(b'')):
这样就写出了同时兼容Python2和Python3解释器的代码。