Contents
list.sort, dict.update 返回啥?
1 2 3 4 5 6 7 | def aa():
a = [2, 8, 3, 1, 6, 4]
return a.sort()
def bb():
b = {1: 2, 3: 4, 5: 6}
return b.update({7: 8})
|
好吧,虽说我用 Python 也不少年头了,但是最近还是犯上面例子中的错误。
虽然我知道他们会修改值本身的内容,而我却想当然以为它们会将修改后的值返回出来,但现实是很残酷的,它们都返回 None。
1 2 3 4 5 | In [10]: print [4, 2, 3].sort()
None
In [11]: print {"a": 1, "b": 2}.update({"c": 3})
None
|
tuple 的创建,不是少个逗号么
1 2 3 4 5 6 7 8 | In [36]: a = (); type(a)
Out[36]: tuple
In [37]: a = (1); type(a)
Out[37]: int
In [38]: a = (1,); type(a)
Out[38]: tuple
|
在 Python 中,括号的功能主要是来进行组合的,而不是用来创建元组的。比如:
1 2 | In [47]: a=("first;" "second;"); a
Out[47]: 'first;second;'
|
装饰过后了,我的 func_name 呢?
Python 中不仅可以使用自带的 @staticmethod 等来进行装饰,还可以自己写装饰器来进行装饰。
但是装饰完的函数还是原来的函数么?
1 2 3 4 5 6 7 8 9 10 11 | def wrapper(func):
def _wrapper(*args, **kwargs):
return func(*args, **kwargs)
return _wrapper
def aa():
return "aa"
@wrapper
def bb():
return "bb"
|
1 2 3 4 5 | In [11]: aa.func_name
Out[11]: 'aa'
In [12]: bb.func_name
Out[12]: '_wrapper'
|
经过装饰的函数,它的 func_name 已经发生了变化,说明它已经不是原来的函数定义了。
那么如何解决经过装饰的函数还拥有原来的一此属性呢?
使用 functools.wraps
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import functools
def wrapper(func):
@functools.wraps(func)
def _wrapper(*args, **kwargs):
return func(*args, **kwargs)
return _wrapper
def aa():
return "aa"
@wrapper
def bb():
return "bb"
|
1 2 3 4 5 | In [11]: aa.func_name
Out[11]: 'aa'
In [12]: bb.func_name
Out[12]: 'bb'
|
Tip
如果某个函数之间已经装饰过了,然后改变装饰器,则原来的函数仍然是使用改变之前的装饰器。
bool(gevent.spawn(lambda: True)): True? or False?
1 2 3 4 5 6 | def aa():
return "aa"
aa_spawn = None
aa_spawn = gevent.spawn(aa)
print bool(aa_spawn) == True
|
上面的代码最后结果是 True 么?
是
好,咱们接着看。
1 2 3 4 5 6 7 | def aa():
return "aa"
aa_spawn = None
aa_spawn = gevent.spawn(aa)
aa_spawn.join()
print bool(aa_spawn) == True
|
上面的代码最后结果是 True 么?
难道不是么,我 X,还真不是。
怎么回事啊?
这个就要看 bool 函数的内部判断逻辑了。
以下情况会被认为是 False,其他的情况是 True。
- None
- False
- zero of any numeric type, for example, 0, 0L, 0.0, 0j.
- any empty sequence, for example, '', (), [].
- any empty mapping, for example, {}.
- instances of user-defined classes, if the class defines a __nonzero__() or __len__() method, when that method returns the integer zero or bool value False.
这时候咱们再看一下 aa_spawn 这个对象。
1 2 3 4 5 6 7 8 9 10 In [25]: dir(aa_spawn) Out[25]: ['GreenletExit', '__class__', ... ... '__module__', '__new__', '__nonzero__', '__reduce__', ... ...]哦,我看到 __nonzero__ 这个属性了。当 gevent.greenlet 对象还没运行时,该属性为 True ,但当它执行完了,该属性就会变成 False 了。
SO,判断一个对象是否为真,千万要小心使用诸如以下形式的自动判断啊:
1 2 3 4 5 6 | if some_instance:
do_something()
some_instance and do_something()
some_instance or do_something()
|
自定义异常无法正常 Pickle 反序列化
我们先来定义一个自定义异常 MyError
1 2 3 4 5 6 7 8 | class MyError(Exception):
def __init__(self, desc):
super(MyError, self).__init__()
self.__desc = desc
def __str__(self):
return "MyError: {}".format(self.__desc)
|
接下来咱们序列化一下:
1 2 3 4 5 6 | In [10]: import pickle
In [11]: p = pickle.loads(e)
In [12]: p
Out[12]: "c__main__\nMyError\np0\n(tRp1\n(dp2\nS'_MyError__desc'\np3\nS'saifsdf'\np4\nsb."
|
貌似序列化成功了,接下咱们再反序列化一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | In [16]: pickle.loads(p)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-18-6aaee2b1d950> in <module>()
----> 1 pickle.loads(p)
/usr/lib/python2.7/pickle.pyc in loads(str)
1380 def loads(str):
1381 file = StringIO(str)
-> 1382 return Unpickler(file).load()
1383
1384 # Doctest
/usr/lib/python2.7/pickle.pyc in load(self)
856 while 1:
857 key = read(1)
--> 858 dispatch[key](self)
859 except _Stop, stopinst:
860 return stopinst.value
/usr/lib/python2.7/pickle.pyc in load_reduce(self)
1131 args = stack.pop()
1132 func = stack[-1]
-> 1133 value = func(*args)
1134 stack[-1] = value
1135 dispatch[REDUCE] = load_reduce
TypeError: __init__() takes exactly 2 arguments (1 given)
|
好家伙,出现这么信息,竟然异常了。
好吧,有问题,查看官方网站 pickle 文档 。
pickle 只能序列化和反序列化以下类型:
- None, True, and False
- integers, long integers, floating point numbers, complex numbers
- normal and Unicode strings
- tuples, lists, sets, and dictionaries containing only picklable objects
- functions defined at the top level of a module
- built-in functions defined at the top level of a module
- classes that are defined at the top level of a module
- instances of such classes whose __dict__ or the result of calling __getstate__() is picklable(see section The pickle protocol for details).
按照以上定义没有问题啊, MyError 符合其中第 7 条的要求。事实也是可以序列化,但不能反序列化。好吧,继续看官方文档。
Pickling and unpickling extension types object.__reduce__() When the Pickler encounters an object of a type it knows nothing about — such as an extension type — it looks in two places for a hint of how to pickle it. One alternative is for the object to implement a __reduce__() method. If provided, at pickling time __reduce__() will be called with no arguments, and it must return either a string or a tuple.
原来 pickle 对自定义异常 一无所知 。所以咱们需要在某个地方告诉它应该怎么序列化和反序列化。
1 2 3 4 5 6 7 8 9 10 11 | class MyError(Exception):
def __init__(self, desc):
super(MyError, self).__init__()
self.__desc = desc
def __str__(self):
return "MyError: {}".format(self.__desc)
def __reduce__(self):
return (MyError, (self.__desc,))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | In [10]: import pickle
In [20]: e = MyError("error")
In [21]: p = pickle.dumps(e)
In [24]: p
Out[24]: "c__main__\nMyError\np0\n(S'error'\np1\ntp2\nRp3\n."
In [28]: pickle.loads(p)
Out[28]: __main__.MyError()
In [29]: c = pickle.loads(p)
In [30]: str(c)
Out[30]: 'MyError: error'
|
json.loads(json.dumps({1: 2})) == {1: 2}?
1 2 3 4 5 6 | In [4]: import json
In [5]: a = {1: 2}
In [7]: json.loads(json.dumps(a)) == a
Out[7]: False
|
奇怪,序列化 + 反序列化前后不应该一样么?
好吧,我们看一下,JSON 序列化再反序列化的数据是什么样本?
1 2 | In [6]: json.loads(json.dumps(a))
Out[6]: {u'1': 2}
|
字典的 Key 值由原来的数值类型变成了字符串类型。
JSON 为什么会有这种奇怪的行为?
我们来看一下 JSON 中文网 的 JSON 格式说明。
原来 JSON 语法规定:一个键值对的集合(Python 叫字典),其键必须为 string(字符串)。(JSON 作为一种数据交换格式,可能需要考虑兼容性, 不是所有语言都支持键为数值类型)
好吧,这种情况我只能 Orz...
str.encode([encoding[, errors]]) 中的关键字参数
在 Python2.6 上面,它的执行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 | In [10]: s = u"abc"
In [11]: s.encode("utf-8", errors="ignore")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-11-a702e9e976da> in <module>()
----> 1 s.encode("utf-8", errors="ignore")
TypeError: encode() takes no keyword arguments
In [12]: s.encode("utf-8", "ignore")
Out[12]: 'abc'
|
在 Python2.7 上面,它的执行结果如下:
1 2 3 4 | In [9]: s = u"abc"
In [10]: s.encode("utf-8", errors="ignore")
Out[10]: 'abc'
|
Python2.7 才支持关键字参数。具体可参见 str.encode ,str.decode 也是一样的。
我在 Google 搜索了一下,看来被这个坑的,还不止我一个,哈哈。