笔记注释
我会用不同颜色的笔在notability上面做笔记,其中:
- 蓝色表示自己的理解,并不确定是完全正确的表达。
- 红色表示提醒,是完全没有问题的标注。
第四部分 面向对象的惯用法
8 对象引用,可变性和垃圾回收
- 变量不是盒子,对象在赋值之前就创建了,所以在对变量赋值的过程中,实际上就是在给变量贴标签,顺理成章的可以给一个对象贴很多个标签,引申出别名的概念。
- 元组具有相对不可变性,所以浅复制对于可变对象会同时改变原件和副本,但是对于元组(不可变对象),只会应用在改变的部分。理解浅复制副本共享内部对象的引用。
- 深复制需要用到
copy
库的copy()
和deepcopy()
这两个方法。 - python唯一支持的参数传递模式是共享传参,函数内部的形参是实参的别名,从而函数可能会修改接收到的任何可变对象。
- 默认的实参值(在括号里面参数)应该不可变,如果是可变的实参,会导致在实例化的时候该参数被共享,下面是一个简单的例子。
1 | class Bus: |
- 创建或者调用类,函数的时候,要思考调用方法是否会修改传入的参数。解决的办法一般是创建副本。
1 | a = [1 ,2 , 3, 4] |
- 第八章看完,对于其中的弱引用,动态内存管理的理解还是不够,希望以后会理解的更加明白。
9 符合python风格的对象
- Unicode编码,计算机保存字符串,保存的是二进制,所以对于字符串,需要进行编码和解码的工作,python默认支持UTF-8这种可变长度编码。Unicode不同的实现方式,比如UTF-16,UTF-8,UTF-32本质上就是编码的字节数不一样。为了独立的表示字符,编码应该是要唯一的。
- 迭代器,生成器,和可迭代对象。
- <generator object> 生成器:生成器不会一次存储所有的值,而是会在需要的时候一次生成一个值。有两种方法定义生成器:
- 生成器函数 当一个函数包含yield时,他是一个生成器,通过
next()
或者通过for
循环进行遍历查看值。 - 生成器表达式 例子
(i for i in range(1, 2, 3))
,和列表推导式很像。[i for i in range(1, 2, 3)]
- 生成器本身就是迭代器,对它调用iter()只是返回它自己。
- 生成器函数 当一个函数包含yield时,他是一个生成器,通过
- <iterable> 可迭代对象,生成器是可迭代对象;大多数Python内置类型(如list、tuple)的
__iter__
方法都返回新的迭代器对象,而不是自身。因为如果返回自身,那必须要实现一个迭代器。返回可迭代对象的优点是:- 符合Python惯例:可迭代对象和迭代器分离
- 避免状态管理:不需要跟踪迭代位置
- 支持多重迭代:可以同时进行多个独立的迭代
- 代码简洁:不需要实现复杂的
__next__(self)
方法
- <iterator> 迭代器 迭代器需要实现两个特殊方法(魔法方法)
__iter__(self)
和__next__(self)
。其中__iter__(self)
返回的是它本身。
- Python的format方法支持三种转换标志:
!s
- 调用str()
函数(默认行为),!r
- 调用repr()
函数,!a
- 调用ascii()
函数。!r
出现在类的__repr__
方法中,__repr__()
被调用需要是对构造方法的准确表达。(repr是为了让编程的人看懂的,这样会保留准确的引号,转义符号等。)目的是:
- 显示准确的数据类型:
!r
会保留字符串的引号,数字的确切表示等。 - 便于调试:
repr()
的输出通常是”无歧义的”,能清楚看出数据的真实形态。 - 代码重现性:理想情况下,
repr()
的输出应该是可以用来重新创建对象的有效Python
表达式。