旧笔记-python

之前学python的时候整理的一些东西,先丢这里

因为不怎么会用,所以笔记里的斜体、黑体字基本代表了两边有单、双下划线。

事实证明以前用这个方式记东西除了能偷懒,没任何用。甚至之后就没做过笔记。有些肯定要重新学的就不记录在这了。

基本知识和未分类的知识

  1. 在shell中,一般用python寻找实际的文件需要加.py后缀名,但import语句中不必如此。
  2. 关于语句和函数,函数后直接加()
    函数有reload(需从imp导入);print;dir
  3. dir 获得模块内部的可用的全部变量名的列表
  4. 不可变:数字、字符串、元组
    可变:列表、字典、集合
  5. ord() #输入字符到Unicode码
    chr() #输入Unicode码到字符
  6. 字节文本总是以“b”或“b”作为前缀;它们生成字节类型的实例,而不是str类型的实例。它们只能包含ASCII字符;数值为128或更大的字节必须用转义符表示。
  7. re,格式为··· = re.···(’···’,’···’),第一个引号内为删选的格式,作为分隔部分的用中括号表示,需要选出来的部分用小括号表示
    . 表示简单字符
    * 表示重复任意次数(包括0次)
    注:一个中括号只能表示一个字符,中括号内有多个字符时,这些字符成或的关系,删选顺序为从左往右
  8. 序列.append(···)增加序列大小,增加的括号内的项置于序列最后
    序列.pop(相对偏移量)移除序列中的某一项,此语句本身可赋值给另一对象,以保存移除的项
  9. while条件不能是==
  10. ‘……’.join([]),是用……把列表里的各项连在一起形成一个字符串。若用空字符串则可以实现列表合并为字符串。
  11. 字符串格式化调用中,’{标签:格式}……’.format() 注:注意冒号
  12. list[a:b]是前闭后开的,range(a,b)是前闭后开的
  13. ‘’.find()是字符串方法(即不能用在列表中)列表中可以用list.index(x)
  14. list[x:x] = [……]相当于指定位置的拓展,list[:0] = [……]常用作头部拼接
  15. dict.get()和dict.setdefault()的区别在于当字典中没有需获取的键时,setdefault在返回default?值的时候会将其保存在字典中,实现原位置增加key:value
  16. 字符串中的find(),列表和元组中的index()
    括号内为(字符串,开始查找处的偏移量,结束查找处的偏移量)
  17. eval(string) 能把字符串当作可执行代码
  18. open(‘file’,’…’)中,…处a为追加写入,w为覆盖写入,r为读取
  19. if x : 相当于 if x = 0: 或if x = ‘’: 等,意思为如果x包含内容(为True)
  20. 列表、字典(的value)、元组可以包含任何种类的对象;集合(和字典的key)可以包含任意的不可变类型对象
  21. 字典中一个key对应一个value,用dict(由(key,value)组成的列表)生成字典时,有重复key不会产生error,而是以从左向右的顺序迭代更新。
  22. 一个序列解包赋值总是返回一个包含多个匹配项的列表。a,*b,c = 'spam’中,得到b为[‘p’,‘a’];a,b,c,*d = [1,2,3,4]中得到d为[4];a,b,c,d,*e = [1,2,3,4]中得到e为[]
  23. 序列解包的边界情况,注意以下情况会产生错误:使用了多个带星号的名称;名称数目少于值序列长度,同时没有带星号的名称;带星号的名称自身没有被编写到一个列表中(即a = ……会报错,而a, = ……则不会报错)
  24. 共享引用的效果近当原位置处的修改才会体现出来
  25. 增量赋值属于原位置修改,当一个引用进行了非原位置修改后,就退出了共享引用
  26. 以下代码的效果相同(即能实现三元表达式的效果,其中第二个语句要求Y为真):A = Y if X else Z ; A = (X and Y) or Z ; A = [Z,Y][bool(X)]
  27. 函数any检验是否存在真值,函数all检验是否全部为真值
  28. 记L为一列表,一下两个语句具有相同效果 : list(filter(bool,L)) ; [x for x in L if x]
  29. for line in open(‘某文件’).readlines 和 for line in open(‘某文件’) 都可以按行读取文件
  30. break语句是退出当前的循环,不包括外面的大循环,continue同理(break是循环语句的内容,和if无关)
  31. zip函数当各个参数长度不等时,会以最短序列的长度为准来截断结果元组
  32. zip后面的参数可以是任何序列,也可以包含不同的序列
  33. xxx = dict.fromkeys(键序列,初始化的值),可以生成一个字典,包含第一个参数中所有的键,其值都为第二个参数
  34. enumerate函数参数为一个序列,返回一个生成器对象,返回由(相对偏移量,值)元组组成的列表
  35. next() 返回一次迭代并删除
  36. ‘分隔符号’.join(包含插入内容的列表) eg: ‘,’.join([‘a’,‘b’,‘c’]) 得到 ‘a,b,c’
  37. 注意:next()函数等价的是 .next() 方法
  38. sorted 函数可以对可迭代对象排序,并返回一个列表
  39. 多遍迭代器:如range、字典视图可迭代对象等,支持在其结果上的多个迭代器,且这些迭代器会记住它们各自的位置。
  40. 单遍迭代器:如zip、map、filter、reduce等,不支持同一结果上的多个迭代器(或者说这些迭代器无法记住各自的位置)
  41. os.popen对象支持 .next() 方法,但不支持 next() 函数
  42. 迭代上下文有:for循环、列表推导、列表索引赋值、列表分片赋值、列表extend方法、内置函数map、in成员关系测试表达式、内置函数sorted、sum、any、all、内置函数list、tuple、字符串join方法等
  43. 不带参数的 dir() 返回目前调用者作用域内的所有变量
  44. 用help()查看模块的文档字符串时,可以导入模块后把模块名直接作为参数,或用模块名的字符串作为参数。例:import email ; help(email.message) 或 help(‘email.message’) 或 from email import message ; help(message)
  45. help() 后加list或任意列表(或赋值为列表的变量)可返回list的帮助,其他类型同理,但要返回字符串的帮助,只能加空字符串(‘’)或str
  46. 注意:在python中要尽可能使用for循环
  47. 注意:所有在原位置修改对象的方法的返回值都是None,因此不要将此方法带入表达式中
  48. 注意:一定要使用括号来调用函数,例:file.close() 缓冲内容输出到磁盘并关闭文件。此外需注意不加 () 并不会引起错误,因为引用函数也是合法的
  49. 注意:不要在导入和重载中使用拓展名或路径
  50. 变量赋值默认是对当前级和内级的赋值(当前在最外侧时即默认为全局变量赋值)
  51. global起声明全局变量的作用,在定义函数中使用,可使当前级函数内对变量作出的改变对当前级和外级都生效(但再内级的变量赋值不是全局变量赋值,理由同50条)
  52. 原位置改变对象并不会把变量划分为局部变量,实际上只有对变量名赋值才可以
  53. 注意:需要清楚地区分变量名和对象,修改一个对象并不是对一个变量名赋值
  54. 总结:所有在函数def语句(或者lambda)内赋值的变量名默认均为局部变量。函数能够随意使用在外层函数内或全局作用域中的变量名,但是必须声明为非局部变量和全局变量来改变其属性
  55. LEGB规则仅对简单变量名有效,被验证的属性变量名(带点的)遵循一种完全不同的查找规律
  56. del可以移除变量
  57. 访问全局变量的方式有(以thismod.py的函数中为例):1. global var ; 2. import thismod ; thismod.var = ; 3. import sys ; glob = sys.modules[‘thismod’] ; glob.var = ; 注:模块是可以导入自身的!
  58. 通过嵌套函数以及return返回函数,可以实现生成函数工厂(闭包的功能实现)
  59. 保存外层作用域状态的方法:全局变量;类实例中的属性;外层作用域中的引用;参数默认值;函数属性;(可变参数的默认值)
  60. 利用参数默认值保存外层作用域的状态(在现在的循环变量中仍有用):嵌套函数使用def f2(x=x): ,arg=val语句表示参数arg在调用时没有参数传入进来的话,默认会使用值val,在上例中即表示参数x会默认使用外层作用域中x的值
  61. 注意:对于大部分代码而言需直接避免在def中嵌套def,因为可以用两个def+def中引用另一个函数代替
  62. lambda可以生成函数并作为表达式,格式是 lambda 变量 :返回结果的表达式
  63. 循环变量的情况,可能需要默认值参数,而不是作用域。当函数中定义的lambda或def嵌套在一个循环之中,而这个内嵌函数又引用了一个外层作用域的变量,该变量被循环所改变,在这种情况中如果仍使用作用域,那么生成的所有函数就会返回相同的值(即最后一次生成的变量的值),此时必须要用默认值参数,而不是作用域。
  64. 在声明nonlocal名称的时候,它必须已经存在于该外层函数的作用域中,而不能由内嵌def中的第一次赋值来创建
  65. nonlocal名称只能出现在外层的def中,而不能在模块的全局作用域中或def之外的内置作用域中
  66. global和nonlocal的区别:global使得作用域的查找从外围模块的作用域开始,并允许对那里的名称赋值,如果名称不存在于该模块中,作用域查找将继续深入内置作用域。;nonlocal将作用域查找限制为只在外层的def中,同时要求名称已经存在那里,并允许对它们赋值,作用域查找不会继续进入到全局或内置作用域
  67. python在函数创建时就解析nonlocal,而不是在函数调用的时候
  68. 函数属性与对应函数处在同一层,且可以在函数内使用,这样就可以作为代替nonlocal的一种方式。因为自增一个属性时修改这个函数对象引用的一部分,而不是真的要在外层作用域内给一个名称赋值,因此不需要nonlocal声明。
  69. 在python 3.X 中函数传入的参数需要传入后手动解包,return可以利用元组返回多个值再手动解包(模拟输出参数和多重结果)
  70. 在函数调用时,参数必须按此顺序出现:所有基于位置的参数(value),之后时所有关键字参数(name=value)和*iterable形式的组合,之后是**dict形式
  71. 在函数头部,参数必须按此顺序出现:所有一般参数(name),之后是所有默认值参数(name=value),之后是name或形式,之后是所有name或name=value的keyword-only参数,之后是**name形式
  72. 函数头部中的参数名称还可以有一个注解值,格式为 变量名:注释=默认值。对于函数名也有一个注解值,格式为 def 函数名(参数列表) -> 注释: ,注意lambda函数不支持注解,注解可以使用函数的__annotations__属性获取,会返回一个字典,包含 参数:注解 形式的键值对,函数注释则会以 ‘return’:函数注解 的形式返回在字典中
  73. 参数名也是变量名,要遵循变量命名规则
  74. isinstance() 函数来判断一个对象是否是一个已知的类型,以下是 isinstance() 方法的语法: isinstance(object, classinfo)
  75. 获取当前递归的最大深度限制,导入sys,用sys.getrecursionlimit()。同时可以用sys.setcursionlimit(n) 来重设递归的最大深度限制
  76. lambda嵌套map调用或列表推导表达式可以实现执行循环的效果
  77. map(函数,由第一个参数组成的可迭代对象,由第二个参数组成的……,……)
  78. reduce需要从functools模块中导入,格式为reduce(函数,可迭代对象,可迭代对象为空时的初始值),其中第三个参数可选
  79. reduce函数常与内置的operator模块组合
  80. 通用的列表推导结构如下:[expression for target1 in iterable1 if condition1 for target2 in iterable2 if condition2 …… for targetN in iterableN if conditionN] 注意:不同for之间只用空格连接,不需要其他分隔符号!
  81. 列表推导的格式[(x,y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1],如果用map,filter,reduce代替的话就是list(reduce(lambda z,w:z+w,map(lambda x:list(map(lambda y:(x,y),filter(lambda y:y % 2 == 1,range(5)))),filter(lambda x:x % 2 == 0,range(5)))))
  82. 常规函数用return返回一个值,生成器函数用yield返回一个生成器对象
  83. 与retuen不同,yield作为一个表达式,可写成x = yield i 的形式,下一次运行.next()方法时将yield返回值(默认为None)赋给内层作用域的x
  84. 生成器函数产生的生成器对象的send方法:将上一次保留状态中的yield语句返回值赋值为send方法的参数,并执行.next()方法 (83、84条对应009-3.py)
  85. 编写生成器函数时,for i in List:yield i 可以写为 yield from List
  86. 一个易犯错的地方:a = (生成器表达式) ; b = a ; c = a 此时a、b、c的指针指向同一个对象
  87. 一个易犯错的地方:可变对象被引用时,自身的变化会影响引用其的对象!即使对象是正在使用中的生成器对象
  88. 生成器如果内部发生错误,在.next()方法中,发生错误之前的部分仍能返回,但当直接用list等直接获取所有结果时,会返回停止时的错误
  89. 注意一下两个表达式中for和if的顺序:x for x in List if condition ; x if condition else y for x in List
  90. 用while iter:的格式来遍历一个单遍迭代器会失败,因为单遍迭代器的bool值永远是True,即使它所有元素都被返回过了
  91. [f(x,y) for x in L1 for y in L2]可以构成列表推导,但[f(x,y) for x,y in s]不能,s只能是含两个元素的对象来被解包
  92. 字典推导的两种格式为:{key:value for key,value in L} 和 {x:f(x) for x in L} 注意前一种不能把key,value用两个for来写,否则value只取最后一个值。但是允许key中含有多个变量,此时会用到多个for,但当value中有key中没有的变量时,该变量也只取for后的最后一个值。例:{str(x)+str(y):[x,z] for x in range(3) for y in range(3,6) for z in range(6,9)} 的结果为:{‘03’: [0, 8], ‘04’: [0, 8], ‘05’: [0, 8], ‘13’: [1, 8], ‘14’: [1, 8], ‘15’: [1, 8], ‘23’: [2, 8], ‘24’: [2, 8], ‘25’: [2, 8]}
  93. 注意不仅函数调用可以赋值给一个对象,函数引用也可以,且像time.close这种后面不加元素的函数的引用赋值有时很有用
  94. 现在python版本里,sys模块中不再含有platform,platform自成一个模块
  95. 注意:局部变量是被静态检测的(在代码运行前,就已被检测),所以存在一个陷阱,详细见P652
  96. 以from复制而来的名称和其来源的文件之间并没有联系。但可变对象除外,因为其修改的是对象而不是名称。
  97. 导入其他模块中的变量时同时可以给该变量重新命名。例: from M import func as mfunc(注意:此时不会给func赋值)
  98. 词法作用域搜索:在Python中,一段代码的作用域完全由该代码在文件中所处的实际位置决定。作用域绝不会被函数调用或模块导入影响。例如,在moda模块的f()函数中声明了全局变量X,在modb中声明X后,导入moda并调用moda.f(),此时modb中的X不为全局变量,不会被f()函数修改。
  99. 模块间互相导入,并由 . 获取是被允许的,例如可以使用mod1.mo2.X
  100. 注意在mod1可以编写import mod2,然后使用mod2.mod3.X,却不能编写import mod2.mod3。这一语法会启用所谓的包(目录)导入,它们的import语句将用于反映目录树结构,而非简单的文件导入链。
  101. reload是一个函数,而不是一条语句!reload在Python3中被移入imp标准库模块。reload传入的参数必须是一个已经存在的模块对象,而不是一个新的名称。
  102. reload会在原位置修改模块对象并覆盖其命名空间,reload并不会删除并重新创建模块对象。例如若mod模块里声明了变量a,导入mod模块之后将mod模块里的名称a改为c,再重新加载,此时命令行下可以同时用mod.a和mod.c,此时命令行下mod的属性中同时含有a和c。
  103. 包导入基础:包导入适用于import和from语句。import dir1.dir2.mod语句表明了机器上有个目录dir1,而dir1里有子目录dir2,以及dir2内包含有一个名为mod.py(或类似文件)的模块文件,此外这个导入还意味着dir1在某个容器目录dir0内,而dir0目录可以在Python模块搜索路径中找到。
  104. pythonpath的添加方法。(1)系统命令行中 export PYTHONPATH=$PYTHONPATH:/home/ershisui(2)Python中 import sys; sys.path.append(’/home/ershisui/’)
  105. 如果选择使用包导入,包导入语句的路径中的每个目录内都必须有__init__.py这个文件,否则包导入会失败。也就是说在第103条笔记的例子中,dir1和dir2内都必须包含__init__.py文件。容器目录dir0则不需要__init__.py文件,因为其本身没列在import语句之中。更详细地说,init.py文件可以用作包初始化的钩子。
  106. 一个进程中python在首次导入某个目录时,会自动执行该目录下__init__.py文件中的所有程序代码。注意,任何已导入的目录也可以传递给reload,来强制该项目重新执行,此时仍会运行一遍__init__.py文件。
  107. 如果要使用 from* 语句,必须在__init__.py文件内定义__all__列表来规定目录以 from* 语句形式导入时,需要导出什么。如果没有设定__all__,from* 语句不会自动加载嵌套于该目录内的子模块;取而代之的是,加载了所有该目录的__init__.py文件中赋值语句定义的名称(因为没导入目录,__init__无法将属性给目录,所以直接导入了__init__中的名称)(有__all__则不会载入这些名称)。
  108. from* 语句不会导入__init__。
  109. import语句可以直接导入一个目录,如果是初次导入的话则会运行一遍__init__,将__init__的所有属性给目录名(__init__起到了将目录变成一个模块的作用),但不会把目录里的模块和子目录作为该目录的属性。
  110. 如果要让一个目录内的模块或子目录作为该目录的属性,只能以 import dir.mod 的格式导入。
  111. 总结:__init__的作用就是在导入该目录时运行,将该目录变成一个模块,并将自己的属性给这个目录模块。(变成模块的说法可能不准确)
  112. import语句后可接任意个点,from A import B 语句中A可以有任意个点,B不可以含有点!(见截图笔记5)
  113. 相对导入:在python3中,import后的路径及from后的第一个路径都是绝对导入(即按标准的模块搜索顺序找到模块),如果需要导入同一个(或者往上几层)目录下的某个模块时,必须使用from . import格式的语句,头部的一个点代表当前目录,n个点代表当前目录往上n-1层目录。可以用 from . import mod 导入模块,或 from .mod import f 导入当前目录下另一个模块中的变量。
  114. 由于导入模块时的搜索顺序和当前执行的文件位置有关,当使用包相对导入时,也代表着这个模块无法作为执行文件运行或测试了。
  115. 当一个模块作为顶级脚本被运行时,它的__name__属性的值将变为“main”字符串,但是当它被导入时却不是(字符串为模块名)。
  116. 命名空间包导入算法:如果导入spam(或带属性)时,在导入的搜索过程中既找不到含有__init__.py文件的常规包,也找不到名为spam的模块,但可以找到不含__init__.py文件的spam目录,python会自动地把这些目录打包,创建一个新的命名空间包spam,其__path__属性为含有这些路径的可迭代对象,并且其无__file__属性(常规包的__file__属性为其__init__.py文件的路径)。
  117. 相对导入也适用于命名空间包。命名空间包也支持任意的嵌套(无论较底层组件是什么类型)。
  118. import语句的as拓展功能可以重命名一个模块起到简化作用,然而重命名一个包的话却并没有什么卵用。
  119. assert语句,后接一个表达式,若表达式为真,无影响,若表达式为假,引出错误并结束进程。
  120. sys模块的argv返回一个包含命令行参数的列表,它是一个包含了在命令行上录入的单词的字符串列表,其中的第一项被规定为将要允许的脚本名称,后几项为命令行打开脚本的语句中,后面的以空格隔开的参数。利用sys.argv可以通过命令行参数向脚本提供输入。
  121. 模拟全局变量的一种方法:例如 global x ; x = 0 的效果可以由 import sys ; glob = sys.modules[name] ; glob.x = 0 来模拟。
  122. 用名称字符串导入模块:内置的__import__函数参数为一个模块名的字符串格式,返回值为这个要导入的模块。注意:由于__import__是一个函数而不是一个语句,所以必须把它的返回值赋值给一个变量才有效。另外用 importlib.import_module 函数或许更好。
  123. 假如要重载某个模块A,并且A导入模块B和C,重载只适用于A,而不适用于B和C。A中导入B和C的语句在重载的时候会重新运行,但它们只是获取了已经载入的B和C模块对象。
  124. 解决笔记第123条中的问题的一个方法是编写一个递归重载器。需要利用模块的__dict__属性、type内置函数、标准库的types模块。
  125. from陷阱:一个模块中的变量的引用仅在这个模块的作用域中引用,被from的形式导入到另一个模块的时候,同名的变量赋值语句不会对原模块中变量被引用时的值造成影响。例如《Python学习手册》P761。
  126. 只有用from导入模块中的变量无法用reload重载。
  127. 如果要重载用from导入的变量的话,需要经过下面三步:用import导入模块(由于from导入过模块,只不过没给模块赋值,所以此时模块还时之前的模块),重载模块(模块被重载了,但之前导入的那个变量未被刷新),再用from导入那个变量。
  128. 代码和现象见recur1.py和recur2.py。
  129. Python类的__slots__属性详解:https://blog.csdn.net/cadi2011/article/details/97798628
  130. 委托模式(代理模式)中存在代理方和受委托方两者,一般情况下代理方负责接口适配,而受委托方负责具体执行。
  131. shelve数据库的打开方式是x = shelve.open(…),关闭方式是x.close()
  132. 将存储在shelve数据库中的类实例取出时不需要再导入自己的类就能使用该实例继承的方法。
  133. 可pickle化的类必须被编写在一个模块文件的顶部,而且该模块文件又必须可以通过sys.path模块的查找路径所列出的目录访问到。
  134. 抽象父类的一种更好的实现方法是使用装饰器,可以在创建实例的时候就报错,而不是等到运行为完善的方法时才报错。该装饰器需要在abc库中导入ABCMeta和abstractmethod。3.X版本中,需要在创建类时的class头部使用关键字参数metaclass=ABCMeta,并在抽象的方法前加入@abstractmethod。2.7版本中需要在类中加入 metaclass = ABCMeta,并在抽象的方法前加入@abstractmethod。
  135. 无法从def语句外访问函数或方法内的局部变量,func.attr 的格式无法在外部作用域中得到局部作用域中的变量。具体见p857。
  136. 类可以访问其外部作用域中的变量,但无法作为外部作用域被访问其中的变量。若需要类中的方法访问类的实例创建时修改的变量,只能在类的方法中使用 self.x ,而不是 x 。详见p860下部分代码。关于这个的作用域测试加强版代码在010-1.py。
  137. 与嵌套函数中会发生UnbrondLocalError(详见补充笔记1)类似,若在内层类(嵌套在函数里的类)的内部调用一个变量后又对那个变量赋值,则前面的调用会调用global frame内的变量,而不是向外一层(函数层)的变量。代码见截图笔记6。
  138. 类的命名空间字典包含其中命名的属性、方法(但不包含其父类中命名的)。实例的命名空间字典包含其类中及类的方法调用中产生的self的属性。
  139. 常见运算符重载表在p871~872。
  140. __getitem__和__setitem__在python3中分别起到索引、切片调用和索引、切片赋值的作用。在python2中见p875~876。
  141. 列表括号中的分片是一种对象,还可以表示为slice(start,stop,step),并且start、stop、step是slice对象的三个属性。
  142. __index__方法在使用时会为一个实例返回一个整数值,例如python3中将该实例放在索引、切片参数、bin函数参数、oct函数参数、hex函数参数等的时候。具体见p876。
  143. __getitem__方法同时是迭代的一个退路选项。使用从0到更大的索引值,重复对序列进行索引运算,直到检测到超出边界的IndexError异常。
  144. 实际实现迭代应先尝试__iter__方法,只有当对象不支持迭代协议的时候才尝试__getitem__。如果使用__iter__方法,python会重复调用这个迭代器对象的__next__方法来产生元素,直到引发StopIteration异常。具体实现见实例笔记010-2.py。
  145. 多遍迭代器的实现可以用两个类,其中一个是单遍迭代器,另一个的__iter__方法中通过返回单遍迭代器的实例来重复使用单遍迭代以实现多遍迭代的效果(这些单遍迭代器的实例命名空间不会互相干扰)。具体实现见实例笔记 skipper.py。
  146. 针对多重迭代器的类实现,还可以直接在__iter__方法中使用 yield 语句,而不需要再定义一个辅助类。
  147. 对于in上下文(只是in),contains 方法,优先于 iter 方法,优先于 getitem 方法。对于其他的迭代、推导上下文, iter 方法优于 getitem 方法。对于索引和分片,只能用 getitem 方法。
  148. getattr 方法只对类中未赋值的,且在外部未赋值的属性有用。
  149. 如果在 setattr 方法中出现对 self 的属性的赋值运算或setattr内置函数,会导致无限递归。防止这种情况的一种方法是使用对 dict 属性的字典键的赋值。另一种方法是通过一个调用把任意属性赋值传递给一个更高级的父类,如使用 object.setattr(self,attr,value)。注:新式类最好用第二种方法。
  150. delattr 方法在使用 del 语句时同样有和 setattr 方法一样的问题,可以用同样的两种方法解决。
  151. getattribute 方法拦截所有的属性访问,而不只是那些未定义的,所以使用时也需避免循环调用。
  152. 使用 @property 装饰器将一个方法(无参数)伪装成一个类的属性。
  153. python描述符的使用(get , set , delete)见https://www.cnblogs.com/sfencs-hcy/p/10540469.html
  154. 属性查询优先级。
      ① getattribute(), 无条件调用
      ② 数据描述符:由 ① 触发调用 (若人为的重载了该__getattribute__() 方法,可能会调职无法调用描述符)
      ③ 实例对象的字典(若与描述符对象同名,会被覆盖哦)
      ④ 类的字典
      ⑤ 非数据描述符
      ⑥ 父类的字典
      ⑦ getattr() 方法
    155.一个类只要定义方法 get, set, delete 中的一个或多个,就可以称为描述符,但如果只定义了 get() 方法,而没有定义 set(), delete() 方法,则认为是非数据描述符,反之则成为数据描述符。
  155. str 会首先被打印操作和str内置函数尝试(print运行的内部等价形式),且仅当打印操作在顶层时才有效(如不能嵌套在列表中)。repr 用于所有场景,包括交互式命令行、repr函数、嵌套的显示。
  156. __str__和__repr__函数都必须返回字符串,否则会引发错误。
  157. __add__为左侧加法,实例位于左侧;radd__为右侧加法,实例位于右侧;iadd__为原位置加法。使用__radd__时,可以直接重用__add,或更快的可以用__radd=add
    159.当不同类的实例混合出现在表达式中时,优先选择左侧的类。
  158. __call__的运算符重载可以把实例像函数一样进行调用。
  159. ltgtlegeeqne__作为比较运算符,和__add、__mul__不同的是不分左右,例如实例 x 的__lt__方法对 x < 1 和 1 > x 都适用。
  160. cmp 也是比较运算符,但只用在python2中。
  161. 在布尔上下文中,优先调用 bool 方法,其次是 len 方法,若两个方法都没有,则默认为True。(python2中 bool 改为 __ nonzero__)
  162. 鉴于很多原因,建议用其他方法取代析构函数 del
  163. csv库用于读取文件。xxx = csv.reader() 创建阅读器。

网络

基础
  1. 关于URL的格式,协议类型有http和https,后者为加密有证书的协议。协议类型与域名间用 / 分隔,域名与路径之间用 / 分隔,每层路径后面需要加 / ,路径与参数间用 ?分隔,多个参数间用 & 分隔,参数用 key=value 的格式表示。域名后面有时要加上 :端口号 ,http默认端口号为80,https默认端口号为443。
  2. requests库的 get 方法获取指定网址的返回结果。get传入两个参数,第一个为地址,第二个为关键字参数params,其值为一个字典,对应于URL的参数。
  3. API 应用程序接口,可以快速调用某个程序。本质上就是个URL,只是返回的内容统称为数据,没有大量多余的字符。
  4. API 可拆解为地址和参数两部分。地址为从协议到路径的URL前面一段。参数就是URL的参数部分。
  5. 提交数据至服务端进行增加、修改、删除等操作,都是POST操作。典型的POST操作有提交表单进行登录。requests库的 post() 方法可以实现POST操作,传入若干个参数,第一个为地址,剩下的为关键字参数。如果为data,其值为一个字典,对应为待提交的表单数据。如果为json,其值为一个字典,对应为待提交的JSON(JS对象简谱)数据。
  6. http状态码反应了本次请求的状况,用 xxx.status_code 属性得到。常见的有200 - 请求成功
    301 - 资源(网页等)被永久转移到其它URL
    404 - 请求的资源(网页等)不存在
    500 - 内部服务器错误
  7. 得到具体的响应文本内容需调用 xxx.text 属性。
  8. requests库请求图片、excel等非字符文本文件时,不能用 .text 属性,而要使用 .content 属性。请求图片返回的内容是bytes字节。
  9. resquests库中的JSON对象可调用 .json() 方法返回字典数据。
  10. HTTP消息头 headers 作用是在发起请求的时候,除了附加参数外,可以附加更多的隐藏信息。详见:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers
  11. User-Agent消息头用于在指定URL发送请求的时候告诉服务端当前用户的浏览器类型、版本、操作系统、CPU等非隐私的技术信息。在调用get方法时,加个headers关键字参数,值为一个字典,字典中包含键’User-Agent’和对应的值,例如 ‘Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1’ 即可。
  12. Referer消息头表示请求的来源,值可以是地址等。
  13. requests请求后得到的对象可以用 url 属性得到最终响应的URL。
  14. Host消息头表示当前请求的域名(注意不带协议头)。平时使用时,Headers的三个重要信息,Referer、Host、User-Agent 建议都写全。
  15. open函数的第二个参数 'w’代表写入文本,'wb’代表写入二进制内容(如表单),'a’代表继续写入,'r’代表只读。
  16. xlrd库用于解析excel文件。第一个sheet、第一行、第一列的位置是0,0,0。打开excel文件用 xlrd.open_workbook(‘xxxx.xlsx’)。excel对象的 sheet_by_index(i) 方法传入序数用于获取到 sheet 对象。sheet对象的nrows属性为总行数,ncols属性为总列数。sheet对象的cell(x,y)方法传入第x+1行,第y+1列的坐标,返回单元格对象,单元格对象的 value 属性返回单元格的值。
  17. 当 requests.get 返回的对象,其文本发生乱码现象时,需要修改其encoding属性。例如response.encoding=‘utf-8’。
登录相关
  1. requests.get() 函数的另一个关键字参数是cookies,记录需登录页面的账号信息。获得此信息需自己在浏览器中用开发者工具查看。F12进入开发者工具,进入Network菜单,刷新后在name栏中找到mine或者域名,选择Headers子菜单,找到其中的Cookies对应的值。注意cookies在函数中是额外的参数,其值为一个字典,需要把原来的格式转化成字典。注意cookies是临时的,无法保持登录状态。
  2. requests.Session() 返回一个session对象,之后用这个对象发送get或post请求,即可在登陆状态下与服务器通讯。
  3. 开发者工具中勾选Preserve log 找到signinLogin文件,观察模拟登录时需要的参数。例如Form Data中存储了登陆时提交的表单数据,Host,Referer,User-Agent,登录的地址(Request URL),登录的方式(Request Method)为GET还是POST。模拟登录时需要充分利用这些信息。
  4. 个人理解:Session对象的存储状态功能,相当于存储了保留状态的Referer。在保留状态调用一个新的URL时,仍用response2=session.get(url,headers=header)。但headers中不需要Referer项。注意,新的指令导向的页面的headers中的参数有可能发生改变,需要自己到对应的页面中确认。
邮件相关
  1. 使用SMTP协议需要先进入邮箱设置开启SMTP(IMAP/SMTP)支持,通过各种验证后可以得到授权码,一定要保存授权码。
    pgybycqmddbkbgab(我qq邮箱的)
  2. 需要导入以下库。import smtplib; from email.mime.text import MIMEText; from email.header import Header;
  3. MIMEText函数获取 文本内容MIME对象 message = MIMEText(_text, _subtype=‘plain’, _charset=None, *, policy=compat32)。第一个参数为普通文本内容,第二个参数传入次要类型,默认为’plain’纯文本格式,另’html’为网页格式。第三个参数为编码格式,无传入时,如果文本内容只包含ascii内容为’us-ascii’,否则为’utf-8’。
  4. MIME对象重载了索引运算符,[‘From’]、[‘To’]、[‘Subject’] 分别为发送人名称,收件人名称和邮件标题(是名称不是地址)。其值需要用Header函数创建。
  5. Header(s=None, charset=None, maxlinelen=None, header_name=None, continuation_ws=’ ', errors=‘strict’) 第一个参数为文本内容,第二个参数为编码格式,默认情况同MINEText。
  6. 注意以下格式:
    ##邮箱的 SMTP 服务
    mailHost = “smtp.xxxx.com”
    ##用户名
    mailUser = “[email protected]
    ##授权码
    mailPass = “”
  7. 创建SMTP_SSL实例连接邮箱用 smtplib.SMTP_SSL(host=’’, port=0, local_hostname=None, keyfile=None, certfile=None, [timeout, ]context=None, source_address=None)。第一个参数为mailHost,第二个参数为邮箱的SMTP端口号,需要在对应邮箱的帮助文档中找,QQ为465。
  8. 发送邮件前需要授权认证,使用SMTP_SSL实例的login(mailUser,mailPass) 方法,第一个参数为自己的邮箱地址,第二个参数为授权码。
  9. 发送邮件用SMTP_SSL实例的sendmail(sender,receivers,message.as_string()) 方法,第一个参数为发件地址,第二个参数为列表,包含若干个接收地址,第三个参数要传入 MIME对象的as_string()方法返回字符串格式的MIME对象。
  10. 邮件可能会发送错误,需要用try-except语句检测 smtplib.SMTPException。

正则表达式

  1. re.search(regex,text) 用于查找text中是否含有regex。如果含有返回re.Match对象,包含了从左往右最先查找到的最长符合字符串的信息。如果没有返回None。re.Match对象的start()、end()方法分别返回查找到的字符串的起止位置(前闭后开),span()方法返回起止位置的元组。group()方法返回匹配的字符串。
  2. \n换行符 \r回车符 \t制表符 \f换页符 \v垂直制表符
  3. 表达式中很多大写字母表示’非’的概念。例如 \s 匹配任意空白字符,\S 匹配任意非空白字符。\w 匹配任意单词字符(AZ,az,0~9,_),\W 匹配任意非单词字符。\d 匹配任意数字字符(小数点不是数字字符),\D 匹配任意非数字字符。
  4. 小数点 . 匹配除\n,\r外的任何单个字符。
  5. ^ 置于表达式的开头,表示匹配字符串的开头。
  6. $ 置于表达式的结尾,表示匹配字符串的结尾。
  7. 在正则表达式前面多加一个反斜杠表示字符原意。例如\校验\,\d校验\d,$校验$。
  8. 个人总结:re中传入的字符串是把字符串的返回值传给对应的参数,所以写法会和正常的字符串有所区别,例如要用’\n’或r’\n’替代’\n’。一般统一用原始字符串。
  9. 正则表达式中的逻辑或表达式为 | ,优先级低于字符组合。
  10. 内用于枚举字符,也可以用 - 表示范围。
  11. 中括号 [ ] 内可以用 ^ 表示 非 来排除指定的范围。
  12. 一般限定符 ? 前面的子表达式出现0或1次,+ 前面的子表达式出现一次或以上,* 前面的子表达式出现任意多次。
  13. 范围限定符 {n} 表示匹配n次,{n,m} 表示匹配最少n次,最多m次,n不写默认为0,m不写默认最多不限。
  14. 限定符默认使用贪婪模式,即总是匹配最多的结果。在限定符后面加上 ? 可采用非贪婪模式,会检索最少的结果。但注意,仍是从左向右检索,所以检索到的不是针对全文来说最短的!!!!
  15. re库的search函数用于检索返回第一个结果。match函数为开头检索,相当于search函数使用了开头匹配符 ^。
  16. re库的findall()函数可以以列表格式返回匹配表达式的所有内容。(但限定符默认仍是贪婪模式)
  17. re库的finditer()函数可以检索返回一个包含re.Match对象的迭代器。但要小心,即使匹配不到内容,迭代器的布尔值仍为真。
  18. re库的sub()函数用于替换文本中的匹配表达式的内容。格式为re.sub(pattern, repl, sourceText, count) 第一个参数是正则表达式,第二个参数是要替换的字符串,第三个参数是源文本,第四个参数指定最大替换次数,不写默认为0,表示全部替换。
  19. 当一个正则表达式需要被用于大量匹配时,可以进行预编译 re.compile(regex),参数时正则表达式,返回值是 Pattern 对象,Pattern对象可以调用之前提到的所有方法,并去掉其中的regex参数。
  20. 我们把子表达式(必须在小括号内)称为组,获取每个子表达式的匹配结果的过程称为分组。re.Match 对象都能调用分组的group()、groups()方法。group() 或 group(0) 可以获得整个表达式的检索结果,groups() 可以获得包含所有小组字符串的元组,group(i) 和 groups()[i] 可以获得第i个子表达式的匹配结果。

爬虫

  1. urllib库的request模块中的urlopen函数用来打开并读取一个从网络获取的远程对象。
  2. 当创建一个BeautifulSoup对象时,需要传入两个参数,第一个参数时该对象所基于的HTML文本,第二个参数指定了创建该对象的解析器(注意以字符串形式传入)。常用的解析器有自带的html.parser和需要安装的lxml、html5lib。
  3. BeautifulSoup对象的find_all()方法:bs.find_all(tagName,tagAttributes) 可以获取页面中所有指定的标签。
  4. get_text()方法可以清除正在处理的HTML文档中的所有标签,然后返回一个只包含文字的Unicode字符串。通常在打印、存储和操作最终数据的最后才使用。
  5. BeautifulSoup文档的find_all和find方法完整的参数为:find_all(tag,attributes,recursive,text,limit,keywords) ; find(tag,attributes,recursive,text,keywords) 。
  6. 接上:标签参数tag可以传递一个标签的名称或多个标签名称组成的列表。
  7. 接上:属性参数attributes用一个字典封装一个标签的若干属性和对应的属性值。例如:.find_all(‘span’,{‘class’:{‘green’,‘red’}})
  8. 接上:递归参数recursive是一个布尔变量,默认值为True。如果recursive设置为True,find_all就会查找所有标签及其子标签(即递归地查找)。如果recursive值设置为False,find_all就只查找文档的一级标签。
  9. 接上:文本参数text是用标签的文本内容去匹配,而不是用标签的属性。例如:nameList = bs.find_all(text=‘the prince’)
  10. 接上:范围限制参数只用于find_all方法,find等价于limit等于1时的find_all。如果想获取网页中按网页上顺序排序的前x项内容,就可以设置它。
  11. 接上:关键词参数可以用来选择那些具有指定属性的标签。例如:title = bs.find_all(id=‘title’,class_=‘text’) 返回第一个在class_属性中包含单词text并且在id属性中包含title的标签。注意:关键词参数并不建议使用,见书P17。
  12. 需要了解的BeautifulSoup库里的四种对象:BeautifulSoup对象、标签Tag对象、NavigableString对象、Comment对象。
  13. bs文档的 .children 属性可以获得所有子标签,.descendants 属性可以获得所有后代标签。
  14. 接上: .next_siblings 属性可以获得所有当前标签后面的兄弟标签。例如对一个表格,选择标题行,然后调用next_siblings,就可以选择表格中除了标题行以外的所有行。类似的还有 .previous_siblings标签以及返回单个标签的 .next_sibling 和 .previous_sibling。
  15. 接上:.parent 和 .parents 可以查找父标签。
  16. 常用正则表达式符号见书P23。
  17. 标签的attrs属性返回一个字典对象。
  18. BeautifulSoup允许把特定类型的函数作为参数传入find_all函数。唯一的限制条件是这些函数必须把一个标签对象作为参数并且返回布尔类型的结果,此时结合Lambda匿名函数就很舒服了。(见P27)
  19. 匹配中文字符串的正则表达式为 [\u4e00-\u9fa5]
  20. HTML描述了一个网站的结构,是一种标记语言而非编程语言,用来承载网站内容。CSS用于设定页面布局、页面元素样式,不能单独使用。JavaScript作为高级的、解释型的编程语言能使网页可交互。由这三种语言组装成我们看到的网页。
  21. 内容 为标签,为结束标签。内容部分可以嵌套标签。——html格式
  22. 标签包裹的内容就是CSS元素。
  23. 标签包裹的内容是JavaScript代码。
  24. 利用浏览器的检查工具,打开Elements,其中可以查看页面每个部分对应的代码。Elements中任意一个节点都称作为DOM(无论大小)。DOM为树状结构,可以展开收缩、嵌套。
  25. 一个DOM元素,由 标签 + 多个属性 组成,属性的语法为属性名=属性值。例如中 form 为标签,id属性表示这个DOM元素在网页中唯一标识,class属性一般用于指定DOM元素的CSS样式。
  26. 爬虫主题逻辑分为四部分。1.人工查找承载信息的网站。2.分析网页类型(静态网页:服务器渲染出HTML返回给客户端;动态网页:客户端通过Javascripts渲染出HTML)3.使用网页解析器分析网页,静态网页用RE和Beautiful Soup,动态网页分析API和pyppyteer。4.提取需要的信息。
  27. BS对象的 . 用于获取DOM节点(任意后代节点)(返回也是BS对象)。若有多个对象,按dfs的方法返回第一个后代节点。若需要获取的后代节点不存在,则返回None。
  28. 类似字典的索引运算符,可以获得标签对应的属性。.attrs 属性则返回字典格式的标签的所有属性。
  29. get_text() 方法返回该DOM节点的内容,若嵌套标签,将嵌套的每个标签改为换行。注意内容是不包含节点属性的。
  30. find_all(‘xxx’,‘info’) 方法返回所有标签为xxx的后代节点的列表。(dfs顺序)。第二个参数默认筛选class属性中带有info的元素(不一定只是"info")。如果要筛选其他属性,传入的参数变为关键字参数即可。
  31. 更详细的筛选需要使用BS对象的 select() 方法,传入一个字符串(css selector)。该字符串的基本格式为 > 代表子字节关系,. 代表class属性的值。例如 ul > li.info.last > a.download 表示筛选 ul 节点下的class属性为"info last"的 li 节点下的class属性为download的 节点。返回一个包含所有符合条件的BS对象的列表。更详细的语法见 https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Selectors 。在chrome elements菜单中选择节点,右击-copy-copy seletor可以获得该节点不准确的seletor。
  32. 动态页面在network中获取的url返回结果虽然可以调用 .text 属性,但也有可能是json对象,不一定能用BeautifulSoup。可以调用 .json() 方法获取字典格式的对象。
  33. 爬取network中的动态页面数据时常常要用post函数的data或json参数传入表格数据。
  34. 理解:Puppeteer是无界面(Headless)Chrome工具,是一个Node库,提供了一个高级的API来完全模拟人操作浏览器行为。Pyppeteer是其Python版本。

你可能感兴趣的:(python)