January Star
  • Home
  • Categories
  • Tags
  • Archives

Python小贴士(持续更新)

Contents

  • next
  • unicode
  • iter
  • memoryview
  • NotImplemented 与 NotImplementedError
    • NotImplemented
    • NotImplementedError
  • 真值判断
  • () 的用法
  • 1.bit_length() 为啥会报错?
  • 获取当前模块对象
  • startswith&&endswith

这里记录的是我用 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

NotImplemented

为一个内置的常量。

常用于 “ 富比较 ” 的特殊方法( __eq__ 、 __lt__ 等其它方法)的返回值,用于说明该方法未实现。

NotImplementedError

为一个内置异常,是从 RuntimeError 继承的。

多用于用户自定义的抽象方法中,用户可以在自定义的抽象方法中抛出该异常,要求继承该方法所在类的子类实现该方法。

真值判断

在 Python 中使用 bool 函数、if、while 等对某个对象进行真值判断时,有一定的算法。

  1. 以下情况则为 False
    • None
    • False
    • 所有为 0 的数值,比如:0, 0L, 0.0, 0j
    • 任何空的序列,比如: '', (), []
    • 任何空的映射,比如 {}
    • 用户定义的类的实例:如果该类定义了 ** __nonzero__ ** 或者 ** __len__ ** 方法,且返回值为数值 0 或者 False
  2. 其它所有情况都为 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
Comments
comments powered by Disqus

Published

Oct 3, 2014

Category

python

Tags

  • python 23
  • tip 1

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor