## 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
- 开始
- 公益
- 更好的使用看云
- 推荐书单
- 优秀资源整理
- 技术文章写作规范
- SublimeText - 编码利器
- PSR-0/PSR-4命名标准
- php的多进程实验分析
- 高级PHP
- 进程
- 信号
- 事件
- IO模型
- 同步、异步
- socket
- Swoole
- PHP扩展
- Composer
- easyswoole
- php多线程
- 守护程序
- 文件锁
- s-socket
- aphp
- 队列&并发
- 队列
- 讲个故事
- 如何最大效率的问题
- 访问式的web服务(一)
- 访问式的web服务(二)
- 请求
- 浏览器访问阻塞问题
- Swoole
- 你必须理解的计算机核心概念 - 码农翻身
- CPU阿甘 - 码农翻身
- 异步通知,那我要怎么通知你啊?
- 实时操作系统
- 深入实时 Linux
- Redis 实现队列
- redis与队列
- 定时-时钟-阻塞
- 计算机的生命
- 多进程/多线程
- 进程通信
- 拜占庭将军问题深入探讨
- JAVA CAS原理深度分析
- 队列的思考
- 走进并发的世界
- 锁
- 事务笔记
- 并发问题带来的后果
- 为什么说乐观锁是安全的
- 内存锁与内存事务 - 刘小兵2014
- 加锁还是不加锁,这是一个问题 - 码农翻身
- 编程世界的那把锁 - 码农翻身
- 如何保证万无一失
- 传统事务与柔性事务
- 大白话搞懂什么是同步/异步/阻塞/非阻塞
- redis实现锁
- 浅谈mysql事务
- PHP异常
- php错误
- 文件加载
- 路由与伪静态
- URL模式之分析
- 字符串处理
- 正则表达式
- 数组合并与+
- 文件上传
- 常用验证与过滤
- 记录
- 趣图
- foreach需要注意的问题
- Discuz!笔记
- 程序设计思维
- 抽象与具体
- 配置
- 关于如何学习的思考
- 编程思维
- 谈编程
- 如何安全的修改对象
- 临时
- 临时笔记
- 透过问题看本质
- 程序后门
- 边界检查
- session
- 安全
- 王垠
- 第三方数据接口
- 验证码问题
- 还是少不了虚拟机
- 程序员如何谈恋爱
- 程序员为什么要一直改BUG,为什么不能一次性把代码写好?
- 碎碎念
- 算法
- 实用代码
- 相对私密与绝对私密
- 学习目标
- 随记
- 编程小知识
- foo
- 落盘
- URL编码的思考
- 字符编码
- Elasticsearch
- TCP-IP协议
- 碎碎念2
- Grafana
- EFK、ELK
- RPC
- 依赖注入
- 开发笔记
- 经纬度格式转换
- php时区问题
- 解决本地开发时调用远程AIP跨域问题
- 后期静态绑定
- 谈tp的跳转提示页面
- 无限分类问题
- 生成微缩图
- MVC名词
- MVC架构
- 也许模块不是唯一的答案
- 哈希算法
- 开发后台
- 软件设计架构
- mysql表字段设计
- 上传表如何设计
- 二开心得
- awesomes-tables
- 安全的代码部署
- 微信开发笔记
- 账户授权相关
- 小程序获取是否关注其公众号
- 支付相关
- 提交订单
- 微信支付笔记
- 支付接口笔记
- 支付中心开发
- 下单与支付
- 支付流程设计
- 订单与支付设计
- 敏感操作验证
- 排序设计
- 代码的运行环境
- 搜索关键字的显示处理
- 接口异步更新ip信息
- 图片处理
- 项目搭建
- 阅读文档的新方式
- mysql_insert_id并发问题思考
- 行锁注意事项
- 细节注意
- 如何处理用户的输入
- 不可见的字符
- 抽奖
- 时间处理
- 应用开发实战
- python 学习记录
- Scrapy 教程
- Playwright 教程
- stealth.min.js
- Selenium 教程
- requests 教程
- pyautogui 教程
- Flask 教程
- PyInstaller 教程
- 蜘蛛
- python 文档相似度验证
- thinkphp5.0数据库与模型的研究
- workerman进程管理
- workerman网络分析
- java学习记录
- docker
- 笔记
- kubernetes
- Kubernetes
- PaddlePaddle
- composer
- oneinstack
- 人工智能 AI
- 京东
- pc_detailpage_wareBusiness
- doc
- 电商网站设计
- iwebshop
- 商品规格分析
- 商品属性分析
- tpshop
- 商品规格分析
- 商品属性分析
- 电商表设计
- 设计记录
- 优惠券
- 生成唯一订单号
- 购物车技术
- 分类与类型
- 微信登录与绑定
- 京东到家库存系统架构设计
- crmeb
- 命名规范
- Nginx https配置
- 关于人工智能
- 从人的思考方式到二叉树
- 架构
- 今日有感
- 文章保存
- 安全背后: 浏览器是如何校验证书的
- 避不开的分布式事务
- devops自动化运维、部署、测试的最后一公里 —— ApiFox 云时代的接口管理工具
- 找到自己今生要做的事
- 自动化生活
- 开源与浆果
- Apifox: API 接口自动化测试指南