如果我有一个像这样的对象:
d = {'a':1, 'en': 'hello'}
Run Code Online (Sandbox Code Playgroud)
...然后我可以把它传递给urllib.urlencode,没问题:
percent_escaped = urlencode(d)
print percent_escaped
Run Code Online (Sandbox Code Playgroud)
但是如果我尝试传递一个值为type的对象unicode,游戏结束:
d2 = {'a':1, 'en': 'hello', 'pt': u'olá'}
percent_escaped = urlencode(d2)
print percent_escaped # This fails with a UnicodeEncodingError
Run Code Online (Sandbox Code Playgroud)
所以我的问题是关于准备传递给对象的可靠方法urlencode.
我想出了这个函数,我只是遍历对象并编码string或unicode类型的值:
def encode_object(object):
for k,v in object.items():
if type(v) in (str, unicode):
object[k] = v.encode('utf-8')
return object
Run Code Online (Sandbox Code Playgroud)
这似乎有效:
d2 = {'a':1, 'en': 'hello', 'pt': u'olá'}
percent_escaped = urlencode(encode_object(d2))
print percent_escaped
Run Code Online (Sandbox Code Playgroud)
那个输出a=1&en=hello&pt=%C3%B3la,准备好传递给POST电话或其他什么.
但我的encode_object功能对我来说真的很不稳定.首先,它不处理嵌套对象.
另一方面,如果声明,我会很紧张.我还应该考虑其他任何类型吗?
并且正在将这些type()东西与本地对象进行比较,就像这个好习惯一样?
type(v) in (str, unicode) # not so sure about this...
Run Code Online (Sandbox Code Playgroud)
谢谢!
Joh*_*hin 66
你应该感到紧张.你可能在某些数据结构中混合使用字节和文本的想法令人恐惧.它违反了使用字符串数据的基本原则:在输入时解码,专门在unicode中工作,在输出时编码.
更新以回应评论:
您即将输出某种HTTP请求.这需要准备为字节字符串.如果在你的dict中有ordinal> = 128的unicode字符,urllib.urlencode无法正确准备该字节串这一事实确实是不幸的.如果你的dict中混合了字节字符串和unicode字符串,你需要小心.我们来看看urlencode()的作用:
>>> import urllib
>>> tests = ['\x80', '\xe2\x82\xac', 1, '1', u'1', u'\x80', u'\u20ac']
>>> for test in tests:
... print repr(test), repr(urllib.urlencode({'a':test}))
...
'\x80' 'a=%80'
'\xe2\x82\xac' 'a=%E2%82%AC'
1 'a=1'
'1' 'a=1'
u'1' 'a=1'
u'\x80'
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "C:\python27\lib\urllib.py", line 1282, in urlencode
v = quote_plus(str(v))
UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
Run Code Online (Sandbox Code Playgroud)
最后两个测试演示了urlencode()的问题.现在让我们来看看str测试.
如果你坚持使用混合物,那么你至少应该确保str对象以UTF-8编码.
'\ x80'是可疑的 - 它不是any_valid_unicode_string.encode('utf8')的结果.
'\ xe2\x82\xac'没关系; 这是u'\ u20ac'.encode('utf8')的结果.
'1'没问题 - 所有ASCII字符在输入到urlencode()时都是正常的,如果需要,它将进行百分比编码,例如'%'.
这是建议的转换器功能.它不会改变输入字典以及返回它(就像你的那样); 它返回一个新的字典.如果值是str对象但不是有效的UTF-8字符串,则会强制执行异常.顺便说一句,你的这件事不处理嵌套对象关心的是有点误导 - 你的代码只能与类型的字典和嵌套类型的字典的概念,并没有真正飞起来.
def encoded_dict(in_dict):
out_dict = {}
for k, v in in_dict.iteritems():
if isinstance(v, unicode):
v = v.encode('utf8')
elif isinstance(v, str):
# Must be encoded in UTF-8
v.decode('utf8')
out_dict[k] = v
return out_dict
Run Code Online (Sandbox Code Playgroud)
这是输出,以相反的顺序使用相同的测试(因为这次讨厌的一个在前面):
>>> for test in tests[::-1]:
... print repr(test), repr(urllib.urlencode(encoded_dict({'a':test})))
...
u'\u20ac' 'a=%E2%82%AC'
u'\x80' 'a=%C2%80'
u'1' 'a=1'
'1' 'a=1'
1 'a=1'
'\xe2\x82\xac' 'a=%E2%82%AC'
'\x80'
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 8, in encoded_dict
File "C:\python27\lib\encodings\utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 0: invalid start byte
>>>
Run Code Online (Sandbox Code Playgroud)
这有帮助吗?
小智 10
我和德国的"Umlaute"有同样的问题.解决方案非常简单:
在Python 3+中,urlencode允许指定编码:
from urllib import urlencode
args = {}
args = {'a':1, 'en': 'hello', 'pt': u'olá'}
urlencode(args, 'utf-8')
>>> 'a=1&en=hello&pt=ol%3F'
Run Code Online (Sandbox Code Playgroud)
看起来这是一个比它看起来更广泛的主题,特别是当你必须处理更复杂的字典值时.我找到了3种解决问题的方法:
修补urllib.py以包含编码参数:
def urlencode(query, doseq=0, encoding='ascii'):
Run Code Online (Sandbox Code Playgroud)
并将所有str(v)转换替换为类似的内容v.encode(encoding)
显然不好,因为它几乎不可再分配,甚至更难维护.
描述更改默认的Python编码这里.博客的作者非常清楚地描述了这个解决方案的一些问题,谁知道它们中有多少可能隐藏在阴影中.所以它对我来说也不好看.
所以我个人最终得到了这种可憎的结果,它将所有unicode字符串编码为任何(合理)复杂结构中的UTF-8字节字符串:
def encode_obj(in_obj):
def encode_list(in_list):
out_list = []
for el in in_list:
out_list.append(encode_obj(el))
return out_list
def encode_dict(in_dict):
out_dict = {}
for k, v in in_dict.iteritems():
out_dict[k] = encode_obj(v)
return out_dict
if isinstance(in_obj, unicode):
return in_obj.encode('utf-8')
elif isinstance(in_obj, list):
return encode_list(in_obj)
elif isinstance(in_obj, tuple):
return tuple(encode_list(in_obj))
elif isinstance(in_obj, dict):
return encode_dict(in_obj)
return in_obj
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它: urllib.urlencode(encode_obj(complex_dictionary))
对键进行编码也out_dict[k]可以替换out_dict[k.encode('utf-8')],但对我来说有点太多了.
您似乎无法将Unicode对象传递给urlencode,因此,在调用它之前,您应该对每个unicode对象参数进行编码.在我看来,如何以正确的方式执行此操作非常依赖于上下文,但在您的代码中,您应始终了解何时使用unicode python对象(unicode表示)以及何时使用编码对象(bytestring).
此外,编码str值是"多余的":编码/解码之间有什么区别?
| 归档时间: |
|
| 查看次数: |
38228 次 |
| 最近记录: |