《流畅的python》 笔记

时间:2022-10-18 16:06:17

1.不成熟的抽象个过早的优化都会坏事.

2.熟悉doctest是什么,阅读官方文档  https://docs.python.org/3/library/doctest.html). 

3.列表生成式的for嵌套,注意顺序.

# 打印列表中所有字母
[letter for word in ["spades", "diamonds", "clubs", "hearts"] for letter in word] 

4.实例属性和类属性,当我通过类名访问类属性并修改它的时候,它在所有这个类的实例中都会变化,

而通过实例名访问到类属性并修改时类属性时,其实并没有修改,而是在实例的这个作用域下新建了一个同名变量,之后就无法访问到这个类属性了. 

5.dunder写法的函数是用来给python解释器调用的而我们自己代码中,不要调用这种方法,也就是说没有my_object.__len__()这种写法,应使用len(my_object)

6.python中没有&&和||运算符,取而代之的是and和or

7.当运算符的左侧为python内建类型时,右侧为自定义类型进行算术运算时会出现TypeError错误,因为无法修改内建类型的代码,此时需要使用反向运算符的重载

8.使用dis模块可以查看python的字节码.

9.sys.argv是一个列表,用来获取文件的路径和在命令行中执行文件的参数,sys.argv[0]表示代码本身文件路径,后面的则是运行时的参数

《流畅的python》 笔记

 10.__name__属性返回的是这个变量最终所引用的变量名或函数名.

《流畅的python》 笔记

11.(p44)大小端模式,低地址存放低位是小端模式(同,大端是异)

12.(p118)在python中,"一等对象"指的是满足下述条件的程序实体:

(1)在运行时创建

(2)能赋值给变量或数据结构中的元素

(3)能作为参数传给函数

(4)能作为函数的返回结果

python中函数是一等对象.

13.dict.items()返回一个列表其中每个元素都是二元元组由dict的键值对组成(key,word).

14.(p48)原子操作(atomic operation)是不需要synchronized的,所谓原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch(上下文切换,有时也称做进程切换或任务切换,是指CPU从一个进程或线程切换到另一个进程或线程)

15.列表本身是对象,是有地址的,而列表中存放的是元素的地址,想象一下列表中套列表,改变了内层列表中的元素,但是这样并不会改变内层列表本身的地址.

16.函数形参中的*args ,这里的*代表装包,会将你调用这个函数时的没有显式指定名字的实参打包成一个元组传进函数,在函数中再使用*args的时候,表示解包.

如果你的实参直接是一个元组的话,函数会认为你这是一个参数,传到函数中时还要在打包一遍,这时候就是元组中有一个元组类型的变量了,所以实参是元组的时候要先考虑是不是要先解包.

17.不可变类型才能被散列(hash),前提是不可变类型的容器中不能包含可变类型).把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。

18.enumerate传入一个可迭代对象,默认返回从0开始编好号的生成器,产出二元组.(你也可以指定开始的编号)

《流畅的python》 笔记

19.对字典迭代,得到的是键值(key)

20.哈希表(散列表):给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

21.dis.dis反汇编函数,将一句代码以汇编形式打印出来  (这不是反汇编而是生成python字节码)

22.字面量语法与构造方法: 比如说我要创建一个集合a,字面量语法是a={1,2,3},而构造方法是a=set([1,2,3])

构造方法是调用函数创建,字面量语法是把一个字面量赋值给变量.

23.chr是python关键字,chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。

24.集合的对称差集A△B={x|x∈A∪B,x∉A∩B}

25.(p75)使用dict,set查找元素的时间复杂度都是O(1),而列表是O(n),这是由于散列表的原因.

26.(p76)加盐,是在对hash的值上做出一定修改再hash的算法.这个修改就叫做盐(salt),可以是随机值.

27.(p80)不要对字典同时进行迭代和修改(修改字典时会改变散列值,而散列值决定了迭代的顺序)

28.往字典/集合中添加新元素时可能会改变已有键的顺序(p79) -> 综合27,28可散列对象不能同时进行迭代和修改

29.JSON的语法是python的句法的子集,JSON和Python是完全兼容的

30.python3中的str类型使用的是unicode码,所以str能被编码,能使用encoding方法.

31.空格在ascii表中是\x20,而空字符是\x00,退格符\x08.

32.小端模式:高位保存在高字节地址中比如0xfeff其中0xfe是高位保存到地址中是0xff 0xfe

大端模式相反,windows系统默认小端

33.(p95)如果一个代码需要在多种场合,多种操作系统下运行的需要明确指明源码的编码方式,

34.(p97)eval是python中的一个关键字.

eval()函数:官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。(这是解释型语言的特性,运行时编译)

比如eval("[1,2,3]")返回一个列表,相当于将这个str转换成一个代码执行.

35.(p127)内省:在计算机科学中,内省指一种能力,可以确定对象是什么,包含何种信息,可以做什么.使用help或者dir能查看函数的信息.

36.dict.keys返回字典的键,dict.values返回字典的值,dict.items返回字典的键值.

37.(p133)functools.reduce函数,第一个参数是函数,需要2输入1输出,第二个参数是可迭代对象,reduce把可迭代对象中的值连续放进函数中进行计算最终得到一个结果.

38.Python的内置数据类型不支持多重索引如a[1,2]是会报错的

39.Python不允许程序员选择采用传值还是传引用。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能 直接修改原始对象——相当于通过“传值”来传递对象。

40.(p206)每门面向对象的语言至少都有一种获取对象的字符串表示形式的标准方式.__repr__返回一个可以用来表示对象的可打印字符串(返回值是str类型,__str__同理):对一个对象使用print则调用__str__,当__str__,没有实现时调用__repr__.如果一个对象没有实现任何一种则生成默认形式(用尖括号包住的字符串,包含类型名和额外的信息,比如地址)

41.UML类图 #TODO

42.dis模块中LOAD_FAST是加载局部变量的意思,LOAD_GLOBAL是加载全局变量,LOAD_CONST加载常量

43.(p108)转义字符b"\uxxxx"指的是unicode转义字符,4个字节16位,使用decode('unicode_escape')可将其显示.

44.(p94)unicode三明治:尽早把输入(比如读取文件时)的字节序列解码成字符串,在我们的程序中尽可能对字符串操作.

45.(p184)变量与对象:对象是内存中的一种实体,而变量是这个对象的"标签".

对象在赋值等式右边获取,在此之后左边的变量才会绑定到对象上

变量只不过是对象的标注!所以你可以为这个对象贴上多个"标签".

46.str,bytes,array.array等单一类型(里面元素的类型相同)中保存的不是对象的引用而是只在在连续的内存空间的数据本身(跟C语言的数组相似).

47.Python唯一支持的参数传递模式是共享传参,指函数的各个形参获得的是实参的引用.这个结果是,函数能在函数内部通过修改形参从而修改是可变类型的实参.

(通过引用可以修改可变类型,而不可变类型不能,修改可变类型的后果是,新建内存存放修改后的值,而原值不受影响).

48.不要用可变对象作为函数参数的默认值,因为在加载模块时,函数的默认参数会变成函数对象的属性,如果你修改了这个属性,后续调用函数都会受到影响,

如果要接受可变值,默认参数请设置为None.

49.del只是删除对象的这个引用,当某个对象没有引用的时候对象才会销毁.

50.(p198)for循环中的临时变量的作用域是for语句所在的作用域,临时变量除非显式修改/删除会一直存在.

51.当你想比较字符串或者整数是否相等时请使用==而不是is.使用is可能会因为驻留机制而出现意外错误(驻留P200)

52.Python3中str就是unicode类型字符串.

53.chr与ord功能相反,chr(i)返回整数i对应的ASCII字符。ord(i)返回字符i对应的ascii码

54.@classmethod修饰的方法用来调用类属性/类中的方法

55.所谓函数的重载是指多个函数的名称以及返回值类型均相同,仅参数类型或参数个数不同。函数重载大大提高了代码重用率和程序员开发效率。
但是python,不支持以上形式的函数重载。(不支持类似于c++的函数重载)

56.(p171)python函数重载使用functools.singledispatch装饰器,当实参参数类型不同时,调用不同的函数.

57.静态作用域与动态作用域

58.(p335)抽象方法(纯虚方法)使用@abstractmethod只有声明,而没有具体实现的方法,抽象方法一般存在于抽象类或接口中。

59.any()和all()判断可迭代对象中是否有空,None,False,数值0(字符'0'不算),这种元素存在.(参数必须是可迭代的不然报错)

60.hashable对象才能放进set中,或者当做字典的键.

61.(p214)在类中请不要使用两个前导下划线定义私有属性,因为python本来就没有真正的私有,这样做只会带来麻烦.请使用单下划线象征性说明这个属性请不要再外部访问.

62.(p339)迭代器是一定要同时实现__iter__和__next__方法的,迭代器的__iter__返回自身.(可迭代对象没有__next__方法.)

63.(p339)可迭代对象一定不能是自身的迭代器,也就是说可迭代对象一定不能实现__next__方法.因为可迭代对象通过__iter__方法返回一个迭代器,如果可迭代对象使用了__next__之后,再创建的迭代器就不能完全遍历这个可迭代对象了.

可迭代对象每次调用__iter__都会创建一个独立的迭代器.

64.只要函数的定义体中存在yield关键字,这个函数就是生成器函数.每次调用生成器函数时都会返回一个新的生成器对象.

65.__set/getitem__与__set/getattr__魔法方法的区别是一个使用索引(item)的方法取/赋值,一个是用成员运算符.(attr)取/赋值.

66.re.finditer是惰性的(返回迭代器)只在需要的时候返回结果,相比于re.findall(返回列表)能节省大量内存.

67.(p345)对生成器使用生成器表达式(func(each) for each in gen)以减少计算量和内存占用(生成器表达一般可以替换掉生成器函数,不过生成器函数灵活的多)

68.iloc,islice中,i是index的意思(使用索引定位,绝对位置)

69.(p358)归约函数reduce,这种函数的参数是一个可迭代对象返回单个值.(tensorflow中的reduce_mean)

70.属性和方法在Python中被统称为属性(attributes), 方法是一种可被调用的属性。除了属性和方法,也可以建立特性properties,他可以和存取方法(即getter、setter)一起使用来取代属性,这不会改变类的接口,这符合统一存取原则(uniform access principle):

一个模块所提供的所有服务应该是通过一个统一的符号被使用, 其不会泄漏出它们是通过存储还是经过计算而实现的。

71.调用next()时,迭代器不能修改从数据源中读取的值,只能原封不动的产出值

72.标准库contextlib中的@contextmanager 装饰器能把包含一个yield语句的生成器编程上下文管理器,比直接定义一个至少包含两个方法__enter__,__exit__的类要简便的多

73.在生成器函数中yield关键字所在的语句,a = yield b,其中a是调用生成器中的一方使用.send()方法向生成器传递值,而b是生成器向调用方产出的值.

yield右边的值就像函数返回值,左边的就像函数传递进来的参数

74.(p383)使用协程必须预激,生成器协程可以通过next()或者send(None)激活.

75.generator.throw(exc_type)可以使在yield处暂停的生成器抛出指定的异常,如果生成器处理了这个异常,就会在下个yield处返回yield的值(正常工作).

76.异常处理时,应该仅仅把可能出现异常的代码放在try中,其余的放进else中.(这样做的代码比较规范)

77.yield from表达式的值是子生成器终止时传给StopIteration异常的第一个参数.(生成器在使用return语句后会产生StopIteration异常,参数是return返回的值,yield form会捕获并处理这个异常,并将return的值产出)

78.Python中所有的异常都是从BaseException这个基类派生的.

79.(p418)在io密集型应用中,如果代码写的正确,那么不管用哪种并发策略,吞吐量都会比顺序执行代码高很多.

80.(p424)GIL,线程在阻塞时会放弃GIL(比如等待网络响应时,等待操作系统返回结果时).

81.并发和并行:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生;

并发是在一台处理器上“同时”处理多个任务,并行在多台处理器上同时处理多个任务.

并行是在不同实体上的多个事件,并发是在同一实体上的多个事件.

82.print函数实际上是调用了sys.stdout.write()函数

sys.stdout.write('hello' + '\n') 和print('hello')是等价的.(Python的print会自动换行)

83.当我们打印一些字符时,并不是调用print函数后就立即打印的。一般会先将字符送到缓冲区,然后再打印。这就存在一个问题,如果你想等时间间隔的打印一些字符(在for循环中使用print,一般不会执行每个循环就会立即打印出来的),但由于缓冲区没满,不会立即打印。就需要采取一些手段。如每次打印后强行刷新缓冲区。

使用sys.stdout.flush()强制刷新缓冲区,在write后调用flush能立即打印

84.(p445)显示文本式动画的秘诀....使用退格符'\x08'把光标回退.(惊)

85.在全局作用于下的for语句 比如 for each in iterable:

这个each相当于一个全局变量,不会被自动释放,运行完这句语句后,each始终保留着iterable中的一个元素的引用.这留下了某种隐患.

而for循环在函数中的话,函数退出时会出栈.自动释放局部变量.

86.zip函数可以让多个可迭代对象并行迭代. zip(iter1,iter2,iter3.~~~)返回一个产出多元组的生成器.实现并行迭代.

87.抽象基类继承于abc.ABC,如果有抽象方法必须使用@abc.abstractmethod装饰器,而且在子类中必须实现,不然子类实例化时会异常

88.(p291)不要子例化(继承)内置类型,因为内置类型的子类覆盖的方法不会被隐式调用.用户自己定义的类应该继承collections模块.

89.(p293)超类中的方法可以显式传入self调用C.foo(d).D继承C,d是D的实例

90.即提供接口又提供实现的叫Mixin类(实现继承),不提供实现的叫抽象类(规格继承),只继承不提供接口和实现的叫聚合类.

由于混入类只提供了实现,所以他不能用作某个类的唯一超类.

91.内置类型是不需要任何包/头文件导入就能用的数据类型.python中的int,list,tuple等等,c++中int char float之类

92.argc 是 argument count,argv是 argument vector

93.由尖括号括起来的标准输出代表一个对象.