Python Tricks

Table of Contents

变量交换

python 中交换两个变量不需要引入临时变量

a = 1
b = 2
a, b = b, a
>>> a
2
>>> b
1

由此自然想到三个变量的交换 答案是也可以。

a = 1
b = 2
c = 3
a, b, c = c, a, b
>>> a
3
>>> b
1
>>> c
2

元素遍历

普通的遍历,类似于其他语言中的 for each 或者是基于范围的 for 循环

for i in range(5):
    print(i)

带索引的容器遍历,使用 enumerate

colors = ['red', 'green', 'blue', 'yellow']
for index, color in enumerate(colors):
    print('%d, %s' %(index, color))

事实上,enumerate 还接受第二个参数,用于指定 index 的起始位置,默认为 0

>>> lst = ["a", "b", "c"]
>>> list(enumerate(lst, 1))
[(1, 'a'), (2, 'b'), (3, 'c')]

字符串连接

names = ['raymond', 'rachel', 'matthew', 'roger',
         'betty', 'melissa', 'judith', 'charlie']
print (', '.join(names))

实际上,经常使用的 字符串连接方法有三种,join format 和 + 操作符 在需要连接的字符串个数较少的情况下,+ 的效率有些许优势,随着个数的增加,join 的优势会迅速显现

上下文管理器

with open('data.txt') as f:
    data = f.read()

使用生成式

最常见的是列表推导式

[2*i for i in range(10)]

其次还有:集合推导式和字典推导式,原先在 python2 版本中的 set 是需要通过 set 函数来显示转换的, 但后来 python3 中直接进行 set 推导的语法也被移植到了 python2 中。不过要注意的是 () 定义的不是元组生成式而是生成器

numbers = [1, 2, 3]
my_dict = {number: number * 2 for number in numbers if number > 1}
print(my_dict)

推导式可以嵌套,而且可以加入一定的条件机制,各个条件之间的关系是 与 。

results = [(x, y)
           for x in range(10)
           for y in range(10)
           if x + y == 5
           if x > y]
>>> results
[(3, 2), (4, 1), (5, 0)]

甚至可以利用列表生成式来实现快速排序

def quicksort(lst):
    if len(lst) == 0:
        return []
    else:
        return quicksort([x for x in lst[1:] if x < lst[0]]) + [lst[0]] + \
               quicksort([x for x in lst[1:] if x >= lst[0]])

非常优美

使用装饰器

import urllib.request as urllib

def cache(func):
    saved = {}

    def wrapper(url):
        if url in saved:
            return saved[url]
        else:
            page = func(url)
            saved[url] = page
            return page

    return wrapper

@cache
def web_lookup(url):
    return urllib.urlopen(url).read()

使用 collections 库

deque

from collections import deque
names = deque(['raymond', 'rachel', 'matthew', 'roger',
               'betty', 'melissa', 'judith', 'charlie'])
names.popleft()
names.appendleft('mark')

deque 还具有 maxlen 关键字参数,可以限制其最大容量

letters = deque(['a', 'b', 'c', 'd', 'e'], maxlen = 5)

>>> letters
>>> deque(['a', 'b', 'c', 'd', 'e'])
>>> letters.append('f')
>>> deque(['b', 'c', 'd', 'e', 'f'])

序列解包

p = 'vttalk', 'female', 30, 'python@qq.com'
name, gender, age, email = p

*另外可以用 name 来接收不需要的一大段值

name, *middle, email = p

>>> name
>>> 'vttalk'
>>> middle
>>> ['female', 30]
>>> email
>>> 'python@qq.com'

遍历字典 key 和 value

d = {'a':1, 'b':2, 'c':3}
for k, v in d.items(): # python2 有 iteritems 方法,但在 python3 中已经被移除
    print (k, '--->', v)

链式比较操作

age = 59
if 18 < age < 60:
    print('yong man')

>>> False == False == True
# 相当于 (False == False) == (False == True)
>>> False

if else 三目运算

text = '男' if gender == 'male' else '女'

真值判断

很多情况下 不需要使用 == 和 != 去比较真假

if values:
    do_something()

真假值对照表

类型 | False | True :--: | :---: | :---: bool | False | True str | '' | 非空str 数值 | 0,0.0 | 非0数值 容器 | [],(),{},set() | 非空容器 None | None | 非None对象

for else 语句

mylist = [0, 1, 2, 3]
for i in mylist:
    if i == 2:
        break # 跳出整个 for else 循环
    print(i)
else:
    print('oh yeah')

字符串格式化

python3 更提倡使用 format 格式化方法,虽然很难说这样做代码量更少,但 format 确实有很多 % 格式化方法所不具有的优点

s1 = "foofish.net"
s2 = "vttalk"
s3 = "welcome to {blog} and following {wechat}".format(blog=s1, wechat=s2)

切片

切片操作接受三个参数,分别是起始位置,结束位置(不包括)和步长

items = list(range(10))
# 第1到第4个元素的范围区间
sub_items = items[0:4]
# 奇数
odd_items = items[1::2]
#拷贝
copy_items = items[::]
#或者 
copy_items1 = items[:]

python 中常见的容器拷贝(深)就是通过切片来实现的,因为赋值操作本质上是标签(指针)的赋值

使用生成器

def fib(n):
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b

获取字典元素的 get 方法

当 key 不存在时即返回默认值

d = {'name': 'foo'}
d.get("name", "unknow")

预设字典返回的默认值

当 key 不存在时即返回默认值

# 第一种方式 setdefault
data = {('a', 1), ('b', 2), ('a', 3)}
groups = {}
for (key, value) in data:
    groups.setdefault(key, []).append(value) 

# 第二种方式 defaultdict 库
from collections import defaultdict
groups = defaultdict(list)
for (key, value) in data:
    groups[key].append(value)

函数参数 unpack

本质上就是容器/可迭代对象的解包

def foo(x, y):
    print(x, y)

alist = [1, 2]
adict = {'x': 1, 'y': 2}

foo(*alist)  # 1, 2
foo(**adict)  # 1, 2

注意函数默认参数

def foo(x=[]):
    x.append(1)
    print(x)

>>> foo()
[1]
>>> foo()
[1, 1]

更安全的做法

def foo(x=None):
    if x is None:
        x = []
    x.append(1)
    print(x)

>>> foo()
[1]
>>> foo()
[1, 1]

为 dict 添加missing方法

class Dict(dict):
    def __missing__(self, key):
        self[key] = []
        return self[key]
        
>>> dct = Dict()
>>> dct["foo"].append(1)
>>> dct["foo"].append(2)
>>> dct["foo"]
[1, 2]

很像 collections.defaultdict

from collections import defaultdict
dct = defaultdict(list)
>>> dct["foo"]
[]
>>> dct["bar"].append("Hello")
>>> dct
defaultdict(<type 'list'>, {'foo': [], 'bar': ['Hello']})

python 解释器中的 _

用于获取上一次的对象

>>> range(4)
[0, 1, 2, 3]
>>> _
[0, 1, 2, 3]

Zen

import this

try/except/else

try:
  put_4000000000_volts_through_it(parrot)
except Voom:
  print "'E's pining!"
else:
  print "This parrot is no more!"
finally:
  end_sketch()

print 重定向输出到文件

# python3 中移除
>>> print >> open("somefile", "w+"), "Hello World"

注意打开的模式: "w+" 而不能 "w" , 当然 "a" 是可以的

省略号

在 python3 中可以直接在交互环境中使用 ...

>>> ...
Ellipsis

pow 还有第三个参数

都知道 pow 是用来求幂的函数,但其实 pow 还接受第三个参数,用来求模 pow(x, y, z) == (x ** y) % z

>>> pow(4, 2, 2)
0
>>> pow(4, 2, 3)
1

isinstance可以接收一个元组

元组内的类型是或的关系

>>> isinstance(1, (float, int))
True
>>> isinstance(1.3, (float, int))
True
>>> isinstance("1.3", (float, int))
False
>>> isinstance("1.3", (float, int, str))
True

字典的无限递归

虽然没什么用,但很有意思

>>> a, b = {}, {}
>>> a['b'] = b
>>> b['a'] = a
>>> a
{'b': {'a': {...}}}
# 链表无限循环
>>> a, b =  [], []
>>> a.append(b)
>>> b.append(a)
>>> a
[[[...]]]

Python可以认识Unicode中的数字

不只是ASCII字符串

>>> int(u'1234')
1234

不能访问到的属性

>>> class O(object):pass
...
>>> o = O()
>>> setattr(o, "can't touch this", 123)
>>> o.can't touch this
  File "<stdin>", line 1
    o.can't touch this
                     ^
SyntaxError: EOL while scanning string literal

不过,能用 setattr 设置属性,自然就可以用 getattr 取出

使用计数器对象进行计数

这是 collections 库中的一个 dict 子类,专门用于解决计数问题, 子类还包括 most_common 等方法

from collections import Counter
c = Counter('hello world')

>>> c
Counter({'l': 3, 'o': 2, ' ': 1, 'e': 1, 'd': 1, 'h': 1, 'r': 1, 'w': 1})
>>> c.most_common(2)
[('l', 3), ('o', 2)]

collections 库中的其他方法,稍后进行总结 collections 总结

漂亮的打印 JSON

使用 indent 关键字参数即可偏亮的打印 JSON

>>> import json
>>> print(json.dumps(data))  # No indention
{"status": "OK", "count": 2, "results": [{"age": 27, "name": "Oz", "lactose_intolerant": true}, {"age": 29, "name": "Joe", "lactose_intolerant": false}]}
>>> print(json.dumps(data, indent=2))  # With indention
{
  "status": "OK",
  "count": 2,
  "results": [
    {
      "age": 27,
      "name": "Oz",
      "lactose_intolerant": true
    },
    {
      "age": 29,
      "name": "Joe",
      "lactose_intolerant": false
    }
  ]
}

字典的剧本

键的存在性

# 不推荐
dct.has_key(key)
# 推荐
kye in dct

无限循环的结束

# 不推荐
file = open("some_filename", "r")

while 1:   # infinite loop
    line = file.readline()
    if not line:  # 'readline()' returns None at end of file.
        break

    # Process the line.
    
file = open("some_filename", "r")

# 推荐
for line in file:
    # Process the line.

另外,open 函数的功能也可由 file 替代

序列乘法

比如

zeroes = [0] * 100
spaces = ' ' * 100

here starts my collection

或的另一种写法

# way 1
if i==a or i==b:
    pass
# way 2
if i in (a, b):
    pass