原作者:wuyou @newsmth

Python2 的编码问题一直非常让人恼火,不过你的问题稍微简单一些。 

在交互式命令模式(Interactive shell)下,可以这样来明白你遇到的问题的根源: 
>>> import sys 
>>> sys.stdout.encoding 
在 Windows 下,如果你的系统是简体中文,一般输出会是 cp936 
>>> import codecs 
>>> codecs.lookup('cp936').name 
'gbk' 
这就是在 Windows 终端里面使用的编码。在 print 的时候,无论字符串源采取什么样的编码,最终输出的字符串的编码必须跟 shell 的编码一致,也就是说: 
>>> print some_string 
Python 会做这样一个动作: 
codecs.encode(some_string, coding, errors='strict') 
在你遇到的情况下,some_string 是一个 unicode 字符串,coding 是 gbk。由于你的 unicode 字符串里面包含 gbk 字符集里面没有的字符,Python 就会抛出一个 UnicodeEncodeError. 
  
对于输出到文件中情况是类似的,Python2 的 open() 函数不会传入 encoding,以 'w' 方式打开文件,如果写入 unicode 字符串,Python 会获取当前默认的编码,然后以此种编码把字符串写入文件。可惜的是,“默认编码”只是 Python 自己默认的,在 Objects/unicodeobject.c 中,用一个全局变量 unicode_default_encoding 来表示默认的编码: 
/* Default encoding to use and assume when NULL is passed as encoding 
    parameter; it is initialized by _PyUnicode_Init(). 
  
    Always use the PyUnicode_SetDefaultEncoding() and 
    PyUnicode_GetDefaultEncoding() APIs to access this global. 
  
*/ 
static char unicode_default_encoding[100 + 1] = "ascii"; 
这个值在 Python 中有C接口(PyUnicode_SetDefaultEncoding)去改变,但可惜的是没有 Python 层的接口。 
所以在调用到类似 file.write(some_string) 的时候,首先会有这样的编码过程: 
codecs.encode(some_string, coding, errors='restrict') 
当你的 some_string 是一个 unicode 字符串,并且包含 ascii 字符串不存在的字符时,就会抛出一个 UnicodeEncodeError。 
  
所以,为了解决这个问题,可以这么处理: 
(1) 在 Windows shell 下面: 
     >>> print some_string.encode('gbk', errors='ignore') # replace 也可,只要不是 restrict 
(2) 在写入文件时,两种方法: 
    a. 忽略不存在的字符,同(1) 
    >>> f = open(filename, 'w') 
    >>> f.write(some_string.encode('gbk', errors='ignore') 
    b. byte 方式写入: 
    >>> f = open(filename, 'wb') 
    >>> f.write(some_string.encode('utf-8')) 
a 会损失字符,b 不会。 
  
另外,\uXXXX 的方式不能算错误, 
https://docs.python.org/2/reference/lexical_analysis.html#literals  
\uxxxx          Character with 16-bit hex value xxxx (Unicode only) 
\Uxxxxxxxx      Character with 32-bit hex value xxxxxxxx (Unicode only) 
  
另外,欢迎拥抱 Python 3,在 open() 时可以直接传入 encoding,彻底解决你的后顾之忧。