本篇文章主要总结一些 python 的小知识点
----- 来自 《流畅的 Python》 一书
- in 和 for i in 的机制
a = [1,2,3,4,5,6,7]
for i in a:
print i
# 这里 for in 的机制是判断 i = a[0], i = a[1], .......
if 5 in a:
print "5 in a"
# 这里的 in 机制是判断 5 == a[0], 5 == a[1], ......
- random 的 choice
from random import choice
a = [1,2,3,4,5,6,7]
choice(a)
# 随机返回序列中的一个值
# 注意参数必须是一个非空序列
-
__getitem__
方法会实现 [] 下标操作 -
__len__
实际上直接返回 PyVarObject 里的 ob_size 属性 - hypot
from math import hypot
# 返回 sqrt(x*x + y*y)
hypot(3,4) = 5
# 可以直接计算直角三角形斜边的长
-
__str__
和__repr__
的区别
__str__
是在 str() 函数中被使用,或者是在用 print 函数打印一个对象的时候才调用的,它返回的字符串对终端用户更友好;
str() 和 repr() 都是 python 的内置函数,用来格式化字符串的函数,而__str__`` __repr__
是在类(对象)中对类(对象)本身进行字符串处理。
如果定义了__repr__
,但没有定义__str__
,对象将表现为__str__
=__repr__
。
可以查看 stackoverflow 上的这个问题解答 stackoverflow,如果英文看不懂,可以看看翻译中文 - ord()
ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值. - 变量泄露(python 2.7)
x = 'abc'
b = [x for x in 'bdef']
print x
# f
# 但是在 python 3 中不会出现这个问题
# 列表推导,生成器等推导式在 python 3 中有了自己的局部作用域,类似函数。
- 元组功能
元组是一种很强大的可以当作记录来用的数据类型,体现在元组拆包特性上。第二角色则是充当一个不可变的列表。 - 列表生成列表
board = [ ['_'] * 3 for i in range(3)]
board
# [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
board[1][2] = 'X'
board
# [['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_'
上面的例子类似于:
board = []
for i in rage(3):
row = ['_'] * 3
board.append(row)
但是很容易写成这样:
board = [ ['_'] * 3] * 3
board
# [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
board[1][2] = '0'
board
# [['_', '_', '0'], ['_', '_', '0'], ['_', '_', '0']]
# 类似于
row = ['_'] * 3
board = []
for i in rage(3):
board.append(row)
# 在 board 中的 row 其实是同一对象的不同引用,改变其中任何一个就改变了所有
- 一个关于 += 的谜题
t = (1, 2, [30, 40])
t[2] += [50, 60]
会发生什么呢?
a. t 变成 (1, 2, [30, 40, 50, 60])。
b. 因为 tuple 不支持对它的元素赋值,所以会抛出 TypeError 异常。
c. 以上两个都不是。
d. a 和 b 都是对
如果你看到这里,停止往下拉,思考一下,然后去解释器中运行一下。
很不幸,结果不像我像的那样,我一开始选择了 a,因为 t[2] 是一个列表,而列表是可变的,但是我忽略了 t 本身是一个元组的缘故,元组具有不可变的特性。其实它只完成了 t[2] + [50, 60] 的操作,在最后赋值的时候抛出异常了,所以答案是 d。可以得到 3 个教训:
* 不要把可变对象放在元组里。
* 增量赋值不是一个原子操作。
* 可以查看 python 的字节码。(其实我没有看懂)
- random.seed()
seed() 方法改变随机数生成器的种子,可以在调用其他随机模块函数之前调用此函数。如果两次种子是相同的,那么生成的随机数是固定的。 - 为什么切片和区间会忽略最后一个元素
在切片和区间操作里不包含范围的最后一个元素是 Python 的风格,这个习惯符合 Python、C 和其他语言里以 0 作为起始下标的传统。 - 当只有最后一个位置信息时,可以快速看出切片和区间里有几个元素:range(3) 和 my_list[:3] 都返回 3 个元素。
- 当起始位置都可见时,可以快速计算切片和区间的长度,用后一个数减去第一个下标(stop - start)即可。
- 可以利用任意一个下标来把序列分割成不重叠的两部分,只要写成 my_list[:x] 和 my_list[x:] 就可以了。
>>> l = [10, 20, 30, 40, 50, 60]
>>> l[:2] # 在下标2的地方分割
[10, 20]
>>> l[2:]
[30, 40, 50, 60]
>>> l[:3] # 在下标3的地方分割
[10, 20, 30]
>>> l[3:]
[40, 50, 60]
另外可以用 s[a:b:c]
的形式对 s 在 a 和 b 之间以 c 为间隔取值。a:b:c 这种用法只能作为索引或者下标用在 [] 中来返回一个切片对象:slice(a, b, c)
。
对 seq[start:stop:step]
进行求值的时候,Python 会调用 seq.__getitem__(slice(start, stop, step))
。
- 代替列表
虽然列表既灵活又简单,但面对各类需求时,我们可能会有更好的选择。比如,要存放 1000 万个浮点数的话,数组(array)的效率要高得多,因为数组在背后存的并不是 float 对象,而是数字的机器翻译,也就是字节表述。这一点就跟 C 语言中的数组一样。再比如说,如果需要频繁对序列做先进先出的操作,deque(双端队列)的速度应该会更块。- 数组
若只包含数字的列表,那么 array.array 比 list 更高效。这个名为数组,跟 C 语言的数组一样精简。数组在 Python 3 中不支持 list.sort() 这种就地排序方法,需要用 sorted 函数。 - 内存视图
memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同一个数组的不同切片。
内存视图其实是泛化和去数学化的 NumPy 数组。它让你在不需要复制内容的前提下,在数据结构之间共享内存。其中数据结构可以是任何形式,比如 PIL图片、SQLite 数据库和 NumPy 的数组,等等。这个功能在处理大型数据集合的时候非常重要。-- Travis Oliphant ,NumPy 的主要作者。
- Numpy 和 Scipy
这两个库主要用于科学计算,而且需要自行安装这两个库。Numpy 底层是 C 语言写的,所以速度极快。
Pandas 也利用了这个库,可以好好尝试一下。- 队列
collections.deque 是一个线程安全,快速从两端添加或者删除元素的数据类型。注意双向队列只是优化了头尾的操作,从队列中间删除元素操作会慢一些。另外 queue 也实现了队列,它也是线程安全的。
- 队列
- 数组
- Python 中的排序
sorted 和 list.sort 背后的排序算法是 Timsort,它是一种自适应算法,会根据原始数据的顺序特点交替使用插入排序和归并排序,以达到最佳效率。这样的算法被证明是很有效的,因为来自真实世界的数据通常是有一定的顺序特点的。 - 上下文管理器
with 实现,依赖于__enter__
和__exit__
函数。
class Test():
def __enter__(self):
print('enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit')
def test(self):
pass
with Test() as t:
t.test()
可以基于类也可以基于 contextlib
模块
import contextlib
@contextlib.contextmanger
def file_open(name):
# do enter thing
yield {}
# do exit thing
- += extend
a = [1,2]
a += [3, 4] --> √
a += (3, 4) --> √
a = a + [3, 4] --> √
a = a + (3, 4) --> X
事实上 += 调用了 __iadd__ 方法,它里面调用了 extend 方法。这样
a.extend(range(3)) --> √
注意:append 会将原本的类型直接放入列表,extend 只会将元素放入列表
- 元编程
type
可以用来创建类
def test(self):
return self.name
User = type('user', (基类相关,), {'name': 'ad', 'test': test})
obj = User()
obj.name --->> ad
a = obj.test()
a --->> ad
# 元类来控制类的实例化过程
# 在类的实例化过程中,首先会去寻找 metaclass,
# 找到了就按元类去创造类,找不到就使用 type 去创建类的实例
class MetaClass(type):
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
class Te(metaclass=MetaClass):
def __init__(self, s):
self.s = s
def __str__(self):
return self.s
- _slots_
我们知道动态语言允许我们直接在运行过程中给类加上需要的功能,但是有时候这个非常难受,我们想限制 class 的属性怎么办,比如只允许对实例添加有关属性。这个也作为优化内存访问的一个手段,尽管本意不是如此。