Python 拾遗录
这篇文章记录使用 Python 碰到的一些有意思的东西. 时时更新.
变量的值交换
Python 里的变量值交换很简洁, 比如我要交换变量 a
和 b
的值, 可以直接写
a, b = b, a
需要注意, 这里的交换顺序显然不是
a = b
b = a
而是用元组给 a
和 b
赋值
tmp = (b, a)
a = tmp[0]
b = tmp[1]
另外, 最近和朋友聊天, 谈到位运算, 说了说用异或实现不借助中间变量的变量值交换. 过程是这样的
a = a ^ b
b = a ^ b # 此时 b = a ^ b ^ b = a ^ (b ^ b) = a ^ 0 = a
a = a ^ b # 此时 a = a ^ b ^ a = b ^ (a ^ a) = b ^ 0 = b
异或:
- 相同为 0, 不同为 1
- 可以交换
- 可以结合
- 任何值异或 0 不变.
好像没有太多实用的地方? 但挺好玩的.
生成器
以前我们学过可以用带有递推性质的函数来构建一个生成器, 让我们的数据随要随算, 避免存储大量的数据. 简单来说, 它的实现是把函数语句中 “return
递推出来的结果” 改成 “yield
递推出来的结果”. 然后用这个 “函数” 构造一个实例, 每一次可以用 next()
得到该实例的下一个值, 不过一般可以直接用 for
访问. 例如
def odd():
n = 1
while True:
yield n # 每一次调用, 会从上一次的 yield 继续
n += 2
return None # 用了 while True, 是不会走到这一步的
oddNums = odd() # 得到一个生成器
for _ in range(5):
print(next(oddNums))
生成器是迭代器 (Iterator) 的一种, 迭代器就是可以用 next
不断得到下一个值的这种对象.
map
我们经常用 map
把一个函数作用到一个可迭代对象上, 得到的是一个迭代器. 基本为了实用, 常常要再加一句 list()
把它转成列表.
def f(x):
return x * x
iterFx = map(f, [x for x in range(5)]) # 这时的 iterFx 是个迭代器, 没法直接索引元素.
Fx = list(iterFx)
lambda 表达式
lambda 表达式在 Python 里经常出现, 例如
f = lambda x: x * x
print(f(4))
print(list(map(f, [i for i in range(1, 6)])))
就可以把它看成是一个简短的函数定义, 它的参数和定义普通函数时候是一致的. 使用体验上有些类似 MATLAB 里的句柄函数.
但我很少有必须要用到它的时候, 感觉似乎只是为了代码的简洁? 还是说它在某个方面有过优化, 比直接定义一个函数要好?
可以用 lambda 表达式简洁地给出一些感受上有一点点 “违背” 函数传入参数规则的函数定义, 比如
def testFun(n=4):
return lambda x: x + n
testFun()(3)
实际上是函数里套了个函数, 但用起来有种默认参数在前面的感觉. 当然, 这种事情完全可以直接通过真的在函数里面定义一个函数来实现. 所以我还是感觉这个 lambda 表达式只起到了缩短代码量的作用.
位运算判断奇偶
奇数的二进制最后一位是 1, 所以可以用与运算 a & 1
判断 a
是不是奇数, 会稍微快一些.
类和实例的属性方法动态定义
在 Python 里可以动态给类和实例绑定属性或方法, 很方便. 可以在定义类的时候用 __slots__
来限制实例可以绑定些什么属性.
双下划线开头的属性是私有的, 虽然实际上仍然可以访问, 就看使用者有没有守规矩了.
defaultdict
从 collections 里可以导入这个模块, 它是另一种字典, 可以指定默认的值的类型. 在未初始化某个键对应的值时, 会自动给这个键创建一个指定类型的值. 如
from collections import defaultdict
test = defaultdict(list)
test["omg"].append(1234)
print(test)
print(test["omg"])
结果为
defaultdict(<class 'list'>, {'omg': [1234]})
[1234]
和 lambda 表达式结合, 可以嵌套多个字典.
test = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
test["aaa"]["aa"]["a"].append(1234)