python 2,3,os.environ和unicode

时间:2015-06-23 12:57:01

标签: python python-3.x unicode

我是否可以将其编写为与python 2.x和3.x一起使用,而不对其进行明确测试?

# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)

import os
import sys

if (sys.version_info[0] > 2):
  # python 3.x
  os.environ['foo'] = 'bár'
  print(os.environ['foo'])
else:
  # python 2.x
  os.environ['foo'] = 'bár'.encode('utf8')
  print(os.environ['foo'].decode('utf8'))

4 个答案:

答案 0 :(得分:4)

设置时,可以使用异常处理;在Python 2上尝试将环境变量设置为unicode对象会引发异常:

try:
    # Python 3
    os.environ['foo'] = 'bár'
except UnicodeEncodeError:
    # Python 2
    os.environ['foo'] = 'bár'.encode('utf8')

获取时,尝试使用decode方法;它在Python 3中失败并出现属性错误:

try:
    # Python 2
    print(os.environ['foo'].decode('utf8'))
except AttributeError:
    # Python 3
    print(os.environ['foo'])

答案 1 :(得分:1)

总的来说,我认为Martijn的回答是最好的答案,但是如果你因为某种原因正在寻找替代方案,我会在一段时间内成功地使用它:

os.environ[key] = {
    type(''): lambda x: x.encode('utf-8'),  # Python 2
    str: lambda x: x,                       # Python 3
}.get(type(value), str)(value)

这有点棘手,但基本上在python2中,dict看起来像这样:

{unicode: lambda x: x.encode('utf-8'),
 str: lambda x: x}

并在python3中它看起来像这样:

{str: lambda x: x}

这是因为python3中的type('')str,所以第二个str是第一个body { background-color: rgb(224,224,224,0.6); background: url(../img/backlogo.png),url(../img/backlogo2.png); } 。然后它从dict中获取相应的lambda,将你的值转换为os.environ dict所需的格式。

这样做的好处是,如果您使用覆盖率报告工具,则dict将计为单个语句,并且python2和3将报告100%覆盖率,即使一个使用一个lambda而另一个使用另一个。如果你没有像我一样过于沉迷于100%的代码覆盖率,那么try / except方法通常更容易理解,更加pythonic和更惯用。

答案 2 :(得分:0)

编辑:我想我可能误解了您的问题。我认为你的问题涉及到每次访问os.environ时都不必测试python版本。但是,我相信你可能意味着如何在没有明确查看版本信息的情况下检查python版本。

如果在python 2上你可以用你自己的处理编码和解码的字典替换os.environ。或者作为一种更保守的方法,您可以创建一个对象,在必要时对字符串进行编码和解码,然后只通过此包装器访问os.environ。例如

取代os.environ

# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)

import os
import sys

if sys.version_info[0] < 3:
  class _EnvironDict(dict):
    def __getitem__(self, key):
      return super(_EnvironDict, self).__getitem__(key).decode("utf8")
    def __setitem__(self, key, value):
      return super(_EnvironDict, self).__setitem__(key, value.encode("utf8"))

  os.environ = _EnvironDict(os.environ)

s = 'bár'
os.environ['foo'] = s
print(os.environ['foo'])

一种不会取代的保守方法 os.environ

# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)

import os

if sys.version_info[0] > 2:
  class _Environ(object):
    def __init__(self, environ):
      self.environ = environ
    def __getitem__(self, key):
      return self.environ[key]
    def __setitem__(self, key, value):
      self.environ[key] = value
else:  
  class _Environ(object):
    def __init__(self, environ):
      self.environ = environ
    def __getitem__(self, key):
      return self.environ[key].decode("utf8")
    def __setitem__(self, key, value):
      self.environ[key] = value.encode("utf8")

environ = _Environ(os.environ)

s = 'bár'
environ['foo'] = s
print(environ['foo'])

答案 3 :(得分:0)

作为替代方案,您可以avoid the unicode_literals import if your code uses native string API heavily

#!/usr/bin/env python
from __future__ import division, absolute_import, print_function
import os

os.environ['foo'] = 'bar'
print(os.environ['foo'])
  • 以及问题中的代码,如果值为非ascii(它是您可能遇到的语言环境),则在Python 3上使用UnicodeEncodeError的Linux上的默认POSIX(C)语言环境中失败sshing,cron,init.d脚本等)
  • 如果sys.stdout.encoding不是utf-8
  • ,它不允许使用非ascii文字值,而不是无法打印这些值

要支持任意Unicode envvars,请在Windows和_wenviron上使用Unicode API(use bytes on POSIX)。

如果您不需要支持任意值,并且您确信当前区域设置/ sys.stdout.encoding / sys.getfilesystemencoding()可以解码所有值:

import os
import sys

# values are representable in `sys.getfilesystemencoding() in this case
def os_encode(unicode_string, encoding=sys.getfilesystemencoding()):
    return unicode_string.encode(encoding, 'strict')
def os_decode(bytestring, encoding=sys.getfilesystemencoding()):
    return bytestring.decode(encoding, 'strict')

# bytestrings mapping (all values are decodable so it is ok to use bytes)
_environ = getattr(os, 'environb', os.environ) 
def setenv(key, value):
    _environ[os_encode(key)] = os_encode(value)
def getenv(key):
    return os_decode(_environ[os_encode(key)])

然后你可以启用unicode_literals导入并在字符串文字中使用非ascii字符:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division, unicode_literals, absolute_import, print_function
import os

setenv('foo', 'bár')
print(getenv('foo'))