Contents
这里记录的是我用 Python 几年以来,觉得大家可能会忽略的的小知识点(好吧,也有可能只是我自己忽略的小知识点)。
next
next 是 Python 提供的一个 Built-in 函数。
提到 next,大家可能都会想到 Python 中的迭代协议中就有要求实现一个 next 方法。
那这个函数的功能也是作用于一个迭代器的。
1 | next(iterator[, default])
|
看到它的函数签名了么,有一个 default 可选参数,这个就是我想要讲的重点。
next 在检索一个迭代器(不是一个可迭代的对象)时,会调用它的 next() 方法, 但当迭代器迭代结束时,如果提供了 default 参数,next 函数会返回 default, 如果没有,则直接抛出 StopIteration 异常。
Note
注意我上面所强调的 迭代器 和 可迭代对象 的区分。
它对应的 Python 代码实现如下(实际上以 C 语言实现):
1 2 3 4 5 6 7 8 9 | onlyone = Object()
def next(iterator, default=onlyone):
try:
return iterator.next()
except StopIteration:
if default != onlyone:
return default
else:
raise StopIteration
|
我想要的就是永远不要一个迭代器抛出 StopIteration 异常。
比如下面这种使用场景(来自 string 标准库源码学习):
1 2 3 4 5 6 7 8 9 10 11 | _ASCII_COUNT = 256
_IDMAP = [chr(n) for n in xrange(_ASCII_COUNT)]
def maketrans(fromstr, tostr):
if len(fromstr) != len(tostr):
raise ValueError, "maketrans arguments must have same length"
frm_to = zip(fromstr, tostr)
# 如果字符 C 在 fromstr 中,则替换为 tostr 中对应 index 的字符
# 否则返回字符 C 本身
sub_c = lambda c: next((t for f, t in frm_to if f == c), c)
return ''.join((sub_c(c) for c in _IDMAP))
|
我就是想要 (t for f, t in frm_to if f == c) 这个生成器(迭代器之一),在 f == c 时立即给我返回值,如果没有则直接返回 c 。
这种情况,正常的 for 格式代码很容易实现:
1 2 3 4 5 6 | def sub_c(c):
for f, t in frm_to:
if f == c:
return t
else:
return c
|
但有些人就是有点小强迫症(比如我):一行可实现的代码,干嘛写那么啰嗦。
unicode
嗯,这也是一个 Python 的内置函数。函数签名如下:
1 2 | unicode(object='')
unicode(object[, encoding[, errors]])
|
我想要讲还是 unicode 的可选参数 errors 。
errors 这个可选参数有三个值 strict 、 ignore 、 replace
当我们用 unicode 函数将一个对象转换成 unicode 字符串时,如果遇到无法解析的字符,unicode 就会根据 errors 的值来决定如何处理该异常。
strict
默认的行为,直接抛出 ValueError
ingore
忽略无法解析的字符,并继续解析
replace
用 U+FFFD 来代替该字符,并继续解析
Tip
一个字符串的 decode 方法和 encode 方法也支持 errors 参数。
iter
还是 Python 的内置函数,直接看它的签名。
1 2 | iter(collection) -> iterator
iter(callable, sentinel) -> iterator
|
第一种用法大家应该都了解的,这里我想说一下第二种用法。
iter 函数会不停地调用第一个参数 callable,直到该 callable 的返回值等于 sentinel(哨兵),它就会抛出 StopIteration 异常。
该用法很类似于 itertools.takewhile 。
memoryview
这个类是在 Python2.7 中才加入的。
这个类可以用来访问一个支持 buffer 协议的对象的内部数据,而不需要拷贝这些数据。可以用在对内存比较敏感的地方。
目前 Python 内置的支持 buffer 协议的有 str 和 bytearry。
比如:
1 2 3 4 5 6 | str_a = "abcdefg"
mem_a = memoryview(str_a)
tag = "bcdefg"
print str_a[1:] == tag # 第 1 种情况
print mem_a[1:] == tag # 第 2 种情况
|
第 1 种情况 Python 会使用切片操作生成一个字符串 "bcdefg" ,然后再跟 tag 进行比较。
第 2 种情况 Python 只会生成一个新的 memoryview 对象再然后再跟 tag 进行比较。该新的 memoryview 对象本质是对 str_a 的 bcdefg 段的引用,并不生成新的字符串。
NotImplemented 与 NotImplementedError
NotImplementedError
为一个内置异常,是从 RuntimeError 继承的。
多用于用户自定义的抽象方法中,用户可以在自定义的抽象方法中抛出该异常,要求继承该方法所在类的子类实现该方法。
真值判断
在 Python 中使用 bool 函数、if、while 等对某个对象进行真值判断时,有一定的算法。
- 以下情况则为 False
- None
- False
- 所有为 0 的数值,比如:0, 0L, 0.0, 0j
- 任何空的序列,比如: '', (), []
- 任何空的映射,比如 {}
- 用户定义的类的实例:如果该类定义了 ** __nonzero__ ** 或者 ** __len__ ** 方法,且返回值为数值 0 或者 False
- 其它所有情况都为 True
其实真值判断在 Python 入门时,大家都有遇到过,基本对于前 6 种会被判定为 False,大家也都清楚。
但是最后一种会被判断为 False,忽略的人可能会比较多。
() 的用法
在 Python 中,很多人还了解到元组的生成语法后,就会以为 () 是元组专享的。
其实在 Python 中, () 真实的意思应该是组合,一般情况下有它无它都可以。
比如:
1 2 3 4 5 6 7 8 | In [13]: ("abcd" "ABC")
Out[13]: 'abcdABC'
In [18]: (1)
Out[18]: 1
In [19]: ("adfi")
Out[19]: 'adfi'
|
1 2 3 4 5 6 7 8 | In [20]: "asdfi" "asdif"
Out[20]: 'asdfiasdif'
In [21]: 1
Out[21]: 1
In [23]: "adfi"
Out[23]: 'adfi'
|
如果在 () 的内部至少有一个 , 才会解释成元组,或者啥都没有,就是一个空元组。
大家想深入了解的话,可以看 Python 官方文档 Parenthesized forms 。
1.bit_length() 为啥会报错?
1 2 3 4 5 6 7 8 9 10 11 | In [25]: 1.bit_length()
File "<ipython-input-25-069f7d2f7d97>", line 1
1.bit_length()
^
SyntaxError: invalid syntax
In [27]: 1..is_integer()
Out[27]: True
In [28]: 1.0.is_integer()
Out[28]: True
|
Python 里面不是是一切皆对象么,为啥我大浮点数可以调用方法,整数就不可以,还报语法错?
其实这个由于 Python 的语法解析器导致的。
当它解释到 [number][dot] 这种形式时,它会将之当作一个浮点数,你一个浮点后面直接加方法名,中间没有 . 当然就直接报语法错了。
1..is_integer() 这样的话,它就不会报错了,因为 1. 它会解释成浮点数, .is_integer() 就是正常的方法调用了。
那么在 Python 中整数就没办法直接调用方法了么?
那倒不是,方法还挺多。
1 2 | In [29]: 1 .__add__(2) # 空格可以任意添加多个
Out[29]: 3
|
1 2 | In [30]: (1).__add__(2)
Out[30]: 3
|
再来个疯狂一点的:
1 2 | In [35]: 1 . __add__ ( 2 )
Out[35]: 3
|
StackOverFlow 上面有一个问题解释得比较详细: why-does-1-add-2-not-work-out 。
获取当前模块对象
获取当前模块的名称很容易。
1 2 | In [58]: __name__
Out[58]: '__main__'
|
但有时我们需要获取当前的模块对象,并且对它做一些有趣的事。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | In [59]: import sys
In [60]: current_module = sys.modules[__name__]
In [61]: test_var
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-61-2538835f11fb> in <module>()
----> 1 test_var
NameError: name 'test_var' is not defined
In [62]: current_module.test_var = "hack var"
In [63]: test_var
Out[63]: 'hack var'
|
startswith&&endswith
startswith 和 endswith 的用法相信大家都知道。
1 2 3 4 5 6 7 | In [1]: l = "abcdefg"
In [2]: l.startswith("ab")
Out[2]: True
In [3]: l.endswith("fg")
Out[3]: True
|
但是有时候想判断多个 startswith 或者 endswith ,我们一般会写成:
1 2 | In [4]: l.startswith("ab") or l.startswith("bc")
Out[4]: True
|
如果是三个或者三个以上的话,就要写个循环或者其相等体来判断。
1 2 | In [2]: any(l.startswith(key) for key in ("ab", "cd", "ef"))
Out[2]: True
|
那,有没有比较方便的判断方法呢?
咱们看一下 startswith 和 endswith 的函数说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | In [5]: l.startswith?
Type: builtin_function_or_method
String form: <built-in method startswith of str object at 0x8aec300>
Docstring:
S.startswith(prefix[, start[, end]]) -> bool
Return True if S starts with the specified prefix, False otherwise.
With optional start, test S beginning at that position.
With optional end, stop comparing S at that position.
prefix can also be a tuple of strings to try.
In [6]: l.endswith?
Type: builtin_function_or_method
String form: <built-in method endswith of str object at 0x8aec300>
Docstring:
S.endswith(suffix[, start[, end]]) -> bool
Return True if S ends with the specified suffix, False otherwise.
With optional start, test S beginning at that position.
With optional end, stop comparing S at that position.
suffix can also be a tuple of strings to try.
|
哈哈,看到最后一行,发现它们的参数还可以是一个元组。
prefix can also be a tuple of strings to try.
所以,想判断多个开头或者结尾的话,传入一个元组即可。
1 2 | In [7]: l.startswith(("ab", "bc", "cd"))
Out[7]: True
|