💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## python 学习记录 **last update: 2022-06-06 10:23:11** ---- [TOC=3,8] ---- ### Linux源码安装python(2、3) ~~~shell wget https://www.openssl.org/source/openssl-1.1.1o.tar.gz tar zxvf openssl-1.1.1o.tar.gz ./config --prefix=/opt/openssl-1.1.1o --openssldir=/opt/openssl-1.1.1o/openssl no-zlib make && make install echo "/opt/openssl-1.1.1o/lib" >> /etc/ld.so.conf ldconfig -v ~~~ ~~~shell yum -y install gcc gcc-c++ gdb wget https://www.python.org/ftp/python/3.10.4/Python-3.10.4.tgz tar -zxvf Python-3.10.4.tgz cd Python-3.10.4 ./configure -h make clean ./configure --prefix=/usr/local/python-3.10.4 --with-openssl=/opt/openssl-1.1.1o --with-openssl-rpath=auto --with-ssl make && make install ~~~ ~~~ ls /usr/local/python-3.10.4/bin -Fl /usr/local/python-3.10.4/bin/python3.10 --version /usr/local/python-3.10.4/bin/pip3.10 --version /usr/local/python-3.10.4/bin/python3.10 -m site /usr/local/python-3.10.4/bin/pip3.10 list -vvv --format=columns ~~~ ~~~ ln -s /usr/local/python-3.10.4/bin/python3.10 /usr/local/bin/python3 ln -s /usr/local/python-3.10.4/bin/pip3.10 /usr/local/bin/pip3 ~~~ 可选 ~~~ ln -s /usr/local/bin/python3 /usr/local/bin/python ln -s /usr/local/bin/pip3 /usr/local/bin/pip pip install -U pip -i https://pypi.tuna.tsinghua.edu.cn/simple/ python --version pip --version ~~~ [centos 安装python3.8报错_qq_36664203的博客-CSDN博客](https://blog.csdn.net/qq_36664203/article/details/106301856) [linux 源码编译安装python3.* - 明月知秋的博客](https://www.xcwmoon.com/post/135) [python3 openssl问题(贼有用) - Captain_Li - 博客园](https://www.cnblogs.com/lemon-le/p/13419429.html) [python3中pip3安装出错,找不到SSL的解决方式_python_脚本之家](https://www.jb51.net/article/176223.htm) [解决安装python3.7.4报错Can''t connect to HTTPS URL because the SSL module is not available_python_脚本之家](https://www.jb51.net/article/166688.htm) [编译安装numpy报 error: ‘for’ loop initial declarations are only allowed in C99 mode 问题解决_小白_愚妄的博客-CSDN博客](https://blog.csdn.net/weixin_42883321/article/details/122458160?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-122458160-blog-123213842.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-122458160-blog-123213842.pc_relevant_antiscanv2&utm_relevant_index=1) > `$: export CFLAGS='-std=c99'` ---- ### PyCharm 使用记录 > python 语法最接近自然语言,也就是像我们平常说话那般自然,即使你不熟悉语法,当你不知道该怎么写时,你就按照自己的想法写,你会发现很多时候你总是对的,它就是你所想的那样,python 就是这样,就是你所想的样子,那样自洽,那样自然,那样简单,不需要任何的刻意,它不追求任何技巧,不故作高深,只是人思想的自然表达而已,仿佛每个人天生就熟悉它。 #### 控制台中文输出乱码 解决方法:https://www.cnblogs.com/it-tsz/p/9823536.html 1. 文件 -> 设置 -> 编辑器 -> 文件编码:项目编码 - GBK 2. 文件 -> 设置 -> 编辑器 -> 文件和代码模板 -> Python Script: ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- ' a python script ... ' __author__ = 'xiasf' ``` ---- #### 习惯配置 主题:Monokai (配色方案) 字体: Droid Sans Mono 大小: 13 行高: 1.1 - 编辑器 > 常规 > 外观 > 显示空格(前导、内部、尾随) - 编辑器 > 常规 > 编辑器选项卡 >外观 > 编辑已修改(*) - 系统设置 > 推出 IED 之前确认 > 终止进程 插件: - CodeGlance (类似 sublime 中的代码预览地图) - Chinese (汉化) ---- #### OSError: Cannot load native module 'Crypto.Cipher._raw_ecb': ``` OSError: Cannot load native module 'Crypto.Cipher._raw_ecb': Trying '_raw_ecb.pyd': [Error 193] %1 不是有效的 Win32 ``` https://github.com/Legrandin/pycryptodome/issues/155 上面报错不是这个问题,而是 64位的操作系统,只能安装 64位的 python 才行。https://www.python.org/downloads/release/python-2718/ ---- #### 关闭 sql 等警告提示 通常我们不需要它检查sql,因为这需要链接数据库,而数据库还可能是远程的,开发时不希望连接检查。 https://blog.csdn.net/weixin_42686768/article/details/97121081 https://www.cnblogs.com/lurenjia1994/p/9681637.html https://www.cnblogs.com/zq8421/p/10356383.html https://www.cnblogs.com/wisir/p/10898469.html https://blog.csdn.net/windscloud/article/details/80208960 [Python基础之PEP8规范(代码写作规范) - 知乎](https://zhuanlan.zhihu.com/p/88729367) [Python PEP8 编码规范中文版_基因记忆-CSDN博客](https://blog.csdn.net/ratsniper/article/details/78954852) https://legacy.python.org/dev/peps/pep-0008/ ---- #### pyautogui.click() 没有效果 [51模拟器使用python pyautogui点击没有效果的解决方法_york1996的博客-CSDN博客](https://blog.csdn.net/york1996/article/details/104154806) > 右键启动 PyCharm 或者其他 IDE 的时候选择以 管理员权限 启动即可。 ---- #### 关闭IDE时选择终止或断开连接的区别 PyCharm 终止和断开连接不一样,终止是正常关闭正在执行程序,如 `db` 类析构时会断开数据库连接,而非正常终止程序断开连接则不会这样,没有执行析构。 ---- ### python 学习记录 ---- #### 见微知著 见微知著,以小见大,简单的事物总是蕴含着其最本质的规律与哲学。 - 那是不是越低级的程序越难学,越高级的程序越简单?表面上来说,是的,**但是,在非常高的抽象计算中,高级的 Python 程序设计也是非常难学的**,所以,高级程序语言不等于简单。 - 任何计算机程序都是为了执行一个特定的任务,**有了输入,用户才能告诉计算机程序所需的信息,有了输出,程序运行后才能告诉用户任务的结果。** 输入是 Input ,输出是 Output ,因此,我们把输入输出统称为 Input / Output,或者简写为 IO。 - **计算机之所以能做很多自动化的任务,因为它可以自己做条件判断。** - **为了让计算机能计算成千上万次的重复运算,我们就需要循环语句。** - 写计算机程序也是一样,**函数就是最基本的一种代码抽象的方式。** - 在协程中,不能调用普通的同步 IO 操作,因为所有用户都是由一个线程服务的,**协程的执行速度必须非常快,才能处理大量用户的请求。** 而耗时的 IO 操作不能在协程中以同步的方式调用,否则,等待一个 IO 操作时,系统无法响应任何其他用户。这就是异步编程的一个原则:**一旦决定使用异步,则系统每一层都必须是异步,“开弓没有回头箭”。** - 但是在 Python 中,代码不是越多越好,而是越少越好。代码不是越复杂越好,而是越简单越好。请始终牢记,**代码越少,开发效率越高。** [Python 教程 - 廖雪峰的官方网站](https://www.liaoxuefeng.com/wiki/1016959663602400) [Python 3.11.3 Documentation](https://docs.python.org/zh-cn/3/index.html) [PEP 8 – Style Guide for Python Code | peps.python.org](https://peps.python.org/pep-0008/) ---- #### 数据类型 list:列表,索引数组 [a, b, c, ...] tuple:元组,不可变的列表 (a, b, c) dict:字典,关联数组 {'k': 1, ...} set:集合,没有 value 的 dict {'k', ...} ,集合中没有重复的元素 `type()` 获取变量类型: ```python >>> type(1) <class 'int'> ``` `isinstance()` 判断变量类型: ```python def my_abs(x): if not isinstance(x, (int, float)): raise TypeError('bad operand type') if x >= 0: return x else: return -x ``` ```python >>> isinstance(1, str) False >>> isinstance(1, (int, float, bool, str)) True ``` ---- #### 函数参数 相较于 php 来说,python 的函数参数定义及调用时传参方式太灵活了,这样在很多时候可以极大的方便调用,不过定义函数也复杂很多。 1. **位置参数**(必传,支持按顺序或按参数名传入) 2. **默认参数**(支持按顺序或按参数名传入,没有传入参数时,参数值为默认值(缺省值),**默认参数必须指向不变对象!**,通常把变化大的参数放前面,**变化小的参数放后面。变化小的参数就可以作为默认参数。默认参数可以简化函数的调用。**) 3. **可变参数** `*args`(传入的参数个数是可变的,允许0个或任意个按位置传入的参数,函数接收到的参数是一个元组 `tuple`,调用时也可以在前面加一个 `*` 号把 `list` 或 `tuple` 元素变成可变参数传进去 ) 4. **关键字参数** `**kwargs`(类似 可变参数,允许0个或任意个按参数名传入的参数,函数接收到的参数是一个 `dict`,关键字参数 常用于扩展函数的功能) 5. **命名关键字参数**(限制关键字参数的名字,如限制必传,也可以有默认值;特殊分隔符 `*` 后面的都是命名关键字参数,如果已经有了一个可变参数 `*args` ,后面的命名关键字参数就不需要特殊分隔符 `*` 了) > 参数组合:在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,**参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数 和 关键字参数。** > 对于任意函数,都可以通过类似 `func(*args, **kw)` 的形式调用它,无论它的参数是如何定义的。(不论按顺序传参还是按参数名传参) [函数的参数 - 廖雪峰的官方网站](https://www.liaoxuefeng.com/wiki/1016959663602400/1017261630425888) ```python def fun(a, b): print(a, b) # TypeError: got multiple values for argument 'a' # TypeError: 参数 'a' 获取了多个值 fun(1, a=1) # SyntaxError: positional argument follows keyword argument # SyntaxError: 位置参数不能在关键字参数后面 fun(b=2, 1) # TypeError: fun() takes 2 positional arguments but 3 were given # TypeError: 接受2个位置参数,但给定了3个 fun(1, 1, 2) # ---------- def fun(**kwargs): print(kwargs) # TypeError: fun() takes 0 positional arguments but 3 were given # TypeError: 接受0个位置参数,但给定了3个 fun(1, 2, 3) fun(*{"a": 1, "b": 2, "c": 3}) fun(a=1, b=2, c=3) # ok # ---------- def fun(*args): print(args) # TypeError: fun() got an unexpected keyword argument 'a' # TypeError: 获得了意外的关键字参数“a” fun(a=1, b=2, c=3) fun(*{"a": 1, "b": 2, "c": 3}) # ok # -------------------------------------------- # 位置参数 def fun(name): pass fun('foo') # ---------- # 位置参数、默认参数 def fun(name, value="def", value2="def2"): pass # 以参数名称传参 fun('foo', value2="val2") # ---------- # 可变参数 ——— 参数数量不定 def fun(*args): print(args) fun(1, 2, 3) # (1, 2, 3) fun(*(1, 2, 3)) # (1, 2, 3) fun(*{"a": 1, "b": 2, "c": 3}) # ('a', 'b', 'c') def fun(a, *args): print(a, args) fun(1, 2, 3) # 1 (2, 3) def fun(a, *args, d): print(a, args, d) fun(1, 2, 3, 4) # TypeError: fun() missing 1 required keyword-only argument: 'd' fun(1, 2, 3, d=4) # 1 (2, 3) 4 # ---------- # 关键字参数 ——— 以参数名称传参,参数数量不定 def fun(**kwargs): print(kwargs) fun(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3} fun(**{"a": 1, "b": 2, "c": 3}) # {'a': 1, 'b': 2, 'c': 3} def fun(a, **kwargs): print(a, kwargs) fun(a=1, b=2, c=3) # 1 {'b': 2, 'c': 3} def fun(a, **kwargs, d=4): ^ SyntaxError: arguments cannot follow var-keyword argument # ---------- # 可变参数、关键字参数 def fun(*args, **kwargs): print(args, kwargs) fun(1, 2, 3, a=1, b=2, c=3) # (1, 2, 3) {'a': 1, 'b': 2, 'c': 3} fun(1, 2, 3, **{"a": 1, "b": 2, "c": 3}) # (1, 2, 3) {'a': 1, 'b': 2, 'c': 3} # ---------- # 命名关键字参数 ——— 命名关键字参数 c 必传、b 有默认值 def fun(a, *, b, c=3): print(a, b, c) # TypeError: fun() takes 1 positional argument but 3 were given fun(1, 2, 3) fun(1, b=2) # 1 2 3 def fun(*, b, c=3, **kwargs): print(b, c, kwargs) fun(a=1, d=4) # TypeError: fun() missing 1 required keyword-only argument: 'b' fun(a=1, b=2, d=4) # 2 3 {'a': 1, 'd': 4} def fun(*args, b=2, c, **kwargs): print(args, b, c, kwargs) fun(1, 2, a=1, d=4) # TypeError: fun() missing 1 required keyword-only argument: 'c' fun(1, 2, a=1, c=3, d=4) # (1, 2) 2 3 {'a': 1, 'd': 4} ``` ---- #### 高级特性 想到就能做到,为什么不呢。 **切片:** `[a:b:t]` 以最自然,效率最高的方式操纵 list 或 tuple。 ```python L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] # 取指定索引范围 >>> L[1:3] ['Sarah', 'Tracy'] # 倒数第一个元素的索引是 -1 >>> L[-2:] ['Bob', 'Jack'] >>> L[-2:-1] ['Bob'] ``` `L[0:3]`表示,从索引`0`开始取,直到索引`3`为止,但不包括索引`3`。即索引`0`,`1`,`2`,正好是3个元素。 ```python L = list(range(100)) # 前10个数,每两个取一个: >>> L[:10:2] [0, 2, 4, 6, 8] # 原样复制 >>> L[:] [0, 1, 2, 3, ..., 99] ``` 有了切片操作,很多地方循环就不再需要了。Python的切片非常灵活,一行代码就可以实现很多行循环才能完成的操作。 在很多编程语言中,针对字符串提供了很多各种截取函数(例如,substring),其实目的就是对字符串切片。Python没有针对字符串的截取函数,只需要切片一个操作就可以完成,非常简单。 list,tuple,string 都可以进行切片,操作结果类型仍是原类型。 ---- **迭代**: 如果给定一个 list 或 tuple,我们可以通过 `for ... in` 循环来遍历这个 list 或 tuple,**这种遍历我们称为迭代(`Iteration`)**。 python 的 `for` 循环还可以作用在其他可迭代对象上。 通过下标完成的遍历在 python 中不是迭代。 ```python for (i=0; i<length; i++) { n = list[i]; } ``` ```python >>> d = {'a': 1, 'b': 2, 'c': 3} >>> for key in d: ... print(key) ... a c b ``` 默认情况下, dict 迭代的是 key 。如果要迭代 value ,可以用 `for value in d.values()`,如果要同时迭代 key 和 value ,可以用 `for k, v in d.items()`。 ```python >>> l = {'a':1} >>> l {'a': 1} >>> l.items() dict_items([('a', 1)]) >>> type(l.items()) <class 'dict_items'> >>> type(l) <class 'dict'> ``` 只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。 ```python >>> from collections.abc import Iterable >>> isinstance('abc', Iterable) # str 是否可迭代 True >>> isinstance([1,2,3], Iterable) # list 是否可迭代 True >>> isinstance(123, Iterable) # 整数 是否可迭代 False ``` `for` 循环里,同时引用了两个变量: ``` # 列表如何遍历到下标呢? >>> for i, value in enumerate(['A', 'B', 'C']): ... print(i, value) ... 0 A 1 B 2 C # 元组可以遍历到下标 >>> for x, y in [(1, 1), (2, 4), (3, 9)]: ... print(x, y) ... 1 1 2 4 3 9 ``` ---- **列表生成式**: 列表生成式 即 List Comprehensions,是 Python 内置的非常简单却强大的可以**用来创建 list 的生成式。** 生成 list 最简单的方式是 `range()` 方法: ```python >>> list(range(1, 11)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ``` 但如果要生成更复杂的列表时,就需要用表现力更强的 **列表生成式**了: ```python >>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] ``` for 循环后面还可以加上 if 判断作为 过滤条件 以控制是否生成元素: ```python >>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100] ``` 在一个列表生成式中,`for`前面的`if ... else`是表达式,而`for`后面的`if`是过滤条件,不能带`else`: ```python >>> [x if x % 2 == 0 else -x for x in range(1, 11)] [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] ``` 还可以使用两层循环,可以生成全排列: ```python >>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] ``` 列表生成式也可以使用两个变量来生成 list : ```python >>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> [k + '=' + v for k, v in d.items()] ['y=B', 'x=A', 'z=C'] ``` ~~~ [ v for v in Iteration ] [ 表达式 for 元素 in 迭代对象 ] [ ... (表达式 for 元素 in 迭代对象) for 元素 in 迭代对象 ] 嵌套表达式从最右边开始计算 ~~~ ---- **生成器**: 通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而生成器就可以解决这个问题,边循环边计算。 创建列表和生成器的区别仅在于最外层的`[]`和`()`: ```python >>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630> # next() 生成下一个值 >>> next(g) 0 >>> next(g) 1 ... >>> next(g) 81 # 没有更多的元素时,抛出StopIteration的错误。 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration ``` 通过for循环来迭代它,并且不需要关心StopIteration的错误。 ```python >>> g = (x * x for x in range(10)) >>> for n in g: ... print(n) 0 1 ... 81 ``` 函数 generator,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似 generator: ```python def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' >>> f = fib(6) >>> f <generator object fib at 0x104feaaa0> ``` generator 函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到 `return` 语句或者最后一行函数语句就返回。而变成 generator 的函数,直接调用生成 generator 对象,在每次调用 `next()` 的时候执行,遇到 `yield` 语句返回,再次 `next()` 执行时从上次返回的 `yield` 语句处继续执行。 ```python >>> for n in fib(6): ... print(n) ... 1 1 2 3 5 8 ``` for 循环调用 generator 时,发现拿不到 generator 的 return语句 的返回值。如果想要拿到返回值,必须捕获 StopIteration 错误,返回值包含在 StopIteration 的 value 中: ```python >>> g = fib(6) >>> while True: ... try: ... x = next(g) ... print('g:', x) ... except StopIteration as e: ... print('Generator return value:', e.value) ... break ... g: 1 g: 1 g: 2 g: 3 g: 5 g: 8 Generator return value: done ``` 要理解 generator 的工作原理,它是在 for 循环的过程中不断计算出下一个元素,并在适当的条件结束 for 循环。对于函数改成的 generator 来说,**遇到 return 语句 或者 执行到函数体 最后一行语句,就是结束generator 的指令**,for 循环随之结束。 ---- **迭代器**: list、tuple、dict、set、str、generator 等,这些可以直接作用于 for 循环的对象统称为**可迭代对象**:**`Iterable`**。 可以使用 `isinstance(x, Iterable)` 判断一个对象是否是 `Iterable` **可迭代对象**: ```python >>> from collections.abc import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False ``` 生成器不但可以作用于 for 循环,还可以被 next() 函数不断调用并返回下一个值,直到最后抛出 StopIteration 错误表示无法继续返回下一个值了。 可以被 next() 函数调用并不断返回下一个值的对象称为 **迭代器**:`Iterator`。(表示一个惰性计算的序列) 可以使用 `isinstance(x, Iterator)` 判断一个对象是否是 `Iterator` **迭代器对象**: ```python >>> from collections.abc import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False ``` generator 生成器 既是 `Iterable` **可迭代对象**,也是 `Iterator` **迭代器对象** ,但 list、dict、str 虽然是 `Iterable` **可迭代对象**,却不是 `Iterator` **迭代器对象**。 把 list、dict、str 等 `Iterable` **可迭代对象** 变成 `Iterator` **迭代器对象** 可以使用 iter() 函数: ```python >>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True ``` 为什么 list、dict、str 等数据类型不是 `Iterator` **迭代器对象** ? 这是因为 Python 的 `Iterator` **迭代器对象** 表示的是一个数据流,Iterator 对象可以被 next() 函数调用并不断返回下一个数据,直到没有数据时抛出 StopIteration 错误。可以把这个数据流看做是一个有序序列,但我们却**不能提前知道序列的长度**,只能不断通过 next() 函数实现**按需计算**下一个数据, **所以 Iterator 的计算是惰性的,只有在需要返回下一个数据时它才会计算。** Iterator 甚至可以表示一个无限大的数据流,例如全体自然数。而使用 list 是永远不可能存储全体自然数的。 ---- #### 函数式编程 通过把代码封装成函数,把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。 **函数式编程**(请注意多了一个“式”字)——Functional Programming,虽然也可以归结到面向过程的程序设计,但**其思想更接近数学计算**。 计算是指数学意义上的计算,越是抽象的计算,离计算机硬件越远。 函数式编程就是一种抽象程度很高的编程范式,**纯粹的函数式编程语言编写的函数没有变量**,因此,任意一个函数,**只要输入是确定的,输出就是确定的**,这种纯函数我们称之为**没有副作用**。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。 函数式编程的一个特点就是,允许**把函数本身作为参数**传入另一个函数,还允许**返回一个函数**! **高阶函数**:接收另一个函数作为参数,这种函数就称之为高阶函数。 **map/reduce**: ```python >>> def f(x): ... return x * x ... >>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> list(r) [1, 4, 9, 16, 25, 36, 49, 64, 81] ``` ```python reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4) >>> from functools import reduce >>> def add(x, y): ... return x + y ... >>> reduce(add, [1, 3, 5, 7, 9]) 25 ``` **filter**: Python 内建的 filter() 函数用于过滤序列: ```python >>> def is_odd(n): ... return n % 2 == 1 ... >>> list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) [1, 5, 9, 15] ``` **sorted**: Python 内置的 sorted() 函数就可以对 list 进行排序: ```python sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36] ``` ---- **函数作为返回值**: 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回: ```python def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum >>> f = lazy_sum(1, 3, 5, 7, 9) >>> f <function lazy_sum.<locals>.sum at 0x101c6ed90> >>> f() 25 ``` **闭包 与 nonlocal**: ```python def inc(): x = 0 def fn(): nonlocal x x = x + 1 return x return fn f = inc() print(f()) # 1 print(f()) # 2 ``` **匿名函数 lambda**: `lambda x: x * x` 实际上就是: ```python def f(x): return x * x ``` 用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数: ```python >>> f = lambda x: x * x >>> f <function <lambda> at 0x101c6ef28> >>> f(5) 25 def build(x, y): return lambda: x * x + y * y ``` ---- **装饰器**: 函数对象有一个`__name__`属性(注意:是前后各两个下划线),可以拿到函数的名字: ```python >>> def now(): ... print('2015-3-25') ... >>> f = now >>> f() 2015-3-25 >>> now.__name__ 'now' >>> f.__name__ 'now' ``` 代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。 `wrapper()` 函数的参数定义是 `(*args, **kw)` ,因此,`wrapper()` 函数可以接受任意参数的调用。 ```python def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper @log def now(): print('2015-3-25') >>> now() call now(): 2015-3-25 ``` 带参数的装饰器: ```python def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator @log('execute') def now(): print('2015-3-25') >>> now() execute now(): 2015-3-25 ``` `__name__` 等属性的处理: ```python import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper ``` 在面向对象(OOP)的设计模式中,decorator 被称为装饰模式。OOP 的装饰模式需要通过继承和组合来实现,而 Python 除了能支持 OOP 的 decorator 外,直接从语法层次支持 decorator 。Python 的 decorator 可以用函数实现,也可以用类实现。 **偏函数**: 当函数的参数个数太多,需要简化时,使用 **`functools.partial`偏函数** 可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。 ```python >>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85 # 也可以在函数调用时传入其他值 >>> int2('1000000', base=10) 1000000 ``` `functools.partial` 偏函数 的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。 当作为位置参数时,默认加在左边: ```python max2 = functools.partial(max, 10) max2(5, 6, 7) args = (10, 5, 6, 7) max(*args) ``` ---- #### 模块 在 Python 中,一个 `.py` 文件就称之为一个模块(Module)。 使用模块最大的好处是**大大提高了代码的可维护性**。其次,**编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用**。我们在编写程序的时候,也经常引用其他模块,包括 Python 内置的模块和来自第三方的模块。 使用模块还**可以避免函数名和变量名冲突**。但是也要注意,尽量不要与内置函数名字冲突。 为了避免模块名冲突,Python 又引入了按目录来组织模块的方法,称为**包(Package)**。 ~~~ mycompany ├─ __init__.py ├─ abc.py └─ xyz.py ~~~ 请注意,每一个包目录下面都会有一个 `__init__.py` 的文件,这个文件是必须存在的,否则,Python 就把这个目录当成普通目录,而不是一个包。`__init__.py` 可以是空文件,也可以有 Python 代码,因为`__init__.py` 本身就是一个模块,而它的模块名就是`mycompany`。 多级目录结构: ~~~ mycompany ├─ web │ ├─ __init__.py │ ├─ utils.py │ └─ www.py ├─ __init__.py ├─ abc.py └─ utils.py ~~~ 文件`www.py`的模块名就是`mycompany.web.www`,两个文件`utils.py`的模块名分别是`mycompany.utils`和`mycompany.web.utils`。 **定义模块**: `hello.py` ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- ' a test module ' __author__ = 'Michael Liao' import sys def test(): args = sys.argv if len(args)==1: print('Hello, world!') elif len(args)==2: print('Hello, %s!' % args[1]) else: print('Too many arguments!') if __name__=='__main__': test() ``` 当我们在命令行运行 hello 模块文件时,Python 解释器把一个特殊变量 `__name__` 置为`__main__`,而如果在其他地方导入该 hello 模块时,if判断将失败,因此,这种 `if` 测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。 **作用域**: 类似`_xxx`和`__xxx`这样的函数或变量就是非公开的(private),不应该被直接引用,比如`_abc`,`__abc`等; 之所以我们说,private 函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为 Python 并没有一种方法可以完全限制访问 private 函数或变量,但是,从编程习惯上不应该引用 private 函数或变量。 **模块导入**: ``` 从 xxx 导入 aaa ``` ```python from collections.abc import Iterable ``` python import 相当于 php 中的 `require_once`,**只会引入、执行一次。** ---- #### 面向对象 [Python语法笔记:魔术方法 - 知乎](https://zhuanlan.zhihu.com/p/619847950?utm_id=0) 面向对象的抽象程度又比函数要高,因为一个 Class 既包含数据,又包含操作数据的方法。 类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响。 方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据。 ```python class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score)) ``` **访问限制**: 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线`__`,在 Python 中,实例的变量名如果以`__`开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把 Student 类改一改: ```python class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score)) ``` 改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问`实例变量.__name`和`实例变量.__score`了: ```python >>> bart = Student('Bart Simpson', 59) >>> bart.__name Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute '__name' ``` **继承和多态**: 继承,最大的好处是子类获得了父类的全部功能。 多态,子类可以覆盖父类的方法,从而能够实现让子类在一致的规则接口下实现不同的功能。 ```python class Animal(object): def run(self): print('Animal is running...') class Dog(Animal): pass class Cat(Animal): pass ``` Python 动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。 **获取对象信息**: ```python >>> type(123)==type(456) True >>> type(123)==int True >>> type('abc')==type('123') True >>> type('abc')==str True >>> type('abc')==type(123) False ``` ```python >>> import types >>> def fn(): ... pass ... >>> type(fn)==types.FunctionType True >>> type(abs)==types.BuiltinFunctionType True >>> type(lambda x: x)==types.LambdaType True >>> type((x for x in range(10)))==types.GeneratorType True ``` ```python # object -> Animal -> Dog -> Husky >>> a = Animal() >>> d = Dog() >>> h = Husky() >>> isinstance(h, Husky) True >>> isinstance(h, Dog) True >>> isinstance(h, Animal) True >>> isinstance(d, Husky) False ``` ```python >>> isinstance('a', str) True >>> isinstance(123, int) True >>> isinstance(b'a', bytes) True ``` ```python # 判断一个变量是否是某些类型中的一种 >>> isinstance([1, 2, 3], (list, tuple)) True >>> isinstance((1, 2, 3), (list, tuple)) True ``` > 总是优先使用 isinstance() 判断类型,可以将指定类型及其子类“一网打尽”。 **使用 dir()**: 要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的 list : ```python >>> dir('ABC') ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill'] ``` 类似`__xxx__`的属性和方法在Python中都是有特殊用途的: ```python >>> len('ABC') 3 >>> 'ABC'.__len__() 3 ``` 我们自己写的类,如果也想用 len(myObj) 的话,就自己写一个`__len__()`方法: ```python >>> class MyDog(object): ... def __len__(self): ... return 100 ... >>> dog = MyDog() >>> len(dog) 100 ``` **etattr()、setattr()、hasattr()**: ```python >>> getattr(obj, 'z') # 获取属性'z' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'MyObject' object has no attribute 'z' >>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404 404 >>> hasattr(obj, 'power') # 有属性'power'吗? True >>> getattr(obj, 'power') # 获取属性'power' <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn >>> fn # fn指向obj.power <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn() # 调用fn()与调用obj.power()是一样的 81 ``` **实例属性和类属性**: > 属性 分为类属性 和 实例属性,这和其它语言,比如 php 中的类、对象就有所区别。 当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到: ```python >>> class Student(object): ... name = 'Student' ... >>> s = Student() # 创建实例s >>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性 Student >>> print(Student.name) # 打印类的name属性 Student >>> s.name = 'Michael' # 给实例绑定name属性 >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性 Michael >>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问 Student >>> del s.name # 如果删除实例的name属性 >>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了 Student ``` ---- #### 高级面向对象编程 **__slots__:定义允许绑定的属性** ```python class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 ``` ```python >>> s = Student() # 创建新的实例 >>> s.name = 'Michael' # 绑定属性'name' >>> s.age = 25 # 绑定属性'age' >>> s.score = 99 # 绑定属性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score' ``` 使用 `__slots__` 要注意,`__slots__` 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。 除非在子类中也定义`__slots__`,这样,子类实例允许定义的属性就是自身的`__slots__`加上父类的`__slots__`。 **`@property`装饰器就是把一个方法变成属性调用:** ```python class Student(object): @property def score(self): return self._score # @property 本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值 @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value # 只定义getter方法,就是一个只读属性 @property def age(self): return 2015 - self._birth ``` >[tip] 需要注意的是,属性的方法名不要和实例变量重名,否则造成无限递归,最终导致栈溢出报错`RecursionError`。 ---- **多重继承**: 通过多重继承,一个子类就可以同时获得多个父类的所有功能。 ```python # 奔跑的哺乳动物,例如 狗: class Dog(Mammal, Runnable): pass # 可以飞翔的哺乳动物,例如 蝙蝠: class Bat(Mammal, Flyable): pass # 不能飞翔的鸟类,例如 鸵鸟: class Ostrich(Bird, Runnable): pass # 可以飞翔的鸟类,例如 鹦鹉: class Parrot(Bird, Flyable): pass ``` **混合 MixIn**: 多重继承的设计通常称之为 MixIn ,为了更好地看出继承关系,我们把`Runnable`和`Flyable`改为`RunnableMixIn`和`FlyableMixIn`。类似的,你还可以定义出肉食动物`CarnivorousMixIn`和植食动物`HerbivoresMixIn`,让某个动物同时拥有好几个`MixIn`: ```python class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass # 比如,编写一个多进程模式的TCP服务,定义如下: class MyTCPServer(TCPServer, ForkingMixIn): pass # 编写一个多线程模式的UDP服务,定义如下: class MyUDPServer(UDPServer, ThreadingMixIn): pass # 如果你打算搞一个更先进的协程模型,可以编写一个 CoroutineMixIn: class MyTCPServer(TCPServer, CoroutineMixIn): pass ``` 一个类继承一个主类,其他都是 `MixIn`。 ---- **定制类**: `__str__` 输出实例 时的魔术方法: ```python class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__ ``` `__iter__` 如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。 ```python class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 ``` `__getitem__` Fib实例虽然能作用于 for 循环,看起来和 list 有点像,但是,把它当成 list 来使用还是不行,比如,取第5个元素: ```python class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a >>> f = Fib() >>> f[0] 1 >>> f[1] 1 >>> f[2] 2 >>> f[3] 3 >>> f[10] 89 >>> f[100] 573147844013817084101 ``` `__getattr__` 当调用不存在的属性时,`__get` 魔术方法: ```python class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr) ``` `__call__` 调用实例本身: ```python class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name) >>> s = Student('Michael') >>> s() # self参数不要传入 My name is Michael. ``` ```python >>> callable(Student()) True >>> callable(max) True >>> callable([1, 2, 3]) False >>> callable(None) False >>> callable('str') False ``` 通过 callable() 函数,我们就可以判断一个对象是否是“可调用”对象。 还有很多可定制的方法,请参考 Python 的官方文档 [3. 数据模型 - 特殊方法名称 — Python 3.11.3 文档](https://docs.python.org/zh-cn/3/reference/datamodel.html#special-method-names)。 ---- **枚举类**: ```python from enum import Enum, unique @unique class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 ``` **`metaclass` 元类:** 根据 metaclass 创建出类: ```python # metaclass是类的模板,所以必须从`type`类型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) class MyList(list, metaclass=ListMetaclass): pass >>> L = MyList() >>> L.add(1) >> L [1] ``` metaclass 是 Python 中非常具有魔术性的对象,**它可以改变类创建时的行为**。这种强大的功能使用起来务必小心。 ---- #### 错误处理 **错误、异常** ```python try: print('try...') r = 10 / 0 print('result:', r) except ZeroDivisionError as e: print('except:', e) finally: print('finally...') print('END') try... except: division by zero finally... END ``` **抛出错误:** 错误并不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。 ```python class FooError(ValueError): pass def foo(s): n = int(s) if n==0: raise FooError('invalid value: %s' % s) return 10 / n foo('0') ``` Python 内置的 `try...except...finally` 用来处理错误十分方便。出错时,会分析错误信息并定位错误发生的代码位置才是最关键的。 程序也可以主动抛出错误,让调用者来处理相应的错误。但是,**应该在文档中写清楚可能会抛出哪些错误,以及错误产生的原因。** [内置异常 — Python 3.11.3 文档](https://docs.python.org/zh-cn/3/library/exceptions.html#exception-hierarchy) [优雅地处理 Python 中的异常?Merry工具包做到了! - 知乎](https://zhuanlan.zhihu.com/p/390111195) **日志调试** logging 模块 [Python中logging模块的基本用法 | 静觅](https://cuiqingcai.com/6080.html) > 任何一款软件如果没有标准的日志记录,都不能算作一个合格的软件。作为开发者,我们需要重视并做好日志记录过程。 ~~~ Logger -> Log Record -> Filter -> Formatter -> Handler DEBUG > INFO > WARNING > ERROR > CRITICAL ~~~ [python中logging日志模块详解 - 咸鱼也是有梦想的 - 博客园](https://www.cnblogs.com/xianyulouie/p/11041777.html) [python logging设置颜色-掘金](https://juejin.cn/s/python%20logging%E8%AE%BE%E7%BD%AE%E9%A2%9C%E8%89%B2) [xolox/python-coloredlogs: Colored terminal output for Python's logging module](https://github.com/xolox/python-coloredlogs) [python的logging日志模块_logging.basicconfig(level=logging.debug)_FlyLikeButterfly的博客-CSDN博客](https://blog.csdn.net/FlyLikeButterfly/article/details/120223112) > `[%(levelname)-8s]` 宽度对齐 [Rich:Python开发者的完美终端工具! - 知乎](https://zhuanlan.zhihu.com/p/394105084) [rich/README.cn.md at master · Textualize/rich](https://github.com/textualize/rich/blob/master/README.cn.md) > https://github.com/Textualize/rich/issues/988 颜色有坑,暂时不用,先用标准的日志吧 ---- **单元测试**: 单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。 如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。 单元测试通过后有什么意义呢?如果我们对abs()函数代码做了修改,只需要再跑一遍单元测试,如果通过,说明我们的修改不会对abs()函数原有的行为造成影响,如果测试不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。 这种以测试为驱动的开发模式最大的好处就是**确保一个程序模块的行为符合我们设计的测试用例**。在将来修改的时候,可以极大程度地保证该模块行为仍然是正确的。 单元测试的测试用例要覆盖常用的输入组合、**边界条件和异常**。 单元测试通过了并不意味着程序就没有bug了,但是不通过程序肯定有bug。 ```python import unittest from mydict import Dict class TestDict(unittest.TestCase): def test_init(self): d = Dict(a=1, b='test') self.assertEqual(d.a, 1) self.assertEqual(d.b, 'test') self.assertTrue(isinstance(d, dict)) def test_key(self): d = Dict() d['key'] = 'value' self.assertEqual(d.key, 'value') def test_attr(self): d = Dict() d.key = 'value' self.assertTrue('key' in d) self.assertEqual(d['key'], 'value') def test_keyerror(self): d = Dict() with self.assertRaises(KeyError): value = d['empty'] def test_attrerror(self): d = Dict() with self.assertRaises(AttributeError): value = d.empty ``` **文档测试**: 自动执行写在注释中的代码,doctest 严格按照 Python 交互式命令行的输入和输出来判断测试结果是否正确。只有测试异常的时候,可以用 ... 表示中间一大段烦人的输出: ```python # mydict2.py class Dict(dict): ''' Simple dict but also support access as x.y style. >>> d1 = Dict() >>> d1['x'] = 100 >>> d1.x 100 >>> d1.y = 200 >>> d1['y'] 200 >>> d2 = Dict(a=1, b=2, c='3') >>> d2.c '3' >>> d2['empty'] Traceback (most recent call last): ... KeyError: 'empty' >>> d2.empty Traceback (most recent call last): ... AttributeError: 'Dict' object has no attribute 'empty' ''' def __init__(self, **kw): super(Dict, self).__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'Dict' object has no attribute '%s'" % key) def __setattr__(self, key, value): self[key] = value if __name__=='__main__': import doctest doctest.testmod() ``` ---- #### 进程管理 **IO编程**: IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和新浪服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。 ---- **异步IO**: [Python 异步编程入门 - 阮一峰的网络日志](https://www.ruanyifeng.com/blog/2019/11/python-asyncio.html) **网络编程**: **进程、线程**: ---- #### SQLAlchemy [SQLAlchemy文档 — SQLAlchemy 1.4 Documentation](https://www.osgeo.cn/sqlalchemy/) [用 peewee 代替 SQLAlchemy - Jiajun 的编程随想](https://jiajunhuang.com/articles/2020_05_29-use_peewee.md.html) [peewee — peewee 3.15.3 文档](https://www.osgeo.cn/peewee/) ---- #### Scrapy [Scrapy 教程 — Scrapy 2.5.0 文档](https://www.osgeo.cn/scrapy/intro/tutorial.html) ```shell pip3 install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple ``` ```shell (tutorial-env) PS D:\web\tutorial-env> python.exe -m pip install --upgrade pip Requirement already satisfied: pip in d:\web\tutorial-env\lib\site-packages (22.3.1) Collecting pip Using cached pip-23.1-py3-none-any.whl (2.1 MB) Installing collected packages: pip Attempting uninstall: pip Found existing installation: pip 22.3.1 Uninstalling pip-22.3.1: Successfully uninstalled pip-22.3.1 Successfully installed pip-23.1 (tutorial-env) PS D:\web\tutorial-env> pip install scrapy Collecting scrapy Downloading Scrapy-2.8.0-py2.py3-none-any.whl (272 kB) ---------------------------------------- 272.9/272.9 kB 884.5 kB/s eta 0:00:00 Collecting Twisted>=18.9.0 (from scrapy) Downloading Twisted-22.10.0-py3-none-any.whl (3.1 MB) ---------------------------------------- 3.1/3.1 MB 1.9 MB/s eta 0:00:00 Collecting cryptography>=3.4.6 (from scrapy) Downloading cryptography-40.0.2-cp36-abi3-win_amd64.whl (2.6 MB) ---------------------------------------- 2.6/2.6 MB 2.0 MB/s eta 0:00:00 Collecting cssselect>=0.9.1 (from scrapy) Downloading cssselect-1.2.0-py2.py3-none-any.whl (18 kB) Collecting itemloaders>=1.0.1 (from scrapy) Downloading itemloaders-1.0.6-py3-none-any.whl (11 kB) Collecting parsel>=1.5.0 (from scrapy) Downloading parsel-1.8.1-py2.py3-none-any.whl (17 kB) Collecting pyOpenSSL>=21.0.0 (from scrapy) Downloading pyOpenSSL-23.1.1-py3-none-any.whl (57 kB) ---------------------------------------- 57.9/57.9 kB 610.9 kB/s eta 0:00:00 Collecting queuelib>=1.4.2 (from scrapy) Downloading queuelib-1.6.2-py2.py3-none-any.whl (13 kB) Collecting service-identity>=18.1.0 (from scrapy) Downloading service_identity-21.1.0-py2.py3-none-any.whl (12 kB) Collecting w3lib>=1.17.0 (from scrapy) Downloading w3lib-2.1.1-py3-none-any.whl (21 kB) Collecting zope.interface>=5.1.0 (from scrapy) Downloading zope.interface-6.0-cp311-cp311-win_amd64.whl (204 kB) ---------------------------------------- 204.1/204.1 kB 1.6 MB/s eta 0:00:00 Collecting protego>=0.1.15 (from scrapy) Downloading Protego-0.2.1-py2.py3-none-any.whl (8.2 kB) Collecting itemadapter>=0.1.0 (from scrapy) Downloading itemadapter-0.8.0-py3-none-any.whl (11 kB) Requirement already satisfied: setuptools in d:\web\tutorial-env\lib\site-packages (from scrapy) (65.5.0) Collecting packaging (from scrapy) Downloading packaging-23.1-py3-none-any.whl (48 kB) ---------------------------------------- 48.9/48.9 kB 1.2 MB/s eta 0:00:00 Collecting tldextract (from scrapy) Downloading tldextract-3.4.0-py3-none-any.whl (93 kB) ---------------------------------------- 93.9/93.9 kB 1.8 MB/s eta 0:00:00 Collecting lxml>=4.3.0 (from scrapy) Downloading lxml-4.9.2-cp311-cp311-win_amd64.whl (3.8 MB) ---------------------------------------- 3.8/3.8 MB 1.7 MB/s eta 0:00:00 Collecting PyDispatcher>=2.0.5 (from scrapy) Downloading PyDispatcher-2.0.7-py3-none-any.whl (12 kB) Collecting cffi>=1.12 (from cryptography>=3.4.6->scrapy) Downloading cffi-1.15.1-cp311-cp311-win_amd64.whl (179 kB) ---------------------------------------- 179.0/179.0 kB 1.5 MB/s eta 0:00:00 Collecting jmespath>=0.9.5 (from itemloaders>=1.0.1->scrapy) Downloading jmespath-1.0.1-py3-none-any.whl (20 kB) Collecting six (from protego>=0.1.15->scrapy) Downloading six-1.16.0-py2.py3-none-any.whl (11 kB) Collecting attrs>=19.1.0 (from service-identity>=18.1.0->scrapy) Downloading attrs-23.1.0-py3-none-any.whl (61 kB) ---------------------------------------- 61.2/61.2 kB 821.3 kB/s eta 0:00:00 Collecting pyasn1-modules (from service-identity>=18.1.0->scrapy) Downloading pyasn1_modules-0.3.0-py2.py3-none-any.whl (181 kB) ---------------------------------------- 181.3/181.3 kB 1.6 MB/s eta 0:00:00 Collecting pyasn1 (from service-identity>=18.1.0->scrapy) Downloading pyasn1-0.5.0-py2.py3-none-any.whl (83 kB) ---------------------------------------- 83.9/83.9 kB 1.6 MB/s eta 0:00:00 Collecting constantly>=15.1 (from Twisted>=18.9.0->scrapy) Downloading constantly-15.1.0-py2.py3-none-any.whl (7.9 kB) Collecting incremental>=21.3.0 (from Twisted>=18.9.0->scrapy) Downloading incremental-22.10.0-py2.py3-none-any.whl (16 kB) Collecting Automat>=0.8.0 (from Twisted>=18.9.0->scrapy) Downloading Automat-22.10.0-py2.py3-none-any.whl (26 kB) Collecting hyperlink>=17.1.1 (from Twisted>=18.9.0->scrapy) Downloading hyperlink-21.0.0-py2.py3-none-any.whl (74 kB) ---------------------------------------- 74.6/74.6 kB 2.0 MB/s eta 0:00:00 Collecting typing-extensions>=3.6.5 (from Twisted>=18.9.0->scrapy) Downloading typing_extensions-4.5.0-py3-none-any.whl (27 kB) Collecting twisted-iocpsupport<2,>=1.0.2 (from Twisted>=18.9.0->scrapy) Downloading twisted_iocpsupport-1.0.3-cp311-cp311-win_amd64.whl (39 kB) Collecting idna (from tldextract->scrapy) Downloading idna-3.4-py3-none-any.whl (61 kB) ---------------------------------------- 61.5/61.5 kB 1.7 MB/s eta 0:00:00 Collecting requests>=2.1.0 (from tldextract->scrapy) Downloading requests-2.28.2-py3-none-any.whl (62 kB) ---------------------------------------- 62.8/62.8 kB 1.6 MB/s eta 0:00:00 Collecting requests-file>=1.4 (from tldextract->scrapy) Downloading requests_file-1.5.1-py2.py3-none-any.whl (3.7 kB) Collecting filelock>=3.0.8 (from tldextract->scrapy) Downloading filelock-3.12.0-py3-none-any.whl (10 kB) Collecting pycparser (from cffi>=1.12->cryptography>=3.4.6->scrapy) Downloading pycparser-2.21-py2.py3-none-any.whl (118 kB) ---------------------------------------- 118.7/118.7 kB 2.4 MB/s eta 0:00:00 Collecting charset-normalizer<4,>=2 (from requests>=2.1.0->tldextract->scrapy) Downloading charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl (96 kB) ---------------------------------------- 96.7/96.7 kB 1.8 MB/s eta 0:00:00 Collecting urllib3<1.27,>=1.21.1 (from requests>=2.1.0->tldextract->scrapy) Downloading urllib3-1.26.15-py2.py3-none-any.whl (140 kB) ---------------------------------------- 140.9/140.9 kB 1.4 MB/s eta 0:00:00 Collecting certifi>=2017.4.17 (from requests>=2.1.0->tldextract->scrapy) Downloading certifi-2022.12.7-py3-none-any.whl (155 kB) ---------------------------------------- 155.3/155.3 kB 2.3 MB/s eta 0:00:00 Installing collected packages: twisted-iocpsupport, PyDispatcher, incremental, constantly, zope.interface, w3lib, urllib3, typing-extensions, six, queuelib, pycparser, pyasn1, packaging, lxml, jmespath, itemadapter, idna, filelock, cssselect, charset-normalizer, certifi, attrs, requests, pyasn1-modules, protego, parsel, hyperlink, cffi, Automat, Twisted, requests-file, itemloaders, cryptography, tldextract, service-identity, pyOpenSSL, scrapy Successfully installed Automat-22.10.0 PyDispatcher-2.0.7 Twisted-22.10.0 attrs-23.1.0 certifi-2022.12.7 cffi-1.15.1 charset-normalizer-3.1.0 constantly-15.1.0 cryptography-40.0.2 cssselect-1.2.0 filelock-3.12.0 hyperlink-21.0.0 idna-3.4 incremental-22.10.0 itemadapter-0.8.0 itemloaders-1.0.6 jmespath-1.0.1 lxml-4.9.2 packaging-23.1 parsel-1.8.1 protego-0.2.1 pyOpenSSL-23.1.1 pyasn1-0.5.0 pyasn1-modules-0.3.0 pycparser-2.21 queuelib-1.6.2 requests-2.28.2 requests-file-1.5.1 scrapy-2.8.0 service-identity-21.1.0 six-1.16.0 tldextract-3.4.0 twisted-iocpsupport-1.0.3 typing-extensions-4.5.0 urllib3-1.26.15 w3lib-2.1.1 zope.interface-6.0 ``` ---- **如何爬取更多链接?** 虽然爬虫是**从一个入口链接开始**的,但不要因此就认为它只能完成一次性的简单爬取任务,我们可在 `parse()` 中根据情况使用 `response.follow(next_page, self.parse)` 、`yield scrapy.Request(next_page, callback=self.parse)` **继续生成其他请求,以满足爬取所有其他页面。** ---- **如何处理和保存爬取到的数据?** ---- **如何使用代理?** ---- **如何分布式大规模爬取?** ---- **如何处理登录?** ---- **如何处理验证码?** ---- **如何处理滑块等防爬人机验证?** ---- **如何处理加密防爬?** ---- **如何使用无头浏览器?** ---- **如何管理、控制爬虫?** ---- #### 常用模块与三方包 **pip 源镜像** ~~~ 清华大学 :https://pypi.tuna.tsinghua.edu.cn/simple/ 阿里云:http://mirrors.aliyun.com/pypi/simple/ 中国科学技术大学 :http://pypi.mirrors.ustc.edu.cn/simple/ 华中科技大学:http://pypi.hustunique.com/ 豆瓣源:http://pypi.douban.com/simple/ 腾讯源:http://mirrors.cloud.tencent.com/pypi/simple 华为镜像源:https://repo.huaweicloud.com/repository/pypi/simple ~~~ ```shell pip3 install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple ``` [pip源_淘小欣的博客-CSDN博客](https://blog.csdn.net/weixin_44621343/article/details/116459859) ---- #### venv 为一个应用创建一套“隔离”的 Python 运行环境,使用不同的虚拟环境可以解决不同应用的依赖冲突问题。 ```shell # 创建虚拟环境 python -m venv venv # 激活虚拟环境 source venv/bin/activate ``` [12. 虚拟环境和包 — Python 3.11.3 文档](https://docs.python.org/zh-cn/3/tutorial/venv.html#tut-venv) **windows 环境**: 以**管理员身份**运行 Windows PowerShell : ```shell PS D:\web\tutorial-env> set-executionpolicy remotesigned PS D:\web\tutorial-env> get-executionpolicy RemoteSigned PS D:\web\tutorial-env> .\Scripts\activate (tutorial-env) PS D:\web\tutorial-env> python3 Python 3.11.2 (tags/v3.11.2:878ead1, Feb 7 2023, 16:38:35) [MSC v.1934 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.path ['', 'D:\\Program Files\\Python311\\python311.zip', 'D:\\Program Files\\Python311\\DLLs', 'D:\\Program Files\\Python311\\Lib', 'D:\\Program Files\\Python311', 'D:\\web\\tutorial-env', 'D:\\web\\tutorial-env\\Lib\\site-packages'] ``` [环境搭建 Python Windows中使用venv - 简书](https://www.jianshu.com/p/eb08e9198387) [Python3.10.4激活venv环境失败解决方法_python_脚本之家](https://www.jb51.net/article/272801.htm) [Python3.10.4激活venv环境失败解决方法](https://it.cha138.com/android/show-73531.html) ---- #### 其他 **正则表达式**: [re --- 正则表达式操作 — Python 3.11.3 文档](https://docs.python.org/zh-cn/3/library/re.html) ---- #### pymysql 参数绑定 ```python cursor.execute(' select ... %s ', []) ``` 使用参数绑定而不是 sql 拼接,这是防止 SQL 注入 的最安全的方法。(注意使用参数绑定时,`%s` 占位符不需要使用引号) https://www.cnpython.com/qa/194936 ---- #### windows 使用 pyautogui 时要注意的 windows 使用 pyautogui 时,不能关掉链接,甚至小窗远程连接也不行 https://www.cnblogs.com/sophia201552/p/13344320.html 按这个方法也不行 > 所以我们通常使用一台机子当作“监控机” ---- #### 注意库模块的隐式引用 ```python C:\Python27\python.exe D:/wamp64/www/xiak-DataValley/test_xiak/t.py Traceback (most recent call last): File "D:/wamp64/www/xiak-DataValley/test_xiak/t.py", line 4, in <module> from selenium import webdriver File "C:\Python27\lib\site-packages\selenium\webdriver\__init__.py", line 27, in <module> from .safari.webdriver import WebDriver as Safari # noqa File "C:\Python27\lib\site-packages\selenium\webdriver\safari\webdriver.py", line 20, in <module> import http.client as http_client File "D:\wamp64\www\xiak-DataValley\test_xiak\http.py", line 17 str = json.dumps(cookies) ^ IndentationError: expected an indented block ``` `C:\Python27\lib\site-packages\selenium\webdriver\safari\webdriver.py` 第19行还有这样的代码: ```python try: import http.client as http_client except ImportError: import httplib as http_client ``` 而项目目录 刚好有 D:\wamp64\www\xiak-DataValley\test_xiak\http.py 这个文件,所以被当作模块引用了,看来在不了解所使用的库时,不能随便定义文件模块啊。这有点类似于php中的依赖注入,但这个竟然是隐式的。 ---- [非常详细的字符编码讲解,ASCII、GB2312、GBK、Unicode、UTF-8等知识点都有](https://www.bilibili.com/video/BV1gZ4y1x7p7) [非常生动的Python2和Python3的编解码讲解](https://www.bilibili.com/video/BV1XK4y1t7D4) [HelloDjango - django REST framework 教程_追梦人物的博客](https://www.zmrenwu.com/courses/django-rest-framework-tutorial/) ---- last update: 2020-11-23 10:27:18