1. anaconda
[email protected] UI(jk)NM2@
1.1. conda
官方下载安装包:https://www.anaconda.com/download Anaconda Navigtor :用于管理工具包和环境的图形用户界面,后续涉及的众多管理命令也可以在Navigator中手工实现。 Jupyter notebook :基于web的交互式计算环境,可以编辑易于人们阅读的文档,用于展示数据分析的过程。 qtconsole :一个可执行 IPython 的仿终端图形界面程序,相比 Python Shell 界面,qtconsole 可以直接显示代码生成的图形,实现多行代码输入执行,以及内置许多有用的功能和函数。 spyder :一个使用Python语言、跨平台的、科学运算集成开发环境。 找到你对应的操作系统, 然后下载安装即可使用,非常方便. 官方仓库
1.2. 基本命令
# 创建Py3 版本的环境
conda create -n envname python=3
# 分享environment的yaml文件
conda env export > environment.yaml
# 使用yaml文件创建一摸一样的运行环境
conda env create -f environment.yaml
# 查看已经存在的环境
conda-env list
test * C:\Users\ducha\.conda\envs\test
base D:\Anaconda3
ChatGLM-6B D:\Anaconda3\envs\ChatGLM-6B
# 激活环境
conda activate robotframework
# 删除环境
conda-env remove --name envname
# 更新环境
conda-env update -n invokeai -f environment.yml
conda-env update -f environment.yml # 可以不指定name,直接更新yml VS的环境
# 有的时候使用pip配合txt来更新环境
pip install -r .\requirements.txt
# 更新库(关掉所有VPN,使用默认网络连接)
conda update -n base conda
conda update --all
# 查询并安装库
conda install tensorflow
conda install requests
conda install numpy scipy pandas # 安装多个库
conda install numpy=1.10 # 安装一个固定版本的库
conda remove package_name # 删除一个库
conda update package_name # 更新一个库
conda list # 查看所有已经安装的库
conda search search_term # 搜索一个库
# 常用的包
conda install matplotlib 安装 matplotlib 相关的包
conda install psycopg2 # PostgreSQL 驱动包
conda install pysocks # socks 相关包
conda install scikit-learn # 机器学习相关包
1.3. conda使用国内源
# 添加国内源
conda config --help
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
1.4. anaconda-navigator
有的时候更新之后快捷方式就没了,这时可以在终端启动。
anaconda-navigator
2. 基础知识
- Python 程序是一些 .py 文件的集合,这些 py 文件可以叫做模块,模块中包含语句,语句中包含表达式。
- Python 中所有的内容都是对象。
- 序列(Sequence)是最基本的,索引从0开始,列表、元组、字符串、Unicode字符串、buffer对象和xrange对象。
#
使用井号表示注释符,使用 \ 来换行, 缩进相同的一组语句构成一个代码块,统一缩进为4个字符。- 首行以if、while等开头,后面跟着冒号结束。
- Python 发布的时候需要注明,此程序中需要使用的模块都有哪些,提醒用户安装对应的模块。
# -_- coding:utf-8 -_-
通常在文件开头位置指定文件编码格式。- 引号: ’ ’ 单引号、” ” 双引号、''' ''' 三引号,单引号跟双引号可以互相引用,就是双引号里面可以使用单引号,单引号里面也可以使用双引号。三引号里面可以使用多行的字符,可以使用换行,特别方便。
- 字符串
r' '
使用r来标示开头,说明该字符串里面的内容都是原生字符,不会被转义,一般使用在正则表达式上。 - re.compile(reg) 编译正则表达式,可以加速匹配过程。
- 函数调用的时候,脚本最下面添加
__name__=='__main__'
在 main 函数里面从下向上调用,__name__
是当前模块名,当模块被直接运行时模块名为__main__
。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
if __name__ == '__main__':
main()
2.1. 对象引用
在声明一个str类型的变量的时候,变量名可以理解成指针,指向str对象的内存地址。
a = b 对象的赋值都是进行对象引用(内存地址)传递,a和b指向同一个对象。
type(name)查看对象的类型。
实际上变量名就是一个对象e引用。
注意:变量名没有类型,对象才有类型。
2.2. 内置函数
callable(object) 内置函数判断某个函数是否可以调用。
enumerate(list) 使用枚举可以在循环中方便地找到(当前的)索引。
hasattr(object, ‘attr’) 检查是否包含某个属性。
len(object) 返回对象的长度,可自定义。
reversed(object) reverse object return iterator。
type(object) 返回对象的类型信息。
zip(tuple,tuple) 从两个相关的序列构建一个字典。
2.3. 特殊方法与变量
__call__
__getattr__
__iter__
__len__
__solts__
__str__
__file__ # 当前py文件的物理路径,包含文件名和后缀。
http://docs.python.org/3/reference/datamodel.html#special-method-names
2.4. 逻辑操作符
is :判断左边对象引用是否等于右边对象的引用。
< > ⇐ >= != == :比较操作符。
in 和 not in:判断集合中是否存在某个对象。
and or not:与或非
2.5. 关系运算符
== 等于
!= 不等于
<> 不等于?
< 小于
>
大于
<=
小于等于
>=
大于等于
返回布尔值True/False
Copy
2.6. 赋值运算符
=、+=、-+、*=、/=、%=
Copy
2.7. 转换
int(x [,base ]) # 将 x 转换为一个整数
long(x [,base ]) # 将 x 转换为一个长整数
float(x ) # 将x转换到一个浮点数
complex(real [,imag ]) # 创建一个复数
str(x ) # 将对象 x 转换为字符串
repr(x ) # 将对象 x 转换为表达式字符串
eval(str ) # 用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s ) # 将序列 s 转换为一个元组
list(s ) # 将序列 s 转换为一个列表
chr(x ) # 将一个整数转换为一个字符
unichr(x ) # 将一个整数转换为Unicode字符
ord(x ) # 将一个字符转换为它的整数值
hex(x ) # 将一个整数转换为一个十六进制字符串
oct(x ) # 将一个整数转换为一个八进制字符串
# 原地交换两个变量的值
x = 0
y = 1
x, y = y, x
# 结合后的比较操作符
n = 10
res1 = 1 < n < 100 # True
res2 = 1 > n < 100 # False
2.8. global 关键字
在 Python 中,变量不需要先声明就可以直接用,那我们怎么知道它是局部变量还是全局变量呢? Python 中 global 关键字主要作用是声明变量的作用域。 首先 Python 使用的变量,在默认情况下一定是用局部变量。其次 Python 如果想使用作用域之外的全局变量,则需要加 global 前缀。 在Python中,不用 global 的情况:
a = 5
def test():
a = 1
print 'In test func: a = %d' % a
test()
print 'Global a = %d' % a
程序执行结果为: In test func: a = 1 Global a = 5 可以看出,不加 global 的时候,在函数内部是改不了外面的全局变量的(list类型例外)。
下面是使用 global 前缀的情况:
a = 5
def test():
global a # 此处声明,告诉执行引擎:我要用全局变量a,不用局部变量
a = 1
print 'In test func: a = %d' % a
test()
print 'Global a = %d' % a
程序执行结果为: In test func: a = 1 Global a = 1 可以看出,在函数内部成功的修改了全局变量的数值。
2.9. 外部引用
在大项目的时候,可以使用单独的 global 文件。 在同一个文件夹下,新建2个文件: myglobal.py test.py myglobal.py中放置全局变量,内容示例如下:
a = 0
b = 1
c = 2
d = 3
test.py中是测试代码,其中可以使用全局变量
import myglobal
def test():
myglobal.a = 100
print 'myglobal a = %d' % myglobal.a
test()
print 'after test, myglobal a = %d' % myglobal.a
执行test.py的结果如下: myglobal a = 0 after test, myglobal a = 100 OK,同样成功修改了全局变量,在实际使用中,两种方法各有优势,通常我们大多数时候只是用 Python 写小功能的脚本,此时用 global 关键字就够了。如果写比较大的功能应用时,用后一种方法可以使得全局变量的管理更为方便。
2.10. 变量
# 布尔值,在 Python 中,可以直接用True、False表示布尔值(请注意大小写),也可以通过布尔运算计算出来。
# 空值,是 Python 里一个特殊的值,用None表示。None不能理解为0,因为0是有意义的,而None是一个特殊的空值。
a = 99 # 整数变量
b = 123.456 # 浮点变量
c = 'Python' # 字符串变量
d = 3 > 2 # 变量d的值是True,布尔值可以用and、or和not(非)运算
2.11. 字符与编码
# 字符转换
print(ord('杜')) # 对应Unicode字符表,把一个字符转换成整数
print(chr(25991)) # 对应Unicode字符表,把一个整数转换成字符
print('\u4e2d\u6587') # \u + 十六进制数 也可以表示字符串
# Python 的字符串类型是 str,在内存中以 Unicode编码表示,一个字符对应多个字节,
# 如果要在网络上传输,或者保存到磁盘上,就需要把 str 变为 bytes 类型的对象。
str1 = b'Python' # Python对bytes类型的数据用带b前缀的单引号或双引号表示
str2 = "Python"
str3 = str2.encode('ascii') # 以Unicode编码的 str 通过 encode() 方法可以编码为指定的 bytes类型对象。
print(str1.decode('ascii'))
print(str3.decode('ascii'))
# 说明:纯英文的 string 可以用 ASCII 编码为bytes,内容是一样的,含有中文的 str 可以用 UTF-8 编码为bytes。
# 说明:含有中文的 str 无法用ASCII编码,因为中文编码的范围超过了ASCII编码的范围,Python会报错。
# 说明:在bytes中,无法显示为ASCII字符的字节,用\x##显示。
# 说明:如果我们从网络或磁盘上读取了字节流,那么读到的数据就是 bytes。要把 bytes 变为 str ,就需要用decode()方法。
# 说明:len()函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数
# 说明:在操作字符串时,我们经常遇到string和bytes的互相转换,为了避免乱码问题,应当始终坚持使用UTF-8编码对string和bytes进行转换。
2.12. 格式化输出
# 在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。
# 如果只有一个%?,括号可以省略。%f 表示浮点型,如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串
# 有些时候,字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%
a = 123
b = '杜超群'
print('亲爱的,%s,您好,您的话费余额为%s' % (b, a))
print('%2d-%02d' % (3, 1)) # 格式化整数和浮点数还可以指定是否补0和整数与小数的位数
print('%.2f' % 3.1415926) # 格式化整数和浮点数还可以指定是否补0和整数与小数的位数
3. 基础规范
3.1. 命名规则
标识符只能包含字母,数字和下划线,不能以数字开头,且区分大小写。
禁止使用关键字(py2和py3的关键字不一样)。
以一个下划线开头的变量名_par
不会被 from module import * 语句导入,尽量不用。
以两个下划线开头,且以两个下划线结束的变量名(__par__
)是系统定义的变量名,对Python解释器有特殊意义,我们不要这样定义。
以两个下划线开头的变量名(__par
)是类的私有的局部变量。
交互模式下,变量名(_
)用于保存最后表达式的执行结果。
引用模块中的方法,为了避免方法重复,一般使用包名.方法名。
import random # 引入一个包
x = random.choice([1,2,3,4,5,6])
3.2. 文件头部
第一行写 Linux 中解释器的位置
第二行写文件的字符编码
第三行写该文件的说明(docstring),用三引号的方式来写。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
1\. 文件的说明
"""
3.3. 方法说明
定义的方法同样也使用三引号的方式来添加docstring。
参数说明::param connected_sock: socket.pyi
参数类型说明::type connected_sock: socket.pyi
添加了类型说明 PyCharm就会有友好的代码提示。
疑问:socket.pyi 文件是专门用作自动提示的文件?
def handle(connected_sock: socket):
"""
说明
:param connected_sock: 参数说明
:type connected_sock: socket.pyi
:return: 返回说明
"""
while True:
data = connected_sock.recv(BUFFER)
if len(data) > 0:
print('receive:', data)
current_thread = threading.current_thread()
send_data = '{}:{}'.format(current_thread.ident, data)
connected_sock.sendall(send_data)
4. 数据类型
4.1. integer
不可变类型。
num = 1
id(num) # builtins function(内置函数),说明num是不可变的对象,两次的id不一样(内存地址)。
Out[12]: 140728439795456
num = 2 # 指向的内存空间发生变化(对象引用的位置发生变化)
id(num)
Out[14]: 140728439795488
4.2. bytes
Python 3 中二进制数据由 bytes 类型表示。
Python 3 不会以任意隐式的方式混用 str 和 bytes,正是这使得两者的区分特别清晰。
你不能拼接 str 和 bytes,也无法在 bytes 里搜索 str(反之亦然),也不能将str 传入参数为 bytes 的函数(反之亦然)。
如果我们从网络或磁盘上读取了字节流,那么读到的数据就是 bytes 类型
byte_str = b'kkkk' # byte_str 是 Byte 类型的字符串。
str(b'aoye','utf-8') # 可以把 bytes 转换成 unicode 字符串。
bytes.decode(b'aoye') # 可以把 bytes 转换成 unicode 字符串。
bytes('aoye','utf-8') # 可以把 Unicode 字符串转换成 bytes 类型。
4.3. slice
# 切片(Slice)操作符
# 有了切片操作,很多地方循环就不再需要了。
# Python的切片非常灵活,一行代码就可以实现很多行循环才能完成的操作。
list1 = ['one',' two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten']
str1 = '123456789'
tuple1 = (1,2,3,4,5,6,7,8,9)
print(list1[:]) # 输出全部
print(list1[0:3]) # 取下标为 0 个到 3 的元素(不包括3),如果是 0 开始可以省略。
print(list1[3:6]) # 取下标为 3 个到 6 的元素(不包括6)
print(list1[-2:]) # 取最后两个元素,最后一个元素的下标是 -1
print(str1[:3]) # 对于字符串同样可以使用 Slice。
print(list1[:10:2]) # 前10个元素,每隔2个取一个。
print(tuple1[::3]) # tuple 也可以进行切片操作,结果类型仍然是 tuple。
4.4. set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
set 是无序的。
set 中自动去掉重复元素。
# -*- coding: utf-8 -*-
# 要创建一个set,需要提供一个list作为输入集合
s = set([1, 2, 3, 3, 4, 4, 4])
s = {1, 2, 3, 4, 5}
# 添加一个元素,如果是重复的没有效果。
s.add(5)
# 删除元素
s.remove(2)
# 数学意义
s1 = set([1,2,3])
s2 = set([3,4,5])
s = s1 & s2 # 数学意义,取交集
x = s1 | s2 # 数学意义,取并集
4.5. dict
dict 数据类型,相当于其他语言的 Map 类型,使用 key-value存储,查询速度快。 特性:Hash中的数据并不是按照先后顺序来的(数据结构哈希表) 特性:dict 内部存放的顺序和 key 放入的顺序是没有关系的。 特性:dict 查找和插入的速度极快,不会随着key的增加而增加。 特性:dict 需要占用大量的内存,内存浪费多。 特性:list 查找和插入的时间随着元素的增加而增加。 特性:list 占用空间小,浪费内存很少。
# 定义一个空字典
dict = {}
# 定义一个包含两个值的字典
dict = {'one':111, 'two':222}
# 添加一个元素,如果key已经存在,会被覆盖。
dict['key'] = 'vaule'
# 添加一个元素(推荐)如果key在字典里面,什么都不做。
dict.setdefault('d','abcd')
# 取出元素
x = dict['one']
y = dict.get('Chris') # 如果没有元素,返回None
z = dict.get('Chris', -1) # 如果没有元素,返回指定的 -1
a = dict.pop('one') # 取出并在 dict 对象中删除元素。
# 查询元素
if 'one' in d:
print('在字典d中存在:one')
# 遍历key值
for key in dic:
print(key,dic[key])
# 清空字典
dict.clear()
# 创建并返回字典的浅拷贝(对原始字典内存地址的引用)
dict_1 = dict.copy()
# 返回由字典所有键构成的一个列表
list_1 = dic.keys()
# 返回字典所有值构成的一个列表
list_1 = dic.values()
# 返回一个由元组构成的列表,每个元组包含一对键-值对
list_1_tuple2 = dic.items()
# 返回字典键的一个迭代器
iterator_1 = dic.iterkeys()
# 返回字典值的一个迭代器
iterator_1 = dic.itervalues()
# 返回字典键-值对的一个迭代器
iterator_1 = dic.iteritems()
# 删除任意键-值对,并作为两个元素的元组返回。如字典为空,则返回KeyError异常
tuple1 = dic.popitem()
# 将dic2的所有键-值添加到dic1字典中,并覆盖同名键的值
dic.update(dic2)
# 如果key存在于字典中,就返回True;否则返回False
dic.has_key(key)
# dict 转换成 str
mystr = str(dict1)
### **类型转换**
print str(dict) # 转换成 str
print tuple(dict) # 转换成 tuple keys
print tuple(dict.values()) # 转换成 tuple values
print list(dict) # 转换成 list keys
print list(dict.values()) # 转换成 list values
4.6. tuple
不可变的有序集合。
集合内元素的类型可以不相同。
注意:在定义只有一个元素的 tuple 的时候,(1)表示是1这个数!这是因为括号()既可以表示 tuple ,又可以表示数学公式中的小括号,这就产生了歧义,因此,python 规定,这种情况下,按小括号进行计算,计算结果自然是1,所以,只有1个元素的 tuple 定义时必须加一个逗号(1,),来消除歧义。
注意:tuple 本身不可变,但是其包含的 List 对象中的元素是可以改变的。
# 定义一个空的tuple
tuple_0 = ()
# 定义一个只有1个元素的tuple
tuple_1 = (1, )
# 多个元素
tuple_2 = ('Java', 'Python')
# 多种不同元素
tuple_3 = ('Java', 'Python', ['Golang','Ruby'])
# 访问元素也是从下标0开始。
tuple_3[0]
tuple_3[1]
tuple_3[2][0] = '1' # 注意:tuple 中的list对象是可变的
4.6.1. 类型转换
print tup.__str__() # 转换成 str
print list(tup) # 转换成 list
# tuple 不能转换成 dict
4.7. list
列表:使用[]创建,可变对象,存放对象引用。 一种有序的集合,可以随时添加和删除其中的元素。 元素类型可以不同
null_list = [] # 定义一个空列表
list1 = ['张三','李四','王五', 3.1415926, 88] # 定义列表
print(list1) # 打印所有的元素。
print(list1[0]) # 访问list中的第一个元素,索引是从0开始的。
print(list1[-1]) # 使用-1代表引用最后一个元素,-2 代表倒数第二个元素。
print(list1[4]) # 超出下标会提示IndexError: list index out of range
print(len(list1)) # 使用len()函数可以获得list元素的个数。
list1[0]='第一位' # 直接替换索引是0的位置的值
list1.append(element) # 向 list 末尾追加 element 元素。
list1.count(element) # 返回 element 在列表中出现的次数。
list1.insert(1,'插入') # 把元素插入到指定位置,其他的后移。
list1.index(element) # 返回 element 在列表中的索引,如果不存在,则引发ValueError异常
list1.extend(newlist) # 将 newlist 的元素插入列表末尾。
list1.pop() # 取出list末尾的元素,可以存入变量。
list1.pop(0) # 取出指定位置的元素,其他的下标前移。
list1.remove(element) # 删除首次在列表中出现的element,如果列表不存在element,则引发 ValueError异常
list1.reverse() # 反转列表内容(不创建新列表对象,操作的就是当前列表对象)
list1[::-1] # reverse list
reversed(list1) # reverse list return iterator
list1.sort # 当场对列表内容排序,可选参数 compare-function 是一个函数,它指定了比较条件
# (不创建新列表对象,操作的就是当前列表对象)
# 将列表元素赋值给多个变量,元素个数与变量个数必须相等。
list_1 = [1, 2, 3]
x, y, z = list_1
# 使用枚举可以在循环中方便地找到(当前的)索引
for i, value in enumerate(testlist):
print(i, ': ', value)
# 找到列表中出现最频繁的数
test = [1, 2, 3, 4, 2, 2, 3, 1, 4, 4, 4]
print(max(set(test), key=test.count))
4.7.1. 类型转换
print str(nums) # 转换成 str
print tuple(nums) # 转换成 tuple
# 列表不能转换成字典
4.7.2. List Comprehensions(列表生成式)
由列表和 for 循环组成。
# -*- coding: utf-8 -*-
tuple1 = (1, 2, 3, 4, 5, 6, 7, 8, 9)
list1 = ['Scala', 'Python', 'Java', 'js']
dict = {'a': 1, 'b': 2, 'c': 3}
coll = [(1,1),(2,2),(3,3)]
# 生成:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list_1 = list(range(1, 11))
# 生成:[1x1, 2x2, 3x3, ..., 10x10]
list_2 = [x * x for x in range(1, 11)]
# for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方。
list_3 = [x * x for x in range(1, 11) if x % 2 == 0]
# 使用两层循环
list_4 = [m + n for m in 'ABC' for n in 'XYZ']
# ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
# 使用字典
dict_1 = {'x': 'A', 'y': 'B', 'z': 'C' }
list_5 = [k + '=' + v for k, v in dict_1.items()]
# ['y=B', 'x=A', 'z=C']
4.7.3. 列表生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。
而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。
第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
# -*- coding: utf-8 -*-
list_1 = [x * x for x in range(10)]
gene_1 = (x * x for x in range(10))
print(list_1)
print(gene_1)
# generator 保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
print(next(gene_1)) # 一般我们不会傻傻的使用 next 来获取元素内容。
# 通过for循环来迭代它,并且不需要关心StopIteration的错误。
for x in gene_1:
print(x)
# 斐波拉契数列(Fibonacci)
# 如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
fib(6)
# 打印结果:112358
# fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
# 上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
def fib_1(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
gene_1 = fib_1(6)
# 这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
# generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
# 而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
# 但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
while True:
print()
try:
x = next(gene_1)
print('gene_1:', x)
except StopIteration as e:
print('Generator return value:', e.value)
break
4.8. Enum
当我们需要定义常量时,一个办法是用大写变量通过整数来定义。
好处是简单,缺点是类型是int,并且仍然是变量。
更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。
Python提供了 Enum 类来实现这个功能。
if __name__ == '__main__':
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
# value属性则是自动赋给成员的int常量,默认从1开始计数。
print(name, '=>', member, ',', member.value)
4.8.1. 自定义
既可以用成员名称引用枚举常量,又可以直接根据 value 的值获得枚举常量。
Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。
@unique装饰器可以帮助我们检查保证没有重复值。
# !/usr/bin/env python
# -*- coding: utf-8 -*-
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
if __name__ == '__main__':
day1 = Weekday.Sun
print(day1)
print(day1.value)
print(Weekday.Sun)
print(Weekday.Sun.value)
print(Weekday['Sun'])
print(Weekday(0))
if day1 == Weekday.Sun:
print("True")
4.9. Iterable 迭代器
我们已经知道,可以直接作用于for循环的数据类型有以下几种:一类是集合数据类型,如list、tuple、dict、set、str等;一类是generator,包括生成器和带 yield 的 generator function。这些可以直接作用于 for 循环的对象统称为可迭代对象:Iterable。
凡是可作用于 for 循环的对象都是 Iterable 类型;
凡是可作用于 next() 函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列;
集合数据类型如 list、dict、str 等是 Iterable 但不是 Iterator,不过可以通过iter()函数获得一个Iterator对象。
# -*- coding: utf-8 -*-
list1 = ['Scala', 'Python', 'Java', 'js']
print(iter(list1))
5. 运算符
5.1. 基本运算
int 类型是不可变的,实际是创建了一个新的对象,然后变量名指向新的内存对象。
# 基本运算,加减乘除 整除
e = 1 + 2
f = 4 - 1
g = 5 * 5
h = 100 / 2
i = 100 // 9
a = 10
a += 2
a -= 1
a /= 2
a *= 3
** 幂运算
/ 除法运算(如果有小数则返回结果为小数,如果都为整数则返回结果为整数)
// 整除,取整数部分
% 取余
## in
This is the use case you describe: Checking whether something is inside a list or not. As you know, you can use the `in` operator for that:
3 in [1, 2, 3] => True
6. 函数
使用 def 关键字来定义函数,默认返回None,可使用 return value 来指定返回值。 def 会创建一个函数对象和一个指向对象的引用。 callable(test) 内置函数判断是否可以调用。 ()表示对可调用对象的调用。
6.1. 程序入口
使用主函数作为程序入口,主函数一般放置在程序的尾部
if __name__ == '__main__':
print('Pass')
6.2. 调用函数
# 获取绝对值,只接受一个值
abs(200)
# 计算2的31次方的值
pow(2,31)
# 获取最大值,接受多个参数
max([1,2,3,4,5])
# 类型转换
x = 100
int(x)
str(x)
float(x)
bool(x)
iter(list1) # 将集合类型转换成惰性的迭代器。
6.3. 定义函数
# 定义一个空函数,缺少代码 pass,运行就会出错。
def nop():
pass
# 定义函数:包含一个参数。
def my_abs_0(x):
if x >= 0:
return x
else:
return -x
# 定义函数:包含一个参数,以及参数检查验证部分。
def my_abs_1(x):
if not isinstance(x, (int, float)): # 参数检查,如果x不是int或者float
raise TypeError('bad operand type') # 那么抛出异常
if x >= 0:
return x # 函数体内部可以用return随时返回函数结果,如果没有return语句,自动返回None
else:
return -x
# 定义多个参数函数,多个返回值
import math
def move(x,y,step,angle=0): # 定义函数时,需要确定函数名和参数个数;
nx = x + step*math.cos(angle)
ny = y + step*math.sin(angle)
return nx, ny
a=move(100,100,60,math.pi/6) # 函数可以同时返回多个值,其实就是一个tuple
print(a[0])
print(a[1])
6.4. 位置参数
# 位置参数,传入的两个参数值分别按照位置的先后顺序传到 x 和 n 中
def poper(x, n):
s=1
while n>0:
n=n-1
s=s*x
return s
print(poper(5,2)) # 函数调用
# 默认参数,在调用的时候,如果没有传入 n 的值,就默认使用 2
# 注意:必选参数在前,默认参数在后
# 注意:默认参数必须指向不变参数,不能使用list
# 注意:当不按顺序提供部分默认参数时,需要把参数名写上。
def poper(x, n = 2):
s=1
while n>0:
n=n-1
s=s*x
return s
print(poper(5)) # 函数调用
print(poper(5,2)) # 函数调用
6.5. 可变参数
# 可变参数:可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。
# 正常情况下我们顶一个函数,接受不确定个数的参数,就需要先定义一个集合对象,里面存放参数,然后传递给这个函数。
li=[1,2,3] # 定义一个list 或者 tuple
def calc(list):
sum=0
for x in list:
sum=sum+x
return sum
print(calc([1,2,3,4,5])) # 调用的时候需要传入一个 list 或者 tuple
print(calc([li[0],li[1],li[2]])) # 输入一个现有的 list 比较繁琐
# 可变参数1
def calc1(*list):
sum=0
for x in list:
sum=sum+x
return sum
print(calc1(1,2,3,4,5,6)) # 调用的时候直接输入值,他就能自动转换成列表
print(calc1(*li)) # 也可以直接输入一个列表,使用*,表示把这个 list 的所有元素作为参数传进去。
6.6. 关键字参数
# 可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
# 关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
dict1={'city':'Beijing', 'born':'changchun'} # 定义一个dict
def person(name, age, **aa):
print(name, age, aa)
person('du', 1) # 只传入必选参数。
person('du', 2, city=dict1['city'],born=dict1['born']) # 必选参数 + 任意关键字参数。
person('du', 3, **dict1) # 必选参数 + 定义好的 dict 对象,使用**
6.7. 命名关键字参数
# 关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。
# 如果限制关键字参数的名字,就可以用命名关键字参数。
dict1={'city':'Nanjing','born':'changchun'}
def person(name,age,*,city='Beijing',born,**other): # 命名关键字参数,*号后面的是必须输入的 dict key值。
print(name,age,city,born)
person('du', 2, city=dict1['city'],born=dict1['born']) # 调用函数,除了默认的,born必须给定值
person('du', 3, born='HeiHei') # 调用函数,除了默认的,born必须给定值
# 注意:使用多种类型参数组合在一起的时候,参数组合顺序:必选参数、默认参数、可变参数/命名关键字参数和关键字参数
6.8. 高阶函数
# -*- coding: utf-8 -*-
# 函数本身也可以赋值给变量
f = abs
f(-10)
# 函数名就是指向函数的变量
# 对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!
# abs = 10 # 如果将数值付给函数名,那么这个函数就不能用了...
# 高阶函数
# 变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
# 编写高阶函数,就是让函数的参数能够接收别的函数。
def add(x, y, f):
return f(x) + f(y)
# 调用的时候,我们传递进去两个值变量,和一个abs函数。
print(add(-5, 6, abs))
6.9. 匿名函数
# -*- coding: utf-8 -*-
# 当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便
def f(x):
return x * x
print(list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
print(list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
# 关键字lambda表示匿名函数,冒号前面的x表示函数参数
# 名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
# 因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数(不喜欢这么做)
f_1 = lambda x: x * x
# 同样,也可以把匿名函数作为返回值返回
def build(x, y):
return lambda: x * x + y * y
# Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。
6.10. 偏函数
import functools
# 手工定义一个偏函数
def int2(x, base=2):
return int(x, base)
# functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
int2_new = functools.partial(int, base=2)
if __name__ == '__main__':
# 把字符串转换成整数,base 参数默认是转换成 10 进制,
print(int('12345'))
print(int('12345', base=16))
# 当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
6.11. 闭包
# -*- coding: utf-8 -*-
# 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
# 我们来实现一个可变参数的求和。
def calc_sum(*args):
ax = 0
for n in args:
ax = ax + n
return ax
# 如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数
def lazy_sum(*args):
def sum_1():
ax = 0
for n in args:
ax = ax + n
return ax
return sum_1
f = lazy_sum(1, 3, 5, 7, 9)
print(f) # <function lazy_sum.<locals>.sum_1 at 0x000002614CF60948> 这里的变量 f 指向一个函数,
print(f()) # 加上小括号,执行的时候才会得到结果。
# 内部函数 sum 可以引用外部函数 lazy_sum 的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,
# 这种称为“闭包(Closure)”的程序结构拥有极大的威力。
f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
print(f1 == f2) # False f1()和f2()的调用结果互不影响
# 一个函数可以返回一个计算结果,也可以返回一个函数。
# 返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
6.12. 递归 && 尾递归
# 递归函数,如果一个函数在内部调用自身本身,这个函数就是递归函数。
# 注意:递归调用的次数过多,会导致栈溢出。
def fact(n):
if n == 1:
return 1
return n * fact(n - 1)
print(fact(1000))
# 解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的。
# 所以,把循环看成是一种特殊的尾递归函数也是可以的。
# 尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。
# 这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
print(fact_iter(1000,1))
# 尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。
# 遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化。
# 所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。
# 使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。
# 针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
# Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
7. 内置函数
7.1. del
a = [-1, 3, 'aa', 85]
del a[0] # 删除第 0 个元素
del a[2:4] # 使用切片,删除下标为 2,3的元素(包括头不包括尾)。
del a # 删除变量 a。
7.2. dir
我们可以通过调用 dir() 方法来检查 Python 中的对象。
# -*- coding: utf-8 -*-
class Animal(object):
def run(self):
print('Animal is running...')
# 我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法
def __len__(self):
return 100
if __name__ == '__main__':
a = Animal()
# 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list。
print(dir(a))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
# '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
# '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
# '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'run']
# 类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。
# 实际上,在len()函数内部,它自动去调用该对象的__len__()方法,如下两种方法是等价的。
print(len('ABC'))
print('ABC'.__len__())
# 检测某个对象是否有属性 run
print(hasattr(Animal, 'run'))
# 获取某个对象的属性,如果不存在就抛出AttributeError
print(getattr(Animal, 'run'))
print(getattr(Animal, 'aaa', 404)) # 指定默认值,不存在就返回这个值。
7.3. open
7.3.1. Open File
格式:open(“路径 + 文件名”, “读写模式”, “编码方式”) w:以写方式打开文件若存在,首先要清空,然后重新创建。 a:以追加模式打开(从 EOF 开始, 必要时创建新文件) r: Read Only r+:以读写模式打开 w+:以读写模式打开 (参见 w ) a:Append a+:以读写模式打开 (参见 a ) b: Binary二进制文件 rb:以二进制读模式打开 wb:以二进制写模式打开 (参见 w ) ab:以二进制追加模式打开 (参见 a ) rb+:以二进制读写模式打开 (参见 r+ ) wb+:以二进制读写模式打开 (参见 w+ ) ab+:以二进制读写模式打开 (参见 a+ )
# Open a file, create it if not exist.
fp = open("filename", "w", encoding="utf-8")
# 常用的写法,指定字符集。
with open("filename", "a", encoding="utf-8") as f:
f.writelines(result)
f.close()
7.3.2. Read File
我们如果只是迭代文件对象每一行,并做一些处理,是不需要将文件对象转成列表的,因为文件对象本身可迭代,而且是按行迭代
with open('filename', 'r') as f:
for line in f:
print(line, end='')
转换成列表进行操作(包含换行符)
with open('filename','r') as f:
content = list(f) # 强制转换
content = f.readlines() # 使用函数返回
print(content)
转换成列表进行操作(去掉换行符)
with open('filename','r') as f:
content = f.read().splitlines()
content = [line.rstrip('\n') for line in f]
print(content)
按照行读取文件并得到当前行号
文件对象是可迭代的(按行迭代),使用enumerate()即可在迭代的同时,得到数字索引(行号),enumerate()的默认数字初始值是0,如需指定1为起始,可以设置其第二个参数
with open('filename', 'r') as f:
for number, line in enumerate(f,start=1):
print(number, line, end='')
7.3.3. Other Info
f.read([size]) # size未指定则返回整个文件,如果文件大小>2倍内存则有问题。f.read()读到文件尾时返回”“(空字串)
f.readline() # 返回第一行
file.readline([size]) # 返回包含size行的列表,size未指定则返回全部行。
for line in data: # 遍历所有内容
print(line)
f.write(“hello”) # 如果要写入字符串以外的数据,先将他转换为字符串。
f.tell() # 返回一个整数,表示当前文件指针的位置(就是到文件头的比特数)。
# 用来移动文件指针
# 偏移量 : 单位“比特”,可正可负
# 起始位置 : 0 -文件头, 默认值; 1 -当前位置; 2 -文件尾
f.seek(偏移量,[起始位置])
f.close() # 关闭文件
size = 100
# size为读取的长度,以byte为单位
fp.read(size)
# 读一行,如果定义了size,有可能返回的只是一行的一部分
fp.readline(size)
# 把文件每一行作为一个list的一个成员,并返回这个list。其实它的内部是通过循环调用readline()来实现的。如果提供size参数,size是表示读取内容的总长,也就是说可能只读到文件的一部分。
fp.readlines(size)
str = "I love Python!"
# 把str写到文件中,write()并不会在str后加上一个换行符
fp.write(str)
seq = ["11","22","33"]
# 把seq的内容全部写到文件中(多行一次性写入)。这个函数也只是忠实地写入,不会在每行后面加上任何东西。
fp.writelines(seq)
# 关闭文件,python会在一个文件不用后自动关闭文件,不过这一功能没有保证,最好还是养成自己关闭的习惯。如果一个文件在关闭后还对其进行操作会产生ValueError
fp.close()
# 把缓冲区的内容写入硬盘
fp.flush()
# 返回一个长整型的“文件标签”
fp.fileno()
# 文件是否是一个终端设备文件(unix系统中的)
fp.isatty()
# 返回文件操作标记的当前位置,以文件的开头为原点
fp.tell()
# 返回下一行,并将文件操作标记位移到下一行。把一个file用于for … in file这样的语句时,就是调用next()函数来实现遍历的。
fp.next()
# 将文件打操作标记移到offset的位置。
# 这个offset一般是相对于文件的开头来计算的,一般为正数。但如果提供了whence参数就不一定了,
# whence可以为0表示从头开始计算,
# 1表示以当前位置为原点计算。
# 2表示以文件末尾为原点进行计算。
# 需要注意,如果文件以a或a+的模式打开,每次进行写操作时,文件操作标记会自动返回到文件末尾。
# fp.seek(offset[,whence])
# 把文件裁成规定的大小,默认的是裁到当前文件操作标记的位置。如果size比文件的大小还要大,依据系统的不同可能是不改变文件,也可能是用0把文件补到相应的大小,也可能是以一些随机的内容加上去。
fp.truncate(size)
7.4. filter
# -*- coding: utf-8 -*-
# Filter 函数
# 接收一个函数和一个序列,把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
def is_odd(n):
return n % 2 == 1
# filter()函数返回的是一个Iterator,也就是一个惰性序列。
# 所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。
print(list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])))
# 用 filter 函数求素数(质数),下面的代码有问题,需要完善。
# 计算素数的一个方法是埃氏筛法
# 首先,列出从2开始的所有自然数,构造一个序列
# 取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉
# 取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉
# 取新序列的第一个数5,然后用5把序列的5的倍数筛掉
# 不断筛下去,就可以得到所有的素数
# 可以先构造一个从3开始的奇数序列
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
# 定义一个筛选函数
def _not_divisible(n):
return lambda x: x % n > 0
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数yield n
it = filter(_not_divisible(n), it) # 构造新序列
for n in primes():
if n < 1000:
print(n)
else:
break
Copy
## **join**
str1 = "."
seq = ("a", "b", "c") # 字符串序列
print(str1.join(seq))
Copy
## **print**
import socket
# 可以打印出引入的模块物理路径
print(socket) # <module 'socket' from 'C:\\Users\\Chris\\.conda\\envs\\tensorflow\\lib\\socket.py'>
7.5. range
rang = range(1,11) # 生成数列,1 - 10,注意不包含结尾。
for x in rang:
print(x)
7.6. sorted
# -*- coding: utf-8 -*-
# 排序算法
# 通常规定,对于两个元素x和y,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1
# 内置的排序函数
print(sorted([36, 5, -12, 9, -21]))
# 此外,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序
print(sorted([36, 5, -12, 9, -21], key = abs))
# 默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
print(sorted(['bob', 'about', 'Zoo', 'Credit']))
# 忽略大小写
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower))
# 倒序
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True))
7.7. random
生成随机数,选择随机数
import random
print(random.randint(10,20))
lst=[11,22,33]
print(random.choice(lst))
7.8. type && instance
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
说我们要定义一个 Hello 的 class,就写一个hello.py模块,当 Python 解释器载入 hello 模块时,就会依次执行该模块的所有语句,执行结果就是动态创建出一个 Hello 的 class 对象。
type()函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello。
我们说 class 的定义是运行时动态创建的,而创建 class 的方法就是使用 type() 函数。
要创建一个class对象,type()函数依次传入3个参数:
class 的名称;
继承的父类集合,注意 Python 支持多重继承,如果只有一个父类,别忘了 tuple 的单元素写法;
class 的方法名称与函数绑定,这里我们把函数 fn 绑定到方法名 hello 上。* 通过 type() 函数创建的类和直接写 class 是完全一样的,因为 Python 解释器遇到 class 定义时,仅仅是扫描一下 class 定义的语法,然后调用 type() 函数创建出 class。
正常情况下,我们都用 class Xxx… 来定义类,但是,type() 函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。
# -*- coding: utf-8 -*-
class Hello(object):
def hello(self, name='world'):
print('Hello, %s.' % name)
if __name__ == '__main__':
s1 = Hello()
print(type(Hello)) # <class 'type'> Hello 是 type 类型。
print(type(s1)) # <class '__main__.Hello'> s1 是 Hello 类的对象,是 Hello 类型。
# 先定义函数
def fn(self, name='world'):
print('Hello, %s.' % name)
# 创建Hello class
HelloNew = type('HelloNew', (object,), dict(hello=fn))
s2 = HelloNew()
print(type(HelloNew)) # <class 'type'>
print(type(s2)) # <class '__main__.HelloNew'>
# -*- coding: utf-8 -*-
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
# 子类覆盖父类的方法:多态
def run(self):
print('Dog is running...')
def eat(self):
print('Dog is eating...')
class Cat(Animal):
pass
if __name__ == '__main__':
# 当我们拿到一个对象的引用时,如何知道这个对象是什么类型、有哪些方法
print(type(123)) # <class 'int'>
print(type('str')) # <class 'str'>
print(type(abs)) # <class 'builtin_function_or_method'>
# 它返回对应的Class类型。如果我们要在if语句中判断,就需要比较两个变量的type类型是否相同
if type(123) == int:
print('类型判断')
# 判断基本数据类型可以直接写int,str等,但如果要判断一个对象是否是函数怎么办?可以使用types模块中定义的常量:
import types
def fn():
pass
print(type(fn) == types.FunctionType)
print(type(lambda x: x) == types.LambdaType)
print(type(x for x in range(10)) == types.GeneratorType)
# 使用 isinstance() 判断是否是某个类型。
a = Animal()
b = Dog
c = Cat
print(isinstance(a, Cat))
# 还可以判断是否是某种类型中的一种。
print(isinstance([1, 2, 3], (list, tuple)))
print(dir(Dog))
7.9. map reduce
# -*- coding: utf-8 -*-
# Map Reduce 函数
def f(x):
return x * x
# map()传入的第一个参数是f,即函数对象本身。
# 由于结果 res 是一个Iterator,Iterator是惰性序列,因此通过 list() 函数让它把整个序列都计算出来并返回一个list。
res = map(f, [1,2,3,4,5])
print(list(res))
# reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数
# reduce把结果继续和序列的下一个元素做累积计算
def add(x, y):
return x + y
from functools import reduce
reduce(add, [1, 2, 3, 4, 5])
# lambda 函数
res = reduce(lambda x, y : x * 10 + y, [1, 2, 3, 4, 5])
8. 异常处理
8.1. try … except .. as
语法正确,运行的时候产生的错误
try:
#
except Exception as e:
print(e)
失败重试的场景
for i in range(1, 10):
try:
print("我要重复干的事情,例如多次请求")
break
except Exception as e:
print(e)
8.2. try … except … finally 语句
当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行。而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。
BaseException:所有错误类型的父类,继承关系:https://docs.python.org/3/library/exceptions.html#exception-hierarchy
Python 按照行来执行代码,子错误类型应该先捕获,否则捕获到父类型的时候,下面的子类型就永远都捕捉不到。
try:
print('try ...')
r = 10 / 2
print('result', r)
except ZeroDivisionError as e:
print('error:', e)
# 错误应该有很多种类,如果发生了不同类型的错误,应该由不同的except语句块处理,可以同时添加多种错误接收方式。
except ValueError as e:
print('error:', e)
# 可选:当没有错误发生时,会自动执行else语句
else:
print('No Error!')
# 可选:不管有没有错误,最后的 finally 一定会被执行。
finally:
print('finally')
## **调用堆栈**
如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。
# err.py:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
执行,结果如下:
$ python3 err.py
# 错误信息第1行:告诉我们这是错误的跟踪信息。
Traceback (most recent calllast):
# 第2~3行:调用main()出错了,在代码文件err.py的第11行代码,但原因是第9行:
File "err.py", line 11, in <module>
main()
# 第4~5行:调用bar('0')出错了,在代码文件err.py的第9行代码,但原因是第6行:
File "err.py", line 9, in main
bar('0')
# 第6~7行:原因是return foo(s) * 2这个语句出错了,但这还不是最终原因,继续往下看:
File "err.py", line 6, in bar
return foo(s) * 2
# 第8~9行:原因是return 10 / int(s)这个语句出错了,这是错误产生的源头,因为下面打印了:ZeroDivisionError: integer division or modulo by zero
File "err.py", line 3, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
# 根据错误类型ZeroDivisionError,我们判断,int(s)本身并没有出错,但是int(s)返回0,在计算10 / 0时出错,至此,找到错误源头。
8.3. 记录错误
如果不捕获错误,自然可以让Python解释器来打印出错误堆栈,但程序也被结束了。 既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,同时,让程序继续执行下去。 Python内置的 logging 模块可以非常容易地记录错误信息。 同样是出错,但程序打印完错误信息后会继续执行,并正常退出。 通过配置 logging 还可以把错误记录到日志文件里,方便事后排查。
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
if __name__ == '__main__':
main()
print('END')
## **抛出错误**
根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise语句抛出一个错误的实例。
只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueError,TypeError),尽量使用Python内置的错误类型
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return10 / n
foo('0')
9. io
很多时候,数据读写不一定是文件,也可以在内存中读写,StringIO就是在内存中读写 str,我们创建一个 StringIO对象,然后想文件一样写入即可。
操作二进制数据就需要使用 BytesIO,实现内存中读取bytes。
# -*- coding: utf-8 -*-
from io import StringIO
from io import BytesIO
if __name__ == '__main__':
f = StringIO()
f.write('D')
f.write('\n')
f.write('I love You!')
f.writelines('Shit!')
print(f.getvalue())
b = BytesIO() # 创建实例的对象
b.write('杜超群'.encode('utf-8')) # 写入经过utf-8编码的bytes,
print(b.getvalue()) # 打印出二进制数据
b1 = BytesIO(b'\xe6\x9d\x9c\xe8\xb6\x85\xe7\xbe\xa4') # 初始化一个BytesIO
print(b1.read()) # 打印出初始的内容
10. requests
使用 requests 可以模拟浏览器的请求,比起之前用到的urllib,requests模块的api更加便捷(本质就是封装了urllib3)。requests 库发送请求将网页内容下载下来以后,并不会执行js代码。可以使用conda来管理环境,按照此库。 https://github.com/kennethreitz/requests
官方文档:http://docs.python-requests.org/en/latest/user/install/#install 中文文档:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html
10.1. 请求方法
import requests
r = requests.get('https://github.com/timeline.json')
r = requests.post("https://github.com/post")
r = requests.put("https://github.com/put")
r = requests.delete("https://github.com/delete")
r = requests.head("https://github.com/get")
r = requests.options("https://github.com/get")
10.2. 为URL传递参数
你也许经常想为URL的查询字符串(query string)传递某种数据。如果你是手工构建URL,那么数据会以键/值 对的形式置于URL中,跟在一个问号的后面: 例如:http://bin.org/get?key=val Requests允许你使用 params 关键字参数,以一个字典来提供这些参数。举例来说,如果你想传递 key1=value1 和 key2=value2 到 http://bin.org/get ,那么你可以使用如下代码:
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get", params=payload)
print r.url # 通过打印输出该URL,你能看到URL已被正确编码
# u'http://httpbin.org/get?key2=value2&key1=value1'
10.3. 响应内容:html 网页源代码,Unicode编码。
Requests会自动解码来自服务器的内容。大多数unicode字符集都能被无缝地解码。
import requests
r = requests.get('https://github.com/timeline.json')
r.text
请求发出后,Requests会基于HTTP头部对响应的编码作出有根据的推测。当你访问r.text 之时,Requests会使用其推测的文本编码。你可以找出Requests使用了什么编码,并且能够使用 r.encoding 属性来改变它:
reponse.encoding = 'UTF-8'
reponse.encoding = 'ISO-8859-1'
print(reponse.text)
如果你改变了编码,每当你访问 r.text ,Request都将会使用 r.encoding 的新值。 在你需要的情况下,Requests也可以使用定制的编码。如果你创建了自己的编码,并使用 codecs 模块进行注册,你就可以轻松地使用这个解码器名称作为 r.encoding 的值, 然后由Requests来为你处理编码。 从 response 对象里面处理字符编码。
# response = requests.get('https://news.sina.com.cn/roll/', timeout=10)
# 下载HTML页面,解析默认编码。
def parse_content(response):
# 从页面上获取字符编码,
encodings = requests.utils.get_encodings_from_content(response.text)
if encodings:
encoding = encodings[0]
else:
encoding = response.apparent_encoding
# 按照网页上配置的编码内容来解析网页,如果设置为replace,则会用?取代非法字符;
return response.content.decode(encoding, 'replace')
10.4. 二进制响应内容
bytes 二进制文件,图片等。
以字节的方式访问请求响应体,对于非文本请求: r.content b’[{“repository”:{“open_issues”:0,“url”:“https://github.com/…
Requests会自动为你解码 gzip 和 deflate 传输编码的响应数据。
例如,以请求返回的二进制数据创建一张图片,你可以使用如下代码:
from PIL import Image
from StringIO import StringIO
i = Image.open(StringIO(r.content))
10.5. JSON响应内容
Requests中也有一个内置的JSON解码器,助你处理JSON数据:
import requests
r = requests.get('https://github.com/timeline.json')
r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...
如果JSON解码失败, r.json 就会抛出一个异常。
10.6. 套字节响应内容
在罕见的情况下你可能想获取来自服务器的原始套接字响应,那么你可以访问 r.raw 。 如果你确实想这么干,那请你确保在初始请求中设置了 stream=True 。具体的你可以这么做:
r = requests.get('https://github.com/timeline.json', stream=True)
r.raw
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810>
r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
10.7. 定制请求头
如果你想为请求添加HTTP头部,只要简单地传递一个 dict 给 headers 参数就可以了。
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
headers = {'content-type': 'application/json'}
r = requests.post(url, data=json.dumps(payload), headers=headers)
heasers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
}
reponse = requests.get("https://www.duchaoqun.cn", headers=heasers)
reponse.encoding = 'UTF-8'
10.7.1. User-Agent
User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36 Edg/126.0.0.0
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
10.8. 更加复杂的POST请求
通常,你想要发送一些编码为表单形式的数据,像一个HTML表单。要实现这个,只需简单地传递一个字典给data 参数。你的数据字典在发出请求时会自动编码为表单形式:
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=payload)
print r.text
{
"form": {
"key2": "value2",
"key1": "value1"
},
...
}
很多时候你想要发送的数据并非编码为表单形式的。如果你传递一个 string 而不是一个dict ,那么数据会被直接发布出去。 例如,Github API v3接受编码为JSON的POST/PATCH数据:
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
POST一个多部分编码(Multipart-Encoded)的文件,Requests使得上传多部分编码文件变得很简单:
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)
r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
你可以显式地设置文件名:
url = 'http://httpbin.org/post'
files = {'file': ('report.xls', open('report.xls', 'rb'))}
r = requests.post(url, files=files)
r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
如果你想,你也可以发送作为文件来接收的字符串:
url = 'http://httpbin.org/post'
files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
r = requests.post(url, files=files)
r.text
{
...
"files": {
"file": "some,data,to,send\\nanother,row,to,send\\n"
},
...
}
## 响应状态码
我们可以检测响应状态码:
```python
r = requests.get('http://httpbin.org/get')
r.status_code
Copy
•为方便引用,Requests还附带了一个内置的状态码查询对象:
r.status_code == requests.codes.ok
True
Copy
•如果发送了一个失败请求(非200响应),我们可以通过 Response.raise_for_status() 来抛出异常:
bad_r = requests.get('http://httpbin.org/status/404')
bad_r.status_code
404
bad_r.raise_for_status()
Traceback (most recent call last):
File "requests/models.py", line 832, in raise_for_status
raise http_error
requests.exceptions.HTTPError: 404 Client Error
# 但是,由于我们的例子中 r 的 status_code 是 200 ,当我们调用 raise_for_status() 时,得到的是:
r.raise_for_status()
None
Copy
响应头
•我们可以查看以一个Python字典形式展示的服务器响应头:
r.headers
{
'status': '200 OK',
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json; charset=utf-8'
}
Copy
•但是这个字典比较特殊:它是仅为HTTP头部而生的。根据 RFC 2616 , HTTP头部是大小写不敏感的。
因此,我们可以使用任意大写形式来访问这些响应头字段:
>>> r.headers['Content-Type']
'application/json; charset=utf-8'
>>> r.headers.get('content-type')
'application/json; charset=utf-8'
Copy
•如果某个响应头字段不存在,那么它的默认值为 None
>>> r.headers['X-Random']
None
Copy
Cookies
•如果某个响应中包含一些Cookie,你可以快速访问它们:
>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name']
'example_cookie_value'
Copy
•要想发送你的cookies到服务器,可以使用 cookies 参数:
>>> url = 'http://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'
## 重定向与请求历史
- 使用GET或OPTIONS时,Requests会自动处理位置重定向。
- Github将所有的HTTP请求重定向到HTTPS。可以使用响应对象的 history 方法来追踪重定向。 我们来看看Github做了什么:
```python
>>> r = requests.get('http://github.com')
>>> r.url
'https://github.com/'
>>> r.status_code
200
>>> r.history
[<Response [301]>]
Copy
Response.history 是一个 Request 对象的列表,为了完成请求而创建了这些对象。这个对象列表按照从最老到最近的请求进行排序。
•如果你使用的是GET或OPTIONS,那么你可以通过 allow_redirects 参数禁用重定向处理:
>>> r = requests.get('http://github.com', allow_redirects=False)
>>> r.status_code
301
>>> r.history
[]
Copy
•如果你使用的是POST,PUT,PATCH,DELETE或HEAD,你也可以启用重定向:
>>> r = requests.post('http://github.com', allow_redirects=True)
>>> r.url
'https://github.com/'
>>> r.history
[<Response [301]>]
Copy
超时
你可以告诉requests在经过以 timeout 参数设定的秒数时间之后停止等待响应:
>>> requests.get('http://github.com', timeout=0.001)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
Copy
•注:timeout 仅对连接过程有效,与响应体的下载无关。
错误与异常
遇到网络问题(如:DNS查询失败、拒绝连接等)时,Requests会抛出一个ConnectionError 异常。
遇到罕见的无效HTTP响应时,Requests则会抛出一个 HTTPError 异常。
若请求超时,则抛出一个 Timeout 异常。
若请求超过了设定的最大重定向次数,则会抛出一个 TooManyRedirects 异常。
所有Requests显式抛出的异常都继承自 requests.exceptions.RequestException 。
使用代理
•也可以使用 socks 模块的方式使用代理。
# !/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
# 使用socks代理发送http请求:
# 需要安装socks包conda install -c anaconda pysocks
# 创建session
s = requests.session()
# 设置代理
proxies = {
"http": "socks5://127.0.0.1:8484",
"https": "socks5://127.0.0.1:8484",
}
# 设置头信息
url = 'http://ip.gs'
header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0.1 Safari/604.3.5'}
# 使用代理访问
response = s.get(url, headers=header, proxies=proxies, timeout=20)
# 打印服务器端的相应结果
print(response.content)
10.9. 下载图片
#!/bin/env python
# -*- coding:utf-8 -*-
import time
import requests
def download(url,file_name):
ir = requests.get(url)
print(url+' '+str(ir.status_code))
if ir.status_code == 200:
open(file_name, 'wb').write(ir.content)
for x in range(1,2836):
for y in range(1,100):
file_name = str(x)+'-'+str(y)+'.jpg'
url = image+str(x)+'/'+str(y)+'.jpg'
try:
download(url, file_name)
except:
print(str(x)+str(y))
[[time]].sleep(1)
11. psycopg2
连接pg数据库
在部分环境中会出现问题,后期使用psycopg2-binary代替
11.1. 基本用法
# -*- coding: utf-8 -*-
# !/usr/bin/python
# 需要安装下面的驱动包
import psycopg2
# 连接到一个现有的数据库,如果数据库不存在,那么它就会被创建,最终将返回一个数据库对象。
conn = psycopg2.connect(database="aoye", user="Chris", password="123456", host="127.0.0.1", port="5432")
print("Opened database successfully")
# 创建表:
cur = conn.cursor()
# cur.execute('''CREATE TABLE COMPANY
# (ID INT PRIMARY KEY NOT NULL,
# NAME TEXT NOT NULL,
# AGE INT NOT NULL,
# ADDRESS CHAR(50),
# SALARY REAL);''')
# print ("Table created successfully")
# 插入数据
# cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
# VALUES (1, 'Paul', 32, 'California', 20000.00 )")
#
# cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
# VALUES (2, 'Allen', 25, 'Texas', 15000.00 )")
#
# cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
# VALUES (3, 'Teddy', 23, 'Norway', 20000.00 )")
#
# cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
# VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 )")
# print("Records created successfully")
# 查询数据
cur.execute("SELECT id, name, address, salary from COMPANY")
rows = cur.fetchall()
for row in rows:
print("ID = ", row[0])
print("NAME = ", row[1])
print("ADDRESS = ", row[2])
print("SALARY = ", row[3], "\n")
print("Operation done successfully")
# 更新数据
# cur.execute("UPDATE COMPANY set SALARY = 25000.00 where ID=1")
# 删除数据
# cur.execute("DELETE from COMPANY where ID=2;")
conn.commit()
conn.close()
11.2. 插入数据
这里需要留意,插入的数据需要用如下方式传入(有些数据内容包含单引号,必须使用下面的方式插入)
# -*- coding: utf-8 -*-
from util import *
if __name__ == '__main__':
# create table urls(id SERIAL,md5 varchar(32),url varchar(1000), timestamp TIMESTAMP default now());
import psycopg2
import util.py
conn = psycopg2.connect(database="aoye", user="Chris", password="123456", host="127.0.0.1", port="5432")
cur = conn.cursor()
with open('D:\\Data\\fall11_urls.txt', 'r', encoding='utf-8') as f:
while True:
url = f.readline().split()[1]
if not url:
break
else:
sql = """INSERT INTO urls(md5,url) VALUES (%s, %s)"""
cur.execute(sql,(get_md5(url), url)) # 留意这里的插入数据方式。
conn.commit()
conn.close()
参考文档: https://www.cnblogs.com/Erick-L/p/7106816.html
12. hashlib
import hashlib
# 计算字符串的 md5 值
# MD5是最常见的摘要算法,速度很快,生成结果是固定的 128 bit字节
# 通常用一个32位的16进制字符串表示。
md5 = hashlib.md5()
md5.update('Str1,Str1,Str1,Str1,'.encode('utf-8'))
md5.update('Str1,Str1,Str1,Str1'.encode('utf-8'))
# 如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的。
print(md5.hexdigest())
# SHA1 的结果是 160 bit字节,通常用一个40位的16进制字符串表示。
# 比 SHA1 更安全的算法是 SHA256 和 SHA512
# 不过越安全的算法不仅越慢,而且摘要长度更长。
sha1 = hashlib.sha1()
sha1.update('Str1,Str1,Str1,Str1,'.encode('utf-8'))
print(sha1.hexdigest())
13. Python Collections Counter
统计一个集合中某个内容出现的次数,return a dict.
from collections import Counter
print Counter("hello")
>>> Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
- JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
- JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:
14. JSON
JSON Type | Python Type |
---|---|
{} | dict {‘key’:value, ‘key’:value} |
[] | list [element, element, enement] |
“string” | str class |
1234.56 | int or float |
true/false | True/False |
null | None |
- 使用 loads 方法,将 JSON 字符串转换成 Python 对象。
- 使用 dumps 方法,将 Python 对象转换成 JSON 字符。
- 注意,如果是通过 API 获取的 JSON 字符串,需要先处理成规范的格式,例如去掉没有用的头部和尾部。
- 官方连接:https://docs.python.org/3/library/json.html#json.dumps
14.1. Basic Usage
# -*- coding: utf-8 -*-
# 导入包
import json
if __name__ == '__main__':
# 定义一个dict
d = dict(name='Chris', age=123)
# dumps()方法返回一个str,内容就是一个标准的json {"name": "Chris", "age": 123}
print(json.dumps(d))
# loads()方法把 JSON 字符串反序列化成一个 dict。
json_str = '{"name":"Chris", "age": 33 }'
print(type(json.loads(json_str))) # <class 'dict'>
print(json.loads(json_str)) # {'name': 'Chris', 'age': 33}
14.2. Advanced Usage
- json 模块的 dumps()和 loads() 函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。
# -*- coding: utf-8 -*-
import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
if __name__ == '__main__':
# dumps 函数将一个对象转换成 JSON,
# 仔细看看dumps()方法的参数列表,可以发现,除了第一个必须的obj参数外,dumps()方法还提供了一大堆的可选参数,
# 默认情况下,dumps()方法不知道如何将Student实例变为一个JSON的{}对象
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
s1 = Student('Chris', 18, 100)
# 可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可:
print(json.dumps(s1, default=student2dict))
# 因为通常class的实例都有一个__dict__属性,它就是一个 dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class。
print(json.dumps(s1, default=lambda obj: obj.__dict__))
# 同样的道理,如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个 dict 对象,然后,我们传入的object_hook函数负责把dict转换为Student实例:
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
json_str = '{"age": 20, "score": 88, "name": "Bob"}'
# 反序列化的 Student 对象
student_object = json.loads(json_str, object_hook=dict2student)
14.3. Q & A
- json ‘str’ object has no attribute ‘read’
- 解决:只需要将json.load()换成json.loads()就可以了
15. shutil
复制文件
# oldfile 和 newfile 都只能是文件
shutil.copyfile("oldfile","newfile")
# oldfile 只能是文件夹,newfile可以是文件,也可以是目标目录
shutil.copy("oldfile","newfile")
# olddir和newdir都只能是目录,且newdir必须不存在
shutil.copytree("olddir","newdir")
移动文件(目录)
shutil.move("oldpos","newpos")
# 空目录、有内容的目录都可以删
shutil.rmtree("dir")