热度

Python

Python

Python 编程基础

[编译型和解释型](http://c.biancheng.net/uploads/allimg/190211/2-1Z2111G33L03.gif)
领域:
    Web应用开发:
        通过mod_wsgi模块,apache可运行python的web程序,Python定义WSGI标准应用接口协调HTTP服务器与基于Python的Web程序之间的通信。Web框架:Django、TurboGears、web2py等
    操作系统管理、自动化运维开发:
        例:Ubunut的Ubiquity安装器、RedHat、Fedora的Anaconda安装器等
    游戏开发:
        支持更多的特性和数据类型,例:文明
    编写服务器软件:
        支持各种网络协议,可编写服务器软件及网络爬虫,例:第三方库Twisted
    科学计算:
        NumPy、SciPy、Matplotlip等
3和2的区别:
    print函数代替print语句
    默认使用UTF-8编码
    除法运算
    异常
    八进制字面量表示
    不等于运算符
    数据类型
Python 2to3:自动将Python2.x代码转换为Pyhton3.x代码

安装
Linux 两种方式:
1. 命令行安装
apt update
apt install python3.6
unlink /usr/bin/python // 取消旧python的映射
ln -s /usr/bin/python3.6 /usr/bin/python // python3环境的路径和版本要写正确
2. 源码安装
下载: wget python3.6下载路径
压缩: tar -zxvf python3.6.tgz
编译: ./configure –prefix=/usr/local/ make make install

第一个python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
代码编写
两种方式:
1. 在提示符 >>> 直接输入: print("hello world")
2. 文本编辑器编写并执行python程序
vim hi.python
print("hello world")
python hi.python

注释:
单行注释: #
多行注释: 1. ''' xxx...xxx ''' 2. """ xxx...xxx """

中文编码声明注释:
1. # -*-coding:utf-8 -*- 2. # coding=utf-8

缩进规则:
冒号(:)和代码缩进

编码规范(PEP8)
PEP: Python Enhancement Proposal, 8代表Pyton代码的样式指南
1. 每个import语句只导入一个模块,避免导入多个
2. 不在行为加分号,不将两条命令放一行
3. 每行不超80字符,如超,用小括号连接
4. 用空行增强可读性,顶级定义空两行,方法定义空一行
5. 使用空格分隔 运算符、函数参数

Python标识符命名规范
1. 字符、下划线、数字,数字不开头
2. 不与保留子相同
3. 不包含空格、@、%、$等特殊字符
4. 严格区分大小写
5. 下划线开头有特殊意义
单下划线: 不能直接访问的类属性,无法通过 from ... import * 的方式导入
双下划线: 类的私有成员
双下划线开头和结尾: 专用标识符

关键字(保留子)
查看方式:
import keyword
keyword.kwlist

内置函数
[内置函数](https://docs.python.org/zh-cn/3/library/functions.html)

变量类型和运算符

数值类型(整形、浮点型、复数)
整形:
二进制:1 和 0 组成, 0b|0B
八进制:0~7 , 0o|0O
十进制:普通的整数,不以0开头
十六进制:0~9 + A~F, 0x|0X
浮点型:
十进制形式:eg:3.14
科学计数:eg:2.3e2
复数:
虚部用j|J表示

字符串(长字符串、原始字符串)
长字符串:
三个引号:单引号、双引号
原始字符串:
需要’'进行转义

bytes类型
由多个字节组成,以字节为单位进行操作,只负责以字节(二进制格式)序列来记录数据
将字符串转换为 bytes对象的三种方式:
1. 如内容是ASCII,在字符串之前添加 b 构建字节串值
2. 调用 bytes() 函数
3. 调用字符串本身的 encode() 方法

bool布尔类型
真:True :1
假:False:0

len() 获取字符串长度或字节数
len(string): 字符串的字符|字符串长度|一个字符串占用的字节数, string 进行长度统计的字符串

input() 获取用户输入的字符串
将用户输入的内容放到字符串中,返回一个字符串中

print() 高级用法
同时输出多个变量: eg:print(value,…sep=’’,end=’\n’,file=sys.stdout,flush=False)

格式化字符串(格式化输出)
对各种类型的数据进行格式化输出
print()函数包含三部分:
1. 格式化字符串,相当于字符串模版
2. 固定使用” % “ 作为分隔符
3. 对应的变量,多个用’()’括号括起来
转换说明符:
%d, %i : 十进制的整数
%o : 八进制的整数
%x,%X : 十六进制整数
%e : 科学计数的浮点数
%E : 科学计数的浮点数
%f,%F : 十进制的浮点数
%g : 智能选择%f或%e格式
%G : 智能选择%F或%E格式
%c : 格式化字符及其ASCII码
%r : 使用repr()将变量或表达式转换为字符串
%s : 使用str()将变量或字符串转换为字符串

转义字符
\ : 一行未完,转到下一行继续写
' : 单引号
" : 双引号
\0 : 空
\n : 换行符
\r : 回车符
\t : 水平制表符
\a : 响铃
\b : 退格
\ : 反斜线
\0dd : 八进制数,dd代表字符,eg:\012代表换行
\xhh : 十六进制数,hh代表字符,eg:\x0a 代表换行

数据类型转换
int(x) : 将x转换为整数类型
lloat(x) : 转为浮点型
complex(real,[,imag]) : 创建一个复数
str(x) : 转为字符串
repr(x) : 转为表达式字符串
eval(x) : 计算字符串中有效python表达式,返回一个对象
chr(x) : 将整数x转为一个字符
ord(x) : 将字符x转为对应的整数值
hex(x) : 将整数x转为十六进制字符串
oct(x) : 整数x转为八进制字符串

算术运算符
+ : 加
- : 减
* : 乘
/ : 除
% : 取余,返回除法的余数
// : 整除,返回商的整数部分
** : 幂,返回x的y次方 eg:2**4, 16

赋值运算符
= : 基本赋值
扩展后的赋值运算符:
+= : 加赋值
-= : 减赋值
= : 乘赋值
/= : 除赋值
%= : 取余赋值
*
= : 幂赋值
//= : 取整数赋值
|= : 按位或赋值
^= : 按位与赋值
<<= : 左移赋值
>>= : 右移赋值

位运算符
& : 按位与
| : 按位或
^ : 按位异或
~ : 按位取反
<< : 按位左移
>> : 按位右移
详细说明

比较运算符
> : 大于
>= : 大于等于
< : 小于
<= : 小于等于
== : 等于
!= : 不等于
is : 判断两个变量引用的对象是否相同,不同返回False
is not : 判断两个变量引用的对象是否不相同,不同返回True
比较运算符 == 和 is 的区别:
== : 比较两个变量的值是否相等。
is : 对比两个变量引用的是否是同一个对象

逻辑运算符
and : 与 两个都为True,为True,否则为False
or : 或 一个为True,为True,两都为False,则为False
not : 非 只需一个为True,则为False

三目运算符(三元)
先对逻辑表达式求值,如果为True,则返回True_statements的值,如为False,则返回False_statements的值
A = 5 B = 3
st = “A大于B” if A > B else “A小于B”

运算符优先级
优先级

列表、元组、字典、集合

内置的四种常用数据结构: 列表(list)、 元组(tuple)、 字典(dict)、 集合(set)
列表和元组不同和相同:
    相同:按顺序保存元素
    不同:元组不可修改,列表可修改
字典和集合的相同和不同:
    相同:数据是无序的
    不同:字典可用key-value形式保存数据

序列
序列:一块可存放多个值的连续内容空间,值按一定顺序排列,可通过值位置的编号(索引)访问他们
序列包括:字符串、列表、元祖、集合、字典。 字典和集合不支持索引、切片、相加、相乘等操作
字符串是一种常见的序列,可直接通过索引访问字符串内的字符
索引值从0开始递增,支持索引值负数,从右向左计数
序列切片:是访问序列中元素的另一种方法,可访问一定范围内的元素,通过切片,生成新的序列
snmae[start :end :step]
sname:表示序列的名称
start:表示切片开始索引位置,默认为0,从开头进行切片
end: 表示切片的结束位置,若不指定,默认为序列的长度
step: 表示隔几个存储位置取一次元素
序列相加:两种相同类型的序列使用 “ + “ 运算符做相加操作,但不去重
相同类型:指 + 两侧都是序列类型,都为元组 或 都为字符串
序列相乘:
使用数字n乘以一个序列生成新的序列,eg : print(‘hi’ * 3)
检查元素是否包含在序列中:使用 in 关键字
value in sequence : value 要检查的元素,sequence 指定的序列
序列相关的内置函数:
len() : 计算序列的长度,即返回序列中包含多少个元素
max() : 最大元素
min() : 最小元素
list() : 将序列转为列表
str() : 将序列转为字符串
sum() : 计算元素和
sorted() : 对元素排序
reversted() : 反向序列中的元素
enumerate() : 将序列组合为一个索引序列,用在for循环中

list列表

python没有数组,有列表。 列表将所有元素放在一对中括号中[],相邻元素用逗号隔开,eg:[var1,var2,...varn],个数不限,支持的数据类型即可。可以是:整数、实数、字符串、列表、元组、浮点数等
    type(['xxx',xxx,[xxx,'xxx'],xxx]) :查看数据类型,数据类型为list,即为:列表
创建列表
    使用 = 运算符创建列表, 使用赋值运算符 = 直接将列表赋值给变量
                listname = [element1, element2,... elementn]
    使用list()函数创建列表:
        list() 将 元组、区间等对象转换为 列表
            a_tuple = ('name',23)
            a_list = list(a_tuple)    
访问列表元素
    通过列表的索引获取指定的元素 或 直接使用 print() 函数
        list_str[n]        |        print(list_str)
删除列表
    使用 del 语句删除
        del list_str    

列表添加元素的三种方法
1. append():在列表的末尾追加元素,传递列表或元组,视为一个元素,直接添加到列表中,形成包含列表和元组
list_str.append(obj)
2. extend():不将被追加的列表或元组当作一个整体,只追加列表中的元素
list_str.extend(obj)
3. insert():在列表中间增加元素
list_str.insert(index,obj) : index 将元素插入到列表中指定位置处的索引值,将插入的对象视为一个整体

列表删除元素的三种方法
删除元素的三种场景:
1. 根据元素位置的索引值,用del语句
2. 元素的位置删除,用 list提供的remove 方法
3. 删除所有元素,用list提供的clear方法
删除元素的3中方法:
1. 根据索引值删除元素,类删除列表,用del语句
del list_str[n:m]
2. 根据元素值删除元素
remove 删除第一个和指定值相同的元素,如没有,则显示ValueError错误,删除前判断是否存在,长于count()方法组合使用
3. 删除列表所有元素
clear() : 清空列表的所有元素
list_str.clear()

list列表修改元素
列表的元素类变量,可对列表的元素赋值,即可修改列表的元素
通过索引到列表元素赋值,可用正数索引,也可用负数索引
list_str = list(rang(1,5))
list_str[1:3] = [‘a’,’b’]
可用步长

list常用方法(count、index、pop、reverse、sort)
交互模式:查看列表包含的所有方法: dir(list)
count() :统计列表中某个元素出现的次数
listname.count(obj)
index() :定位某个元素在列表中出现的位置,即索引
listname.index(obj,start,end) : start、end:指定范围内搜索元素
pop() :移除列表中指定索引处的元素,若不指定,默认移除列表中最后一个元素
listname.pop(index)
reverse() :反转列表中的元素
listname.reverse()
sort() :对列表元素进行排序
listname.sort(key=None,reserse=False)
key : 指定每个元素提取用于比较的健,key=str.lower:不区分大小写
reserse :是否需要反转排序,默认False表示从小到大。True为从大到小排序

tuple元组

按特定顺序排序的元素,序列不可变。不可边的列表,保存不可变的内容,用()定义,用,逗号分隔。元组可存储整数、实数、字符串、列表、元组等任何类型的数据
('xxx',[x,xx,'xxx'],(x,xx,xxx))
创建元组
    1. 使用 "=" 运算符直接创建元组
        tupe_str = (x,xx,xxx)
    2. 使用tuple()函数创建元组
        tuple(data)
            list_str = [x,'xx',xxx]
            tuple_str = tuple(list_str)
访问元组元素
    使用元组各元素的索引值获取
        tuple_str[n]
修改元组元素
    元组不可改变序列,元素不可单独修改。
    修改方法:
        1. 对元组重新赋值:
            tuple_str = (x,xx,'xxx')
            tuple_str = ('x','xx',xxx)
        2. 通过连接多个元组的方式向元组中添加新元素
            tuple_str = (x,'xx',xxx)
            tuple_str + ('y',yy,'yyy')
            元组连接的必须是元组,否则抛出:TypeError 错误
删除元组
    使用 del 语句删除:
        tuple_str = ('x',xx,'xxx')
        del(tuple_str)
元组使用场景:
    1. 元组作为很多内置函数和序列类型方法的返回值存在,使用某些函数或方法,如返回元组类型,对元组进行处理。
    2. 元组比列表访问和处理速度更快,如 对指定元素访问,且不修改时,使用元组
    3. 元组在映射(和集合的成员)中可做 健 使用,列表不行。 

元组和列表的区别:
    同属序列类型,按照顺序存放在一组数据,数据类型不受限制
    数据修改:
        列表可修改
        元组不可修改
    字节:元组比列表少16个字节
        列表是动态的,存储指针指向对应的元素 占用8个字节,元素可变,需要额外存储已经分配的长度大小
        元组,长度固定,存储元素不可变,存储空间是固定的。

字典

dict : 类列表,数据的集合,可变序列类型。无序可变序列,内容以 键值对 形式存放
特征:
    通过键来读取元素
    任意数据类型的无需集合
    可变的,可任意嵌套
    键必须是唯一
    键必须不可变

创建字典
    1. 花括号 { }
        每个元素包含2部分: 键 : 值, 键和值用冒号分割,相邻元素用逗号分割,大括号{}包含
        eg : dict_str = {'name':'hale','age':23}
            同一字典 键值 必须唯一,键值可以是整数、字符串、元组
    2. 通过 fromkeys() 创建字典
        创建所有键值为空的字典
        eg:    dict_str = dict.formkeys(list, value=None)
            各个键对应的值为空None, 通常用初始化字典,设置value的默认值
    3. 通过 dict() 映射函数创建字典
        eg: dict_str = dict(one=1,tow=2,thre=2)
             dict_str = [('one',1),('toe',2)]
             dict_str = [['one',1],['tow',2]]
             dict_str = (('one'.1),('two',2))
             dict_str = ['one','two','three']
访问字典
    通过 键 访问对应的元素值。 但字典元素是无序的
        eg: dict_str['one']
             dict_str.get(key[,default])    // 通过 get 方法获取指定键的值
                dict_str.get('two')
            使用get() 方法,可为其设置默认值
删除字典
    使用 del 语句 删除字典
        eg: del(dict_str)

字典基本操作(添加、修改、删除键值对)
字典无需
操作字典的方法:
1. 向现有字典添加键值对
2. 修改现有字典中的键值对
3. 删除指定键值对
4. 判断是否存在指定键值对

字典添加键值对
    dict[key] = value
        dict : 表示字典名称
        key  : 要添加元素的键,不可重复
        value: 要添加的值。支持的数据类型

修改键值对
    不是修改某一键值对的键和值,只修改值
    如 新添加的元素的键已存在,替换原键对应的值

删除键值对
    del 
        eg:del dict_str['one']

判断是否存在
    使用 in 或者 not in 运算符
        eg:'one' in dict_str        |        'two' not in dict_str

字典方法攻略
keys() 、values() 、 items()
keys() :返回字典中的所有键
dict_str.keys()
values() : 返回字典中所有键对应的值
dict_str.values()
items() : 返回字典中所有的键值对
dict_str.items()
返回数据的两个方法:
list() : 将返回的数据转换为 列表
list(dict_str.keys())
利用多重赋值,李彤循环结构将键或值分别赋给不同的变量
for k in dict_str.keys():
print(k,end=’ ‘)
for v in dict_str.values():
…v
for k,v in dict_str.items():
print(‘key: ‘,k,’value: ‘,v)

copy() 方法
    返回一个具有相同键值对的新字典
        dict_str.copy()            // copy 将字典 的数据 拷贝给 字典other
            拷贝原理,有深、有浅。 copy为深拷贝

update() 方法
    使用一个字典所包含的键值对更新已有的字典
        如果已存在的键值对,原value会被覆盖,如不存在,即添加

pop() 方法
    获取指定 key 对应的 value,并删除这个键值对
        dict_stc.pop('key')

popitem()
    随机弹出字典中的一个键值对,弹出字典最后一个键值对,底层存储的最后一个键值对
        dict_str.popitem()

setdefault() 方法
    根据 key 获取对应 value 的值,如获取的key在字典中不存在,会设置默认的value,然后返回该key对应的value
        dict_str.setdefault('one',23)

使用字典格式化字符串
    在字符串模板中按 key 指定变量, 然后通过字典为字符串模板中的 key 设置值 
        字符串模板中使用key
            people = 'name: %(name)s , price: %(price)0.2f, sex: %(sex)s'
            object = {'name':'Hale','price':23,'sex':'man'}
            print(people % object)

Set 集合
保存不重复的元素,集合中的元素是唯一的,set集合是无序的
集合将所有元素放在一堆大括号中{},元素用 ‘,’ 分割
{key,key2,keyN}
只能存储不可变的数据类型,即 整形、浮点型、字符串、元组。 不可以存储 列表、字典、集合。
两种集合类型:
1. set 类型的集合 : 可做 添加、删除元素的操作
2. frozenset 集合 : 不可以

创建set集合
    2种方式:
        1. 使用 {} 创建 :直接将集合赋值给变量
            set_str = {key,key1,keyn...}

        2. set()函数创建
            set_str = set(iteration)
                iteration : 表示字符串、列表、元组、range对象等数据

访问set集合元素
    访问集合元素使用循环结构
        for key in set_str:
            print(key,end=' ')

删除set集合
    del()
        del(set_str)
集合常用操作:向集合中添加、删除元素。以及集合之间做交集、并集、差集等运算。

set集合基本操作(添加、删除、交集、并集、差集)
向set集合中添加元素
set_str.add(element)
只能是数字、字符串、元组或布尔类型,不能添加列表、字典、集合等可变数据
从set集合删除元素
set_str.remove(element)
如删除不存在的,抛出 KeyError错误
如不想提示KetError错误,可使用discard()方法

set集合做交集、并集、差集运算
交集: & 取两集合公共的元素 set1 & set2
并集: | 取两集合全部的元素 set1 | set2
差集: - 取一个集合中另一集合没有的元素 set1 - set2
对称差集: ^ 取集合A和B中不属于A&B的元素 set1 ^ set2

set集合方法
dir(set)
add、clear、copy、difference、difference_update、discard、intersection、intersection_update、isdisjoint、issubset、i是superset、pop、remove、symmetric_difference、symmetric_difference_update、union、update

frozenset 集合(set 集合不可变版本)
特点:
1. 当集合元素不需要改变时,使用frozenset代替set更安全
2. API不需要改变时,必须用frozenset代替set。如 集合元素不可变,set只能包含frozenset
s = set()
f = frozenset(‘key’)
s.add(f)

深入底层字典和集合
字典和集合是进行过性能高度优化的数据结构
字典和集合的工作原理:
数据机构
字典和集合的内部结构都是一张哈希表
对字典:表存储了哈希值(hash)、键和值
对集合:哈希表内只存储单一的元素
哈希表插入数据
哈希表查找数据
哈希表删除元素

字符串常用方法详解

拼接字符串、截取字符串、格式化字符串

字符串拼接(+拼接数字)

使用加号 (+) 作为字符串的拼接运算符
字符串拼接数字
    先将数字转换为 字符串
        数字转换为字符串: str()    repr()
            直接拼接字符串和数字,会报错

截取字符串(字符串切片)
通过索引来操作字符:
string[index] // index 表示索引值,从0开始递增,最后一个为-1
使用范围获取字符串的中间值:
string[start: end: step]
string : 要截取的字符串
start : 要截取的第一个字符所在的索引,默认为0
end :要截取最后一个字符所在的索引。如不指定,默认字符串的长度
step :从start字符开始,step 距离获取一个字符,end索引出的字符。step默认值为1
支持用 in 运算符判断是hi否包含某个子串

split() : 分割字符串
将一个字符串按照指定的分隔符切分成多个子串,字串被保存在列表中,不包含分隔符
str.split(sep,maxsplit)
1. str:要分割的字符串
2. set:指定分隔符,默认使用空字符分割
3. maxsplit:可选参数,指定分割的次数,如不指定,次数不限

join() :合并字符串
将列表(或元组)中多个字符串采用固定的分隔符连接在一起
join_str = str.join(iterable)
1. join_str : 合并后生成的新字符串
2. str : 指定合并时的分隔符
3. iterator: 做合并操作的源字符串数据,允许:列表、元组等

count() :统计字符串出现的次数
用于检索指定字符串在另一个字符中出现的次数,如检索的字符串不存在,返回 0,否则返回出现的次数
str.count(sub[,start[,end]])
1. str : 原字符串
2. sub : 要检索的字符串
3. start : 起始位置
4. end : 终止位置

find() :检测字符串中是否包含某字串
检索字符串中是否包含目标字符串,如包含:出现第一次该字符串的索引,返回-1
1. str:原字符串
2. sub:目标字符串
3. start: 起始位置,若不指定,默认从头开始索引
4. end :结束位置,若不指定,一直检索到结尾
rfind() :字符串从右边开始检索

index() :检测字符串中是否包含某子串
检索是否包含指定的字符串,若指定的字符串不存在,抛出异常
str.index(sub[,start[,end]])
1. str : 原字符串
2. sub : 子字符串
3. start : 起始位置,默认从头开始
4. end : 结束位置,默认到结尾

startswith() 和 endswith
startswith() : 检索字符串是否以指定字符串开头,是返回True,反之False
str.startswith(sub[,start[,end]])
1. str : 原字符串
2. sub : 要检索的字串
3. start :起始位置,默认从头开始
4. end : 结束索引
end.swith(sub[,start[,end]]) : 是否以指定字符结尾,是返回True,反之Flase
str.endswith(sub[,start[,end]])
1. str : 原字符串
2. sub : 检索的字符串
3. start : 起始位置
4. end : 结束位置

字符串大小写转换的三种函数

title()    : 将字符串中每个单词的首字符转为大写,其他转为小写
    str.title()
        str 要进行转换的字符串
lower() : 将字符串所有大写字符转为小写
    str.lower()
upper() : 将字符串所有小写字母转为大写
    str.upper()

去除字符串中空格(删除指定字符)的3种方法
去除字符串中的空格和特殊字符
特殊字符: 制表符 \t、回车符 \r 、换行符 \n
strip() lstrip() rstrip()
1. strip() : 删除字符串前后(左右两侧)的空格或特殊字符
str.strip([chars])
str : 原字符串
chars :指定要删除的字符,可同时指定多个,若不指定,默认为空格、制表符、回车符、换行符等特殊字符
2. lstrip() : 删除字符左边的空格或特殊字符
str.lstrip([chars])
3. rstrip() : 删除字符右边的空格或特殊字符
str.rstrip([chars])

format() 格式化输出
str.format(args)
str : 指定字符串显示样式
args: 指定要进行格式化转换的项,如多项,逗号分割
str 格式:
{ [index][:[fill] align] [sign] [#] [width] [.precision] [type] ]}
index : 指定 ;后边设置的格式要作用到args中第几个数据,索引值从0开始
fill : 指定空白处填充的字符
align : 指定数据的对齐方式
< : 左对齐
> : 右对齐
= : 右对齐,放在填充内容的最左侧,只对数字类型有效
^ : 居中,和width参数一起使用
sign:
+ :
- :
空格:
# :
width :指定输出数据时所占的宽度
.precision : 指定保留的小数位数
type :指定输出数据的具体类型
s : 字符串
d : 十进制整数
c : 将十进制整数自动转换为对应的Unicode字符
e|E : 转为科学技术后,再格式化输出
g|G : 自动再e|f E|F 中切换
b : 将十进制自动转换为二进制,再格式化
o : 转为八进制,再格式化
x|X : 转为十六进制
f|F : 转为浮点数
% : 显示百分比,默认小数点后6位

encode() 和 decode() 字符串编码转换
2中常用字符串类型: str 、bytes。 str:Unicode 。 bytes:二进制数据。使用encode 和decode 进行转换
encode() : 将str类型转换为bytes类型,成为 编码
str.encode([encoding=”utf-8”][,errors=”strict”])
str :要进行转换的字符串
encoding=”utf-8” : 采用的字符编码,默认位utf-8 中文:gb2312
errors=”strict” : 指定错误处理方法:
strict : 遇到非法字符就抛出异常
ignore : 忽略非法字符
replace: 用 “?” 替换非法字符
xmlcharrefreplace: 使用xml的字符引用

decode() : 将bytes类型的二进制数据转换为str类型,过程称为"解码"
    bytes.decode([encoding="utf-8"][,errors="strict"])
        bytes : 要进行转换的二进制数据
        encoding="utf-8": 解码时采用的字符编码
        errors="strict"

dir() 和 help() 帮助函数
dir() : 列出指定类或模块名包含的全部内容,包括函数、方法、类、变量等
help() : 查看某个函数或方法的帮助文档

流程控制

两种基本流程控制结构: 分支结构、循环结构
    分支结构: 实现根本条件来选择性地执行某段代码
    循环结构: 实现根据循环条件重复执行某段代码
    if语句:分支
    while、for in 循环
        break 和 continue 控制程序的循环结构

if条件语句可分3中形式:

    if、if else、 if elif else
        if 表达式:
            代码块
        if 表达式:
            代码块 1
        else:
            代码块 2
        if 表达式 1:
            代码块 1
        elif 表达式 2:
            代码块 2
        elif 表达式 3:
            代码块 3
            ...
        else :
            代码块 n

if 表达式真假值判断方法:
    if 表达式的值 是 布尔值,要么是真 True,假为False
    假 False: False、None、0、 "" 、()、[]、{}

if else 语句用法规范
    1. 代码块不要忘记缩进
    2. if 代码块不要随意缩进
    3. if 表达式不要遗忘冒号

if 语句嵌套
    if condition 1:
        if condition 2:
            code1
        else:
            code2

if condition 1:
    if condition 2:
        code 1
    else: 
        code 2
else:
    if condition 3:
        code 3
    else:
        code 4

pass 语句及作用
    占位

assert 断言函数及用法
    对于对一个bool表达式进行断言,如为True,继续向下执行,否则引发 AssertionError错误
    会让程序奔溃,测试、调试的辅助工具
    eg: age = input('enter age')
        age = int(age)
        assert 30 < age < 100
        print('your age at 30 ~ 100')

while 循环语句

如果条件condition为真,一直重复执行代码块
while 条件表达式:
    代码块
    执行流程:判断条件表达式的值,如果为True 真,执行代码块语句,执行完毕,重新判断条件表达式的值是否为真,如为真,继续执行代码块,直到表达式的值为假False,才终止循环

使用while循环遍历列表和元组
    列表和元组有索引,通过while循环、列表和元组的索引来遍历列表和元组中的所有元素
        eg: tuple_str = ('name','age','sex')
            i = 0
            while(i < len(tuple_str)):
                print(tuple_str[i])
                i += 1

for循环用法

循环语句有2种,while 和for 
    用于遍历字符串、列表、元组、字典、集合等序列类型
        for 迭代变量 in 字符串|列表|元组|字典|集合:
            代码块
        迭代变量:用来存放从序列类型变量中读取的元素,一般不再循环中对迭代变量赋值,代码块值具有相同缩进格式的多行代码。也称为循环体

for 进行数值循环
    如 0 ~100 的累加
    r = 0;
    for i in range(101):
        r += i
    print(r)
range() : 生成一系列连续的整数,用于for循环中
        range(start,end,step)
            1. start : 计数的初始值,默认从0开始
            2. end : 计数的计数值,不能省略
            3. step : 指定步长,两个数之间的间隔,如省略,默认为1
                for i in range(1,10,2):
                    print(i,end=' ')

for循环遍历列表和元组
    列表或元组有几个元素,for循环的循环体就执行几次,迭代变量会依次被赋值为元素的值
isinstance() 函数用于判断某个变量是否为指定类型的实例

for 循环遍历字典
    1. items()    : 返回字典中所有key-value对的列表
    2. keys()    : 所有key的列表
    3. values()    : 所有value 的列表

循环结构中else用法
while 和 for循环,可都跟 else 代码块,作用:当循环条件为False,程序最先执行else代码块的代码
for 循环可使用else代码块,当for把所有元素遍历一次后,会执行else代码块,

for 和while 循环嵌套
for 和 while 可循环嵌套
for i in range(1,10):
j = 0
while j < 3:
print(“i %d, j %d”,(i,j))
j += 1
循环可以两层或者更多层嵌套

列表推导式for表达式
利用range区间、元组、列表、字典、集合等数据类型,快速生成一个列表
[表达式 for 迭代变量 in 可迭代对象 [if 条件表达式]]
for 迭代变量 in 可迭代对象
表达式

元组推导式
利用range区间、元组、列表、字典、集合等,生成一个列表
(表达式 for 迭代变量 in 可迭代对象[if 条件表达式])
使用元组推导式获得新元组或新元组中的元素,有三种方式:
1. 使用tuple(),直接将生成器对象转换为元组
a = (x for x in range(1,10))
print(tuple(a))
2. 使用for循环遍历生成器对象,获得各个元素
a = (x for x in range(1,10))
for i in a:
print(i,end=’ ‘)
print(tuple(a))
3. 使用 next() 方法遍历生成器对象,获得各个元素
a = (x for x in range(3))
print(a.next())
a = tuple(a)
无论是for循环遍历生成器对象,还是next()遍历,遍历后原生成器对象不复存

字典推导式
使用字典推导式可以借助列表、元组、字典、集合以及range区间,快速生成复合需求的字典
{表达式 for 迭代变量 in 可迭代对象 [if 条件表达式]}
[] 扩起来的可省略

集合推导式
集合推导式可借助 列表、元组、字典、集合以及range区间,快速生成符合需求的集合
{表达式 for 迭代变量 in 可迭代对象 [if 条件表达式]}
集合推导式和字典推导式的区别:
如果表达式以键值对(key:value)的形式,则是字典推导式,反之是集合推导式

zip函数用法
可把两个列表 压缩 为一个zip对象(可迭代对象),可使用一个循环并行遍历两个列表
a = [‘a’,’c’,’dd’]
b = [1,3,2]
[x for x in zip(a,b)]
zip函数压缩得到的可迭代对象所包含的元素是由原列表元素组成的元组

reversed 函数及用法
反向遍历,可接收各种序列(元组、列表、区间等),返回一个反序排列的法代器
reversed()可对列表、元组进行反转

sorted函数及用法
与reversed 函数类似,接收一个可迭代对象作为参数,返回一个对元素排序的列表
不会改变传入的可迭代对象,而是返回新的、排序好的列表
sorted 可传入一个 reverse 参数,也可传入一个 key 参数

2种强制离开循环体的方法:
1. continue : 可跳过执行本次循环体中剩余的代码,转而执行下一次的循环
2. break : 完全终止当前循环

函数和lambda表达式

函数是执行特定任务的一段代码,通过一段代码定义成函数,并为该函数指定一个函数名,可多次调用。可代码复用

lambda表达式:可作为表达式、函数参数或函数返回值,可让代码更简洁。

函数(函数定义、函数调用)
    函数定义需注意3点:
        1. 函数需要几个关键的需要动态变化的数据,这些数据被定义成函数的参数
        2. 函数需要传几个重要的数据(就是调用该函数的人希望得到的数据),这些数据被定义成返回值。 
        3. 函数的内部实现过程。

函数的定义

def 函数名(形参列表):
    // 由零条到多条可执行语句组成的代码块
    [return 返回值]
        函数名:函数名是合法的标识符,单词的字符全小写,但此语单词用下划线分隔
        形参列表:定义该函数可接收的参数。形参列表由多个形参名组成,多个用逗号隔开,定义函数指定了形参列表,就必须传入相应的参数值,谁调用函数谁负责为形参赋值。

函数的调用
调用函数就是执行函数
函数名(形参值)
函数名:是调用的函数的名称,形参值:指当初创建函数传入的各个形参的值。即创建函数有多少个形参,就传入多少个值,顺序必须和创建函数一致。函数名后的小括号不能省略。

为函数提供说明文档
使用内置的 help()函数查看帮助文档。 很重要
可为函数编写说明文档, 写在函数声明之后、函数体之前。
可通过help()函数查看,也可通过函数的 doc 属性访问函数的说明文档
def hi():
‘’’
这是说明文档的内容,输出 hi
‘’’
print(‘hi’)
使用help()函数查看该函数的说明文档
help(hi)

函数值传递和引用传递(包括形式参数和实际参数的区别)

函数参数作用:传递数据给函数,令其对接收的数据做具体的操作处理
    形式参数(形参) 和 实际参数(实参)的区别:
        形式参数:在定义函数时,函数名后面括号中的参数就是形式参数
            def hi(name):
                print(name)
        实际参数:在调用函数时,函数名后面括号中的参数成为实际参数,函数的调用者给函数的参数。
            name = 'hale'
            hi(name)
函数参数的传递方式分为2种:值传递和引用(地址)传递
    1. 值传递:参数类型不可变类型(字符串、数字、元组)
    2. 引用(地址)传递: 实参类型为可变类型(列表、字典)
    值传递和引用传递的区别:
        函数参数进行值传递后,如形参的值改变,不影响实参的值。
        函数参数进行引用传递后,若改变形参的值,实参的值也一同改变
            def name(name):

            name('hale')

            def name(name):

            name = ['adam']

函数参数传递机制

1.值传递:实际是将实际参数值的副本(复制品)传入函数,本身不受影响
    值传递会在主程序和swap函数分配两块栈区,用于保存局部变量,实际上在swap 函数战区产生了两个变量,并将主程序栈区的两个变量赋值给swap栈区的两个参数:即对swap函数的两个变量进行初始化。系统存在两个变量,只是存在于不同的栈区中。

函数参数的引用传递
如实际参数的数据类型是可变对象(列表、字典),函数参数的传递方式将采用引用传递方式。但引用传递方式的底层实现,采用的依然是值传递的方式
程序创建了一个字典对象,定义了一个引用变量,其实就是一个指针 指向字典对象, 此时内存中有两个东西:对象本身和只想该对象的引用变量,
具体内容点击

结论:
    1. 不管类型的参数,对参数直接使用 = 符号复制是没用的,使用 = 符号复制不能改变参数
    2. 如函数修改数据,则通过把数据包装成列表、字典等可变对象,然后把列表、字典等对象作为参数传入函数,在函数中通过列表、字典的方法,可改变数据。

python 位置参数
也称 必备参数,指必须按照正确的顺序将实际参数传到函数中,调用函数时传入实际参数的数量和位置必须和定义函数保持一致。
实参和形参数量必须一致
如数量不一致抛出 TypeError异常
实参和形参位置必须一致
若不一致,会出现两中结果:
1. 抛出 TypeError 异常
2. 产生的结果和预期不符

函数关键字参数用法
指使用形式参数的名字来确定输入的参数值,此方式指定函数参数,不需要位置一致,只需参数名正确即可!
def obj(name,age):

obj(age,name)

函数默认参数设置

在定义函数时,直接给形式参数指定默认值
    def 函数名(...,形参名 = 默认值):
        代码块
在调用函数时关键字参数必须位于位置参数的后面,在定义函数时指定了默认值的参数(关键字参数)必须在没有默认值的参数之后

可变参数函数用法
可变参数:不定长参数,传入函数中的实际参数是任意个数。主要形式:
1. 可变参数:形参前添加一个 ‘ * ‘
parameter
parameter 表示形参名,可接收任意多个实际参数,并放到一个元组中
def kebian(name,
obj):
for i in obj:
print(b)
kebian(‘hale’,’judy’,’jally’)
obj 参数可传入多个字符串作为参数值,本质就是一个元组,将参数的多个值集成一个元组
可变的形参可处于形参列表的任意位置
2. 可变参数:形参前添加两个 ‘ * ‘
parameter
parameter 表示形参名,可接任意多个以关键字参数赋值的实际参数,并放到一个字典中。
def obj(name,age,sex, *height,
weight):
print(name,age,sex)
obj(‘hale’,’jally’,’judy’,100,200,110,220)
前三为普通参数,两个字符串收集成元组,最后两个关键字收集为字典

逆向参数收集
    指在程序已有列表、元组、字典等对象,把他们的元素 拆开 后传给函数的参数。需在传入的列表、元组参数前添加一个星号,在字典参数前添加两个星号
        def obj(name,age):
        objs = ['hale',23]
        obj(objs)
    可变参数,如程序将一个元组传给参数,使用逆向收集
        def obj(name,*ages):
            print(name)
            print(ages)
        ages = (12,23,32)
        obj('hale',*ages)

return 函数返回值
用def 创建函数时,可用return 指定返回的值,该返回值可是任意类型,但return在同意函数值可出现一次,只要执行,就会结束函数。
return [返回值]
指定返回值后,在调用函数时,将该函数赋值给一个变量,用变量保存函数的返回值,也可将函数作为其他函数的实际参数
def name(names):
pass
def age(name(name)):
pass

函数返回多个值的方法
将多个值包装成列表后返回,也可直接返回多个值,自动将多个返回值封装成元组

函数递归
在函数体内调用它本身,递归包含了一种隐式的循环。重复执行某段代码,
递归调用,必须在某个时刻函数的返回值是确定的,否则死循环。 递归一定要向已知方向进行
def fn(n):
if n = 10:
return 1
elif n = 20:
return 2
else :
return fn(n+2) - 2*fn(n + 1)

变量作用域(全局和局部)

变量的有效范围:全局和局部
    局部变量:指在函数内部定义并使用的变量,只在函数内部有效
        在执行函数时,为该函数分配一块"临时内存空间",所有局部变量保存在临时变量中,执行完后,释放内存空间,局部变量失效。离开此函数不能再访问,否则抛出NameError
        def name():
            i_name = 'Hale'
        name()
    全局变量:指能作用于函数内部的变量,可在各个函数外部使用,也可在各函数内部使用
        定义方式:
            1. 在函数体外的变量,一定为全局变量:
                i_name = 'Hale'
                def name():
                    print(i_name)
            2. 在函数体内定义使用 global 关键字对变量修饰,即为全局变量
                def name():
                    global your_name = 'Judy'
获取指定作用域范围中的变量
    1. globals() : 该函数返回全局范围内所有变量组成的 变量字典
    2. locals() : 返回当前局部范围内所有变量组成的 变量字典
    3. vars(object) : 获取在制定对象范围内所有变量组成的 变量字典 如不传入 object 参数, vars 和 locals 作用完全相同
globals 和 locals 的区别:
    1. local 获取当前局部范围内所有变量组成的 变量字典, 如全局范围内(在函数外)调用locals 函数,获取全局范围内所有变量组成的 变量字典。 而 globals 无论在哪,获取的都是全局的变量字典
    2. 一般,locals 和 globals 获取的 变量字典 只被访问,不被修改。实际上都可被修改,修改改变全局变量本身。 locals 获取的局部范围内的 变量字典 不会影响局部变量

全局变量和局部变量的遮蔽现象
    全局变量所有函数体内被访问,若函数体中定义了与全局变量同名的变量,会发生遮蔽。
两种方式避免遮蔽:
    1. 访问被遮蔽的全局变量, globals 函数来实现
    2. 在函数中声明全局变量, 使用global 语句来声明全局变量

局部函数及用法(包含 nonlocal 关键字)
在函数体内定义函数,被放在函数体内的函数称为 局部函数
默认情况下,局部函数对外隐藏,在封闭函数内有效,封闭函数返回局部函数,以便在其他作用域中使用局部函数
局部函数内的变量会遮蔽他所在函数内的局部变量
在函数体内声明的变量,赋值语句不是定义新的局部变量,而是访问他所在函数体内的局部变量。
nonlocal 关键字, 可声明访问赋值语句只是访问该函数内的局部变量

函数高级用法

函数本身是一个对象,即可用于赋值,也可用作其他函数的参数,还可作为其他函数的返回值
使用函数变量
    所有函数都是 function 对象,即可把函数本省赋值给变量,就像把整数、浮点数、列表、元组赋值给变量一样
使用函数作为函数形参
    若调用函数能动态传入 某些逻辑代码,就需要在函数中定义函数形参,即可在调用该函数时传入不同的函数作为参数
使用函数作为返回值
    使用函数作为其他函数的返回值

lambda 表达式及用法

使用 lambda 表达式代替局部函数
    lambda [parameter_list] : 表达式
        lambda 几个要点:
            1. lambda 必须使用lambda 关键字定义
            2. 在 lambda 关键字后、冒号左边的参数列表,可没有参数,也可有多个,多个用逗号隔开,冒号右边是该lambda表达式的返回值
            本质:就是匿名的、单行函数体的函数
                lambda x,y :x + y 
                --------------------
                def add (x , y):
                    return x + y
    lambda 两个用途:
        1. 单行函数: 使用lambda 可省去定义函数的过程,代码更简洁
        2. 不需要多次复用的代码,lambda表达式使用完后立即释放,提高了性能

类和对象

封装、继承、多态

面向对象: 一切皆对象

面向对象编程(Object-oriented Programming, OOP),是一种封装代码的方法
    代码封装:隐藏实现功能的具体代码,仅留接口
    分为两部分描述:
        1. 表面特征: 颜色、外观等
        2. 行为:爬、运动
        所有变量都是对象,
    面向对象相关术语:
        类: 即模版,可创建出多个具体实例。称为:类的实例化
        对象: 创造出的实例,称为对象
        属性: 类中所有变量 称为属性
        方法: 类中所有函数 称为方法

定义类
类:仅充当图纸,根据图纸创造对象。 先定义、在创建类的实物对象,通过实例对象实现特定的功能。类名:每个单词首字母大写,单词与单词不能有分隔符
创建类使用 class 关键字
class 类名:
N个类属性
N个类方法

__init__()类构造方法
    用于创建对象使用,没创建一个类的实例对象时,会自动调用它。
        def __init__(self,...):
            代码块
        可包含多个参数,但必须包含 self 此参数,必须作为第一个,类的构造方法至少要一个self参数,self 不需要手动传递参数,python自动给 self传值。
        class Person {
            ''' commit '''
            def __init__(self):
                print('构造方法')
        }

类对象的创建和使用

class 语句只创建类,需手动创建类的对象,创建类对象的过程称为类的实例化
    类名(参数)
    class Person {
        def __init__(self,name,age):
            self.name = name
            self.age = age
    }
    p = Person('Hale',23)

类对象的使用
    作用:
        1. 操作对象的实例变量,即访问、修改实例对象的值,以及给对象添加、删除实例变量
        2. 调用对象的方法。
    类对象访问变量或方法
        使用已创建好的对象访问类中的实例变量
            对象名.变量名
        使用类对象调用类中方法
            对象名.方法名(参数)
            对象与变量名及方法名 用 ' . ' 连接
    给类对象动态添加变量
        为已创建好的对象动态增加实例变量,只要为它的新变量赋值
            xxx.yyy = ['xxxx','xxxx']
        删除变量:
            del xxx.yyy
    给类对象动态添加方法
        如果希望动态增加的方法能自动绑定到第一个参数,可借助于types模块下的MethodType 进行包装

self 用法
    python 类方法中的self 参数相当于 C++中的 this 指针
    同一个类可产生多个对象,当某个对象调用类方法时,该对象会把自身的引用作为第一个参数自动传给该方法,python自动绑定类方法的第一个参数指向调用该方法的对象,即解释器知道操作哪个对象的方法
    对构造方法,self 参数 代表 该 构造方法 正在初始化的对象
    class Obj {
        def __ini__(self):
            pass
        def __name(self):
            pass
        def __jump(self):
            self.name()
    }
        当self参数作为对象的默认引用时,可访问self参数,也可当成实例方法的返回值
        class ReturnSelf {
            def obj(self):
                if hasattr(self,'age'):
                    self.age += 1
                else: 
                    self.age = 1
                return self
        }
        rs = ReturnSelf()        // 实例化

类变量和实例变量(类属性和实例属性)
类中定义的属性和方法,在外部,无法调用。 可把类看做独立的作用域,称为 类命名空间,类属性定义在类命令空间内的变量
类属性 可分为: 类属性也称类变量,和 实例属性也称实例变量
类变量: 指定义在类中,但在各个类方法外的变量,类变量的特点是:所有类的实例化和对象都可共享类变量的值,即类变量可在所有实例化对象中作为公用资源
类变量推荐直接用类名访问,也可用对象名访问
改变类变量的值会作用于该类所有的实例化对象

实例变量(实例属性)
指:定义在类的方法中的属性,特点:只作用于调用方法的对象
实例变量只能通过对象名访问,无法通过类名直接访问
class Obj {
name = ‘hale’
age = 23
def change(self,name,age):
self.name = name
self.age = age
}
one = Obj()
one.change(‘judy’,24)
print(one.name,one.age) // Judy 24
print(Obj.name,Obj.age) // Hale 23

实例方法、静态方法和类方法详解
分为: 类方法、实例方法、静态方法

类实例方法
    实例方法: 至少需要包含一个self参数,用于绑定调用此方法的实例对象,可用类对象、也可用类名调用
        p = Person{}
        p.say('xxx')
        Person.say(person,'xxx')

类方法
    类方法: 至少包含一个参数,名为: cls, 自动将类本身绑定给 cls 参数, cls命名不规定,可随意命名
    类方法需要使用 @classmethod 修饰
        class Obj:
            # @classmethod 修饰的方法是类方法
            @classmethod 
            def name(cls):
                print('Class Method:',cls)
        如没有 @classmethod ,会将 name方法认定为 实例方法,而不是类方法

类静态方法
    静态方法和函数唯一区别: 静态方法定义在类空间,而函数定义在程序所在的空间中
    静态方法没有类似 self cls 特殊的参数,因此不会对包含的参数做任何类或对象的绑定。此方法中无法调用任何类和对象的属性和方法,其和类关系不大
    静态方法使用 @staticmethod 修饰
        class Obj:
            @staticmethod
            def name(p):
                print('static Method', p)
    静态方法的调用:可用类名,也可用类对象
        Obj.name('Class Name')
        o = Obj()
        b.name('Class Object')

类调用实例方法
# 定义全局空间的foo函数
def foo():
pass
# 全局空间的bar变量
bar = 20
class Obj:
# 定义Obj空间的bar变量
def foo():
pass
bar = 200
# 调用Obj空间的函数和变量
foo()
Obj.foo()

总结: python类可调用实例方法,使用类调用实例方法,不会自动为方法的第一个参数self绑定参数值,程序必须显式为参数self传参,称为:未绑定方法

property 函数:定义属性
属性名 = property(fget=NOne, fset=None, fdel=None, doc=None)
fget:制定获取该属性值的类方法, fset:指定设置该属性值的方法, fdel:指定删除该属性值的方法,doc: 提供说明此函数的作用
类似property 函数合成的属性被称为计算属性,并不存储任何状态,值通过某种算法得到,当程序对该值赋值时,被赋的值也会被存储到其他实例变量中

@parperty 装饰器
既保护类的封装特性,又可以用 对象.属性 操作类属性,除了 property 函数,还提供 @property 装饰器,可直接通过方法名来访问方法,
@preperty
def 方法名(self)
代码块
要想实现修改 xx 属性的值,还为xx属性添加 setter方法,可用 setter装饰器
@方法名.setter
def 方法名(self,value):
代码块
可用 deleter 装饰器 删除指定属性
@方法名.deleter
def 方法名(self):
代码块

@函数装饰器及用法
内置的函数装饰器: @staticmethod @classmethod @property
自定义函数装饰器:
1. 将被修饰的函数(函数B) 作为参数传给 @ 符号引用的函数(函数A)
2. 将函数B替换(装饰) 成第一步的返回值

封装机制及实现方法

目的:
    隐藏类的实现细节
    使用预定义方法访问,可加入控制逻辑,限制对属性的不合理访问
    进行数据检查,保证完整性
    便于修改,提高可维护性
实现封装的两方面:
    1. 将对象的属性和实现细节隐藏起来,不可直接访问
    2. 暴露方法,对属性安全访问和操作
定义:
    类的成员命名以双下划线开头,就能隐藏
    class User:
        def __hide(self):
            pass
        def getname(self):
            return self.__name

继承机制及作用

实现继承的类称为 子类, 被继承的类称为 父类,也叫 基类、超类
子类继承父来的语法:在定义类时,将多个父类放在子类后的圆括号内
    class 类名(父类1,父类2,...):
        类定义部分
Python是多继承机制,一个类可继承多个父类
    object 类是所有类的父类,直接父类、间接父类
    子类是对父类的扩展,子类是一种特殊的父类
    从子类的角度看,子类扩展(extend)类父类,从父类的角度看,父类派生出(derive)出子类,
    class Fruit:
        def name(self):
            pass
    class Animal:
        def name(self):
    class Obj(Fruit,Animal):
        pass

父类方法重写
子类与父类同名的方法为方法重写(Override),也叫方法覆盖
使用未绑定方法调用被重写的方法

super()函数:调用父类的构造方法
子类继承父类的构造方法,如子类有多个直接父类,优先选择最前面父类的构造方法
如果子类重写了父类的构造方法,子类的构造方法必须调用父类的构造方法
子类调用父类构造方法的2种方式:
1. 使用未绑定,构造方法是实例方法,可通过此方式来调用
2. 使用super()函数调用父类的构造方法
当子类继承多个父类时,super()只调用第一个父类的构造方法,其他父类构造方法只能使用 未绑定 的方式调用

slots(两侧得都有两下划线): 限制类实例动态添加属性和方法
slots 属性的值是一个元组,该元组的所有元素列出了该类的实例允许动态添加的所有属性名和方法名

type(): 动态创建类
type 可指定三个参数:
1. 参数一: 创建的类名
2. 参数二: 该类继承的父类集合,使用元组指定多个父类
3. 参数三: 该字典对象为该类绑定的类变量和方法

MetaClass 元类
创建类的来,即创建类后,再由类来创建实例进行应用,使用元类可在创建类时动态修改类定义
定义元类: 类名以MetaClass 结尾,元类需要定义并实现 new()方法,一定要有返回值
new()方法作用: 使用class定义新类时,如指定了元类,new方法会被自动执行

多态

同一变量完全可在不同的时间应用不同的对象,当同一变量在调用同一方法,可呈现多种行为
    class Bird:
        def move(self,filed):
            print('bird %s' % filed)
    class Dog:
        def move(self,filed):
            print('dog %s' % field)
    b = Bird()
    b.move('sky')
    d = Dog()
    d.move('road')

枚举类定义和使用
定义方式:
1. 使用Enum 列出多个枚举值来创建枚举类
2. 通过继承Enum 基类来派生枚举类
import enum
s = enum.Enum(‘name’,(‘hale’,’judy’,’jally’))
可通过枚举类变量名或枚举值来访问指定枚举对象
members 属性,返回一个dict字典,包含了该枚举的所有实例,遍历 members属性访问枚举的所有实例

枚举构造器
    可定义构造器,为枚举定义构造器后,在定义枚举实例时,必须为构造器参数设置值

异常处理机制

异常机制主要依赖try、except、else、finally、raise 五个关键字

    1. try 关键字后的代码块简称 try块,放置 引发异常的代码
    2. 在except 后对应的是异常类型和一个代码块,表明该 except 块处理这种类型的daimakaui    
    3. 多个except 后放一个 else 块,程序不出现异常还是执行 else 块
    4. finally 块用于回收在 try 打开的无力资源,异常机制保证 finally 块总被执行
    5. raise 用于引发实际的异常,可单独作为语句使用,引发具体异常对象

常见异常类型
    语法错误 和 运行时错误
        语法错误:
            解析代码是出现的错误,报出 SyntaxError 语法错误
        运行时错误:
            逻辑错误
    常见异常类型
        AssertionError : 当assert 关键字后的条件为假时,程序停止并抛出此异常
        AttributeError : 试图访问的对象属性不存在时,抛出
        IndexError       : 索引超出序列范围
        KeyError       : 字典中查找一个不存在的关键字时
        NameError       : 尝试访问一个未声明的变量时
        TypeError       : 不同类型数据之间的无效操作
        ZeroDivisionError : 除法运算中除数为 0 引发此异常

异常处理机制

try except 异常处理
try:
可能产生异常的代码块
except ([Error1,Error2,…]) [as e]: // ErrorN 表明处理异常的具体类型
处理异常的代码块1
except ([Error3,Error4,…]) [as e]: // as e 表示将异常类型赋值给变量 e
处理异常的代码块2
执行流程:
1. 先执行 try 中的代码块,如出现异常,自动生成异常对象,提交给Python解释器,称为引发异常
2. 解释器收到异常,寻找处理该异常对象的except块,如找到合适的except块,则把异常对象交给except块,如找不到,则终止。
try:
name = input(‘Enter name’)
except (ValueError, ArithmeticError):
print(‘error’)
except :
print(‘no error’)
访问异常信息
通过except块添加 as e 来访问异常对象的相关信息,当解释器决定调用某个except块来处理该异常对象时,会将异常对象赋值给except 块后的异常变量
异常对象包含的属性和方法:
args : 返回异常的错误编号和描述字符串
errno : 返回异常的错误编号
strerror : 返回异常的描述字符串
with_traceback() : 处理异常的传播轨迹信息

异常类的继承体系
    异常类派生于 BaseException,
    [继承关系](http://c.biancheng.net/uploads/allimg/190215/2-1Z2151H054Q0.gif)
    BaseException 主要子类是 Exception,不管是系统的异常类,还是用户自定义的异常类,都应该从Exception派生
        1. 如发生数值类型错误,则调用ValueError 对应的except块处理该异常
        2. 如发生逻辑错误: 2/0,则调用ArthmeticError 对应的except块处理该异常
        3. 如运行时发生其他异常,异常对象是Exception类或其他子类的实例,则调用Exception对应的except块处理该异常
        注: 先捕获小异常,再捕获大异常。

try except else 异常处理结构
作用:指定当try块中没有发现异常时要执行的代码,若try中发现异常,则else块中的语句不会执行
try:
r = 20 / int(input(‘enter num’))
except ValueError:
pass
except ArithmeticError:
pass
else:
print(‘no error’)

try except finally : 资源回收
完整异常处理语法结构:
try:
xxx
except SubException as e:
xxx
except SubException as e:
xxx
else:
xxx
finally:
xxx
在异常处理语法结构中,只有try块是必须的
1. 如没有try块,就没有except 和 finally 块
2. except 和 finally 块可选,可同时出现,也可出现其一
3. 可有多个except块,但捕获父类异常的except块位于捕获子类异常的后面
4. 不能只有try块,没有except finally块
5. 多个except 位于try块后,finally块位于所有except块之后
不要在finally块中使用如return 或 raise 等导致方法终止的语句,若在finally中使用return 或 raise 语句,会导致 try 块、except、中的return、raise语句失效

raise 用法

    如需自行引发异常,则使用raise语句
        raise [exceptionName [(reason)]]
    raise 语句三种用法
        1. raise: 单独一个raise。 引发当前上下文中捕获的异常,如在except中,引发 RuntimeError异常
        2. raise异常类名称: 指定异常类的默认实例
        3. raise异常类名称: 引发指定异常的同时,附带异常的描述信息
raise 不需要参数
    class Demo:
        def __init__(self,init_price):
            self.init_price = init_price
        def bid(self,bid_price):
            try:
                xxx
            except Exception as e:
                xxx
                raise 

except 和 raise 同时使用
    实现通过多个方法协作处理同一个异常,可在except中结合raise语句来完成
        try:
            xx
        except Exception as e:
            raise AuctionException('error message by self')
            raise AuctionException(e)
        对异常的处理分为两个部分:
            1. 应用后台需要通过日志来记录异常发生的详细情况
            2. 根据异常向应用使用者传达某种提示
    用户自定义异常对原始异常进行包装
        raise AuctionException(e)
        被称为: 异常包装或异常转译
自定义异常类
    自定义异常需继承Exception 基类 或Exception的子类,
    class AuctionException(Exception):
        pass

sys.exc_info(): 获取异常信息

捕获异常的2种方式获得更多的异常信息:
    1. 使用 sys 模块中的 exc_info 方法
    2. 使用 traceback 模块中的相关函数
两个方法返回异常全部信息:exc_info 和 last_traceback 
        exc_info : 将当前的异常信息以元组的形式返回,该元组包含三个元素, type、value、traceback
                type: 异常类型的名称
                value: 捕获到的异常实例
                traceback: 是一个traceback 对象
                    try:
                        pass
                    except:
                        print(sys.exc_info())

traceback模块: 获取异常信息

使用traceback 模块查看异常传播轨迹,现将traceback模块引入,提供两个方法:
    1. traceback.print_exec() : 将异常传播轨迹信息输出到控制台或指定文件中
    2. format_exc()    : 将异常传播轨迹信息转换为 字符串
print_exception(etype,value,tb[,limit[,file]])
    三个参数用于分别指定异常的信息:
        etype : 指定异常类型
        value : 指定异常值
        tb : 指定异常的traceback 信息
print_exc([limit[,file]])
    print_exc 自动处理except块所捕获的异常,涉及两个参数
        1. limit : 限制显式异常传播的层数
        2. file  : 指定将异常传播轨迹信息输出到指定文件中,如不指定该参数,默认输出到控制台
        import traceback
        def xxx():
            raise SelfException('by self')
        try:
            xxx()
        except:
                traceback.print_exec(file=open('log.txt','a'))

自定义异常类及用法

自定义一个异常类,通常应继承自 Exception类,直接继承Exception。也可继承自从Exception 继承而来的类,间接继承Exception
[异常继承图](http://c.biancheng.net/uploads/allimg/190819/2-1ZQ9154321244.gif)

异常机制使用细节
成功的异常处理4个小目标
1. 使程序代码混乱最小化
2. 捕获并保留诊断信息
3. 通知合适的人员
4. 采用合适的方式结束异常活动
不要过度使用异常
1. 把异常和普通错误混淆
2. 使用异常处理代替流程控制
3. 不要使用过于庞大的try块
4. 不要忽略捕获到的异常

assert 调试程序
assert 条件表达式 [,描述信息]
assert 语句作用: 当条件表示式为真时,什么也不做,如果为假,则assert抛出 AssertionError异常
age = input(‘enter age’)
name = input(‘enter name’)
assert 20 < age < 100 , ‘age at 20 ~ 100’
assert 可与 try except 异常处理语句配合使用
try:
age = input(‘enter age’)
assert 20 < age < 100 , ‘age at 20 ~ 100’
except AssertionError as e:
print(‘age is error’)

类特殊成员(属性和方法

特殊: 方法名、属性名前后添加双下划线。 可重写或调用方法来实现特殊的功能

如: 构造方法: __init__ ,通过重写类中的此方法实现自己的初始化逻辑
__repr__()方法 : 显式属性
__del__方法:    销毁对象
__dir__用法 : 列出对象的所有属性(方法)名
__dict__属性: 查看对象内部所有属性名和属性值组成的字典
setattr() , getattr() , hasattr()
issubclass , isinstance : 检查类型
__call__
__getitem__ , __setitem__ , __delitem__ , __len__, __contains__
__iter__ , __reversed__ :实现迭代器
生成器详解

__repr__()方法: 显式属性
    class xxx:
        def __init__(self,xx,xxx):
            self.xx = xx
            self.xxx = xxx
    x = xxx('xx','xxx')
__repr__(): 所有的类都是object类的子类,所以所有的对象都具有 __repr__()方法
    此方法:用于实现: 当程序直接打印该对象时,系统输出该对象的 自我描述 信息,用来告诉外界该对象具有的状态信息
    此方法返回该对象实现类的 "类名+object at + 内存地址",如
    class obj:
            def __init__(self,xx,xxx):
                self.xx = xxx
                self.xxx = xxx
            def __repr__(self):
                return "self.xx" + self.xx + \
                    ,"self.xxx" + self.xxx 
    x = obj('xx','xxx')
重写__repr__() 总是返回该对象的所有令人感兴趣的信息所组成的字符串,如下格式的字符串
    类名 [field1 = 值1, filed1 = 值2 ,...]

__del__方法: 销毁对象
    与 __init__() 方法对象, __init__() 方法用于初始化对象,  __del__() 方法用于销毁python对象,在任何对象被系统收回之时,会自动调用该对象的 __del__() 方法
如不需要一个对象时,必须把对象占用的内存空间释放,称为垃圾回收(GC, Garbage Collector)
    Python 采用自动引用计数(ARC) 方式来回收对象所占用的空间
    当对象被垃圾回收时,自动调用该对象的 __del__方法,当对象的引用计数变为0时,对象才会被回收
    class Obj:
        del __init__(self,xx):
            self.xx = xx
        def __del__(self):
            pass
    o = Obj('xx')
    x = o
    del x
若父类提供 __del__()方法,则系统重写 __del__() 方法时必须显式调用父类的 __del__()方法,保证合理回收父类实例的部分属性

__dir__用法:列出对象的所有属性(方法)名
    用于列出该对象内部的所有属性包活方法名,该方法将会返回包含所有属性方法名的序列
    当程序对某个对象执行 dir 函数时,将该对象的__dir__() 方法返回值进行排序,然后包装成 列表
    class obj:
        def __init__(self,xxx):
            self.xxx = xxx
        def info():
            pass 
    x = obj
    print(x.__dir__())
        不仅输出对对象定义的xxx 、info属性和方法,还有系统内置的属性和方法,如 __repr__, __del__方法

__dict__属性: 查看对象内部所有属性名和属性值组成的字典
    即可看对象的所有内部状态,也可通过字典语法来访问或修改指定属性的值
        class obj:
            def __init__(self,xxx):
                self.xxx = xxx
        x = obj
        print(x.__dict__)
        print(x.__dict__(xxx))

setattr(), getattr(), hasattr() 函数用法

动态检测对象是否包含某些属性或方法相关的函数:
    1. hasattr(obj,name) : 检查obj对象是否包含名为 name 的属性或方法
    2. getattr(object,name[,default]) : 获取object对象中名为 name 的属性的属性值
    3. setattr(obj,name,value,/) : 将obj对象的 name 属性设为 value
        class obj:
            def __init__(self,xx,xxx):
                self.xx = xx
                self.xxx = xxx
            def info():
                pass
        x = obj
        print(hasattr(x,'xxx'))        // True
        print(getattr(x,'xxx'))        // True
        setattr(x,'xx','yy')
        print(x.xx)
setattr() 可改变 对象的属性值,若对象设置的属性不存在,可添加属性

issubclass 和 isinstance 函数 : 检查类型

issubclass(cls, clsss_or_tuple) : 检查cls是否为后一个类或元组包含的多个类中任意类的子类
isinstance(obj, class_or_tuple) : 检查obj是否为后一个类或元组包含的多个类中任意类的对象
区别:
    issubclass 第一个参数是类名,判断是否为子类
    isinstance 第一个是变量,判断是否为该类或子类的实例                         
    第二参数都可使用 元组
__base__属性 : 通过该属性可查看该类的所有直接父类,该属性返回所有直接父类组成的元组
    class x:
        pass
    class xx:
        pass
    class xxx(x,xx):
        pass
    print(x.__base__)
    print(xx.__base__)

__subclasses__()方法 : 查看该类的所有直接子类,返回该类的所有组成的列表
    print(x.__subclasses__()

__call__方法
    hasattr 函数判断指定属性或方法是否存在,  __call__属性: 进一步判断该属性或方法是否可调用
    class x:
        def __init__(self,xx,xxx):
            self.xx = xx
            self.xxx = xxx
        def ValidLogin(self):
            pass
    x = x('xx','xxx')
    print(x.xx,'__call__')
    print(x.xxx,'__call__')

__getitem__ , __setitem__ , __delitem__ , __len__ , __contains__ 
    序列的特征 可包含多个元素,相关的特殊方法:
        __len__(self)    : 返回值决定序列中元素的个数
        __getitem__(self,key) : 获取指定索引对应的元素,key是整数值或slice对象,会引发 KeyError异常
        __contains__(self,item) : 判断序列是否包含指定元素
        __setitem__(self,key,value) : 指定设置索引对应的元素,key 是整数值或slice对象,否则引发 KeyError异常
        __delitem__(self,key) : 删除指定索引对应的元素
    def xx(key):
        if not isinstace(key,int) : raise TypeError('must int')
        if key < 0 : raise IndexError('is ini')
    class xxx:
        def __init__(self):
            self.__changed = {}
            self.deleted = []
        def __len__(self):
            pass
        def __getitem__(self,key):
            pass
        def __setitem__(self,key,value):
            pass
        def __delitem(self,key):
            pass

iterreversed :实现迭代器

for循环遍历列表、元组和字典等对象都是可迭代的,都属于迭代器
实现迭代器,需实现两个方法:
    1. __iter__(self) : 该方法返回一个迭代器,必须包含一个 __next__() 方法,返回迭代器的下一个元素
    2. __reversed__(self) : 为内奸的reversed 反转函数提供支持,对指定迭代器执行反转时
class  x:
    def __init__(self,len):
        self.first = 0
        self.sec = 1
        self.__len = len
    def __next__(self):
        if self.__len == 0:
            raise StopIteration
            self.first, self.sec = self.sec, self.first + self.sec
            self.__len -= 1
            return self.first
    def __iter__(self):
        return self

生成器

生成器与迭代器的区别在于: 迭代器通常是先定义一个迭代器类,通过创建实例来创建迭代器。 生成器:先定义一个包含 yield 语句的函数,然后通过调用该函数来创建生成器
创建生成器
    创建生成器的步骤:
        1. 定义一个包含 yield 语句的函数
        2. 调用第一步创建的函数得到生成器
            def xxx(val,step):
                xx = 0 
                for i in range(val):
                    xx += i * step
                    yield xx
        yield xx 语句的作用:
            1. 每次返回一个值,类return 语句
            2. 冻结执行,每次执行到yield 语句时会被暂停

两种方式创建生成器:

    1. 使用 for 循环的生成器推导式
    2. 调用带 yield 语句的生成器函数
生成器的方法:
    1. 外部程序通过 send 方法发送数据
    2. 生成器函数使用 yield 语句接收收据
生成器两个常用方法:
    1. close :用于停止生成器
    2. throw :用于在生成器内部(yield语句内) 引发一个异常

模块和包

常用 sys、OS 、 traceback 三个模块

模块化编程
    模块: Modules, 模块就是Python程序,
        代码的可重复性体现在,编写好模块后,需用到该模块中的某个功能(由变量、函数、类实现),无需重复性工作,直接导入该模块即可使用该功能
    封装特性的结构:
        1. 容器: 例 列表、元组、字符串、字典等,都是对数据的封装
        2. 函数: 对Python代码的封装
        3. 类: 对方法和属性的封装,对函数和数据的封装
    语法格式:
        模块名.函数

导入模块: import

    1. import 模块名1 [as 别名] , 模块名2 [as 别名2],... : 
        导入指定模块中的所有成员(变量、函数、类等)。 当使用模块中的成员时,需要该模块名(或别名)作为前缀
    2. from 模块名 import 成员名1 [as 别名1], 成员名2 [as 别名2] ,... :
        只会导入模块中指定的成员,非全部成员,使用该成员时,无需任何前缀,直接使用成员名或别名
        第二种from 模块名 import * : 可导入指定模块中的所有成员

import 模块名 as 别名
    1. 导入整个模块: import sys
    2. 导入整个模块,指定别名: import sys as s
    3. 导入多个模块: import sys,os
    4. 导入多个模块,同时指定别名: import sys as s, os as o

from 模块名 import 成员名 as 别名
    1. 导入模块的成员 : from sys import argv
    2. 导入模块成员,指定别名 : from sys import argv as v
    3. 导入模块成员,一次导入多个成员 : from sys import argv , winver
    4. 导入多个模块,指定别名 : from sys import argv as v, winver as w

    不推荐使用 from import 导入模块所有成员

自定义模块

    模块文件的文件名是它的模块名,如 xxx.py 的模块名就是 xxx
自定义模块编写测试代码
    如直接运行模块文件,程序会执行该模块的函数,如其他程序导入该模块,不应该执行该模块的函数
    要实现这个效果,需借助 python 内置的 __name__ 变量,当直接运行模块时, name 变量的值为 __main__; 而将模块被导入其他程序并运行该程序,处于模块中 __name__ 变量的值就编程类模块名。 如希望函数只有在直接运行模块文件时才执行,则可增加判断,即只有当 __name__ == '__main__' 时才调用函数

__name__ == '__main__' :
    __name__ :内置的系统变量,用于标识所在模块的模块名,
    作用: 确保只有单独运行该模块时,此表达式才成立,才可以进入此判断语法,执行其中的测试代码,反之,如只是作为模块导入到其他程序文件中, 则此表达式不成立,若运行,不执行判断语句汇中的代码

导入模块的三种方式

当时用import 语句导入模块后,会按顺序查找指定的模块文件
    1. 当前目录,即当前执行的程序程序文件所在目录下查找
    2. 到Pythonpath 环境变量下的每个目录下查找
    3. 到python 默认的安装目录下查找
自定义的模块:
    1. 向sys.path 中临时添加模块文件存储位置的完整路径
    2. 将模块放在sys.path 变量中已包含的模块加载路径中
    3. 设置系统环境变量
导入模块方式一: 临时添加模块完整路径
导入模块方式二: 将模块保存到指定位置
导入模块方式三: 设置环境变量
    linux 设置环境变量:    
        vim ~/.bash_profile 
            PATHONPATH=.:/pythondir/python_module
            export PYTHONPATH

导入模块的本质

    将xxx.py 中的全部代码加载到内存并执行,然后将整个模块内容赋值给与模块同名的变量,该变量的类型是module,而在该模块中定义的所有程序单元都相当于该module对象的成员
    导入同一模块多次,只执行一次

__all__ 变量
    将变量的值设置为一个列表,只有该列表中的成员才会被暴露出来
        def xx():
            pass
        def xxx():
            pass
        __all__ = ['xx','xxx']
    意义:为模块定义开放的公公接口,只有 __all__变量列出的成员,
    如使用模块内__all__ 列表之外的成员,两种解决方法
        1. import 模块名  导入模块, 通过模块名前缀来调用模块内的成员
        2. from 模块名 import 成员 ,导入指定成员

包: 存放多个模块的文件夹
    包:就是文件夹,必须存在名为 "__init__.py" 的文件
        每个包的目录下必须建立一个 __init__.py 的模块,可为空模块
        作用: 将目录当成包来处理

创建包,导入包

    包的创建步骤:
        1. 创建文件夹,名称为该包的包名
        2. 文件夹内创建名为 __init__.py 的文件,
    包的导入:
        1. import 包名[.模块名[as 别名]]
            import xx.xxx
        2. from 包名 import 模块名 [as 别名]
            from xx import xxx
        3. from 包名.模块名 import 成员名 [as 别名]
            from xx.xxx import xxxx

__init__.py
    导入包等同于导入包中的__init__.py文件,可在此文件中编写功能代码。 包作用是包含多个模块,因此此文件作用是导入该包内的其他模块

查看模块(变量、函数、类)方法
    2种方式:
        1. dir()
            dir(xxx)
        2. __all__
            xxx.__all__

__doc__属性:查看文档
    help(string.capwords)
    print(string.capwords.__doc__)

__file__属性:查看模块的源文件路径
    import string        
    string.__file__

第三方模块(库)下载和安装(通过pip命令)
    pip install | uninstall | list  模块名

常见模块

日期、时间、正则表达式、JSON支持、容器等

sys 模块用法
    和解释器关系密切的标准库,帮助访问和解释器联系密切的变量和函数
    import sys
    [e for e in dir(sys) if not e.startswith('__')]        // 双下划线 没有 __all__变量

sys模块包含全部成员 包括变量、函数等

    sys成员:
        sys.argv : 获取运行python 程序的命令行参数
        sys.path : 一个字符串列表,每个字符串都是一个目录名,使用import导入模块时,解释器从这些目录中查找指定的模块
        sys.exit() : 引发SystemExit 异常退出程序
        sys.modules : 返回模块名和载入模块对应关系的字典
        sys.platform : 是字符串,标识解释器运行平台名称,即操作系统名称
        sys.stdin、sys.stdout、sys.stderr : 类文件流对象,表示 标准输入、输出、错误
        sys.flags : 只读属性返回运行py命令时指定的旗标
        sys.getrefcount(object) : 返回指定对象的引用计数,当objet对象引用计数为0,系统会回收该对象
        sys.getfilesystemencoding(object) : 返回系统中保存文件所用的字符集
        sys.getrecursionlimit() : 返回当前支持的递归深度
        sys.getswitchinterval()    : 返回解释器中线程切换的时间间隔,通过setswitchinterval 函数改变
        sys.implementation : 返回当前py解释器的实现
        sys.maxsize : 返回整数支持的最大值,32: 2**31 -1  64: 2**63-1 
        sys.executable : 返回解释器在从磁盘的存放路径
        sys.byteorder : 显示本地字节序的指示符
        sys.copyright : 返回与解释器有关的版权信息
        sys.version : 返回解释器版本的版本信息
        sys.winver : 返回解释器的主版本号
sys获取运行参数:
    sys 模块的argv 属性可获取运行py程序的命令行参数,argv是一个列表
        python argv xx.py "xxx"
动态修改模块加载路径:
    import sys
    sys.path.append('E:\xxx')

os 模块及用法

    代表程序所在的操作系统,用于获取程序运行所在操作系统的相关信息
    import os 
    os.__all__ : 查看该模块所包含的全部变量和函数
        os.name :返回依赖模块操作系统的名称,如 posix、nt、java等
        os.environ : 所有环境变量组的字典
        os.sep : 路径分隔符
        os.fsencode(filename) : 对类路径 path-like 的文件名进行编码
        os.fsdecode(filename) : 对类路径 path-like 的文件名解码
        os.PathLie : 是类,代表 类路径对象
        os.getenv(key,default=None) : 指定环境变量的值
        os.getlogin() : 系统登录用户名。 os.getuid、os.getgroups、os.getgid 等函数,获取用户 ID 、用户组、组ID
        os.getpid() : 进程ID
        os.getppid() : 进程的父ID
        os.putenv(key,value) : 设置环境变量
        os.cpu_count() : 系统的CPU数量
        os.pathsep : 系统上多条路径之间的分隔符。如 ; :
        os.linesep : 系统的换行符
        os.urandom(size) : 加密使用、最多由N个字节组成的bytes对象
    os 进程管理函数:
        os.system(command) : 运行操作系统上的指定命令
        os.abor() : 生成一个SIGABRT 信号给当前进程,UNIX上默认生成内核转储;windows 退出返回3
        os.execl(path,arg0,arg1,...) : 一系列功能类似的函数,如: os.execle、os.execlp等
        os.forkpty() : fork 一个子进程
        os.kill(pid, sig) : 将sig 信号发送到pid对应的过程,用于结束进程
        os.killpg(pgid,sig) : 发送到 pgid 对应的进程组
        os.popen(cmd, mode='r',buffering=-1) : 向cmd命令打开读写管道,buffering村冲参数,返回文件对象用于读写字符串,而不是字节
        os.startfile(path,operation) : 对指定文件使用关联工具执行operation对应的操作

random 模块及用法
    查看该模块所包含的全部变量和函数: import random   random.__all__
    random 模块常用函数:
        random.seed(a=None,version=2) : 指定种子来初始化伪随机数生成器
        random.randrange(start,stop[,step]) : 返回从start开始到stop结束,步长为 step的随机数
        random.randin(a,b) : 生成一个范围为 a <= N <= b 的随机数
        random.choice(seq) : 从seq 中随机抽取一个元素,如为空,抛 IndexError异常
        random.choices(seq,weights=None,cum_weights=None,k=1) : 从seq 抽元素,weigths指定被抽取的权重
        random.shuffle(x[,random]) : 对 x 序列执行洗牌 随机排序 操作
        random.sample(population,k) : 从population序列中随机抽取k个独立元素
        random.random() : 生成从包含0.0 到不包含1.0之间的随机浮点数
        random.uniform(a,b) : 生成一个范围 a ~ b 的随机数
        random.expovariate(lambd) : 生成呈指数分布的随机数,lambda 参数

time 模块

查看time模块包含的全部属性和函数
    import time
    time.__all__        |  [e for e in dir(time) if not e.startswith('__')]
        time.struct_time : 代表一个时间对象,包含9个属性
            tm_year 年
            tm_mon 月    | tm_mday 日    | tm_hour 时    | tm_min 分
            tm.sec 秒    | tm_wday 周    | tm_yday 一年内第几天 | tm_isdst 夏令时
    time 函数:
        time.asctime([t]) : 将时间元组或struct_time转换为时间字符串
        time.ctime([secs]) : 以秒数的时间转换时间字符串
        time.gmtime([secs])    : 将秒数的时间转为 struct_time 对象
        time.localtime([secs]) : 
        time.mktime(t)
        time.perf_counter()
        time.process_time()
        time.sleep(secs)
        time.strftime(format[,t])
        time.time()
        time.timezone
        time.tzname
        [详细点击](http://c.biancheng.net/view/2420.html)

json 模块

JSON: JavaScript Object Notation ,即javascript对象符号,数据交换格式
两种数据结构:    
    1. 由 key-value 对组成的数据结构
    2. 有序集合, 在python中对应于列表
使用JSON语法创建对象:
    object = {
            xxx:xxxx,        // 字符串用双引号包含
            yyy:yyyy,
    }
JSON创建数组:
    arr = [value1,value2,...]

JSON for Python
    JSON 类型转换 Python 类型
        对象(object)                字典(dict)
        数组(array)                    列表(list)
        字符串(string)                字符串(str)    
        整数(number(int))            整数(int)        
        true                        True
        false                        False
        null                        None

    Python类型转换为 JSON类型
        字典(dict)                        对象(object)
        列表(list)和元组(tuple)            数组(array)
        字符串(str)                        字符串(string)
        整形、浮点型、派生的枚举        数值型(number)
        True                            true
        False                            false
        None                            null 
查看模块所有属性和函数: import json -> json.__all__
常用函数和类的功能:
    json.dump(obj,fp,*,skipkeys=False...)    // 将obj对象转换成json字符串输出到fp流中
    json.dumps(obj,*,skipkeys=False,...)    // 将obj对象转换为JSON字符串,并返回该JSON字符串
    json.load(fp,*,cls=NONE,object_hook=None,...)    // 从fp流读取JSON字符串,将其恢复成JSON对象, fp支持write()方法的类文件对象
    json.loads(s,fp,*,encoding=None,cls=None,object_hook=None,...)    // 将JSON字符串s恢复为JSON对象

正则表达式

Regular Expression 描述一种字符串匹配的模式,检查一个字符串是否含有某个子串,也可从字符串中提取匹配的子串,或对字符串中匹配的子串执行替换操作,可用来开发数据抓取、网络爬虫等

查看该模块所包含的属性和函数: import re -> re.__all__
函数作用:
    re.complie(pattern, flags=0) : 将正则表达式字符串编译成 _sre.SRE_Pattern 对象, 该对象代表正则表达式编译之后在内存中的对象,可缓存并复用正则表达式字符串,如多次使用同一正则表达式字符串,则可先编译它。
        pattern : 所编译的正则表达式字符串, flags : 匹配旗标,
    re.match(pattern , string, flags=0) : 从字符串开始位置匹配正则表达式,如匹配不成功,match 函数返回None,
        pattern : 正则表达式。 string :匹配的字符串。 flags : 正则表达式的匹配旗标。
    re.search(pattern, string, flags=0) : 扫描整个字符串,返回字符串中第一处匹配pattern的匹配对象,
        pattern : 正则表达式。 string :被匹配的字符串。 flags : 匹配旗标
    re.findall(pattern, string, flags=0) :  扫描整个字符串,返回字符串中所有匹配pattern的子串组成的列表。 
        pattern : 正则表达式。 string :被匹配的字符串。 flags : 匹配旗标
    re.finditer(pattern, string, flags=0) : 扫描整个字符串,返回字符串所有匹配pattern 的子串组成的迭代器。
        pattern : 正则表达式。 string :被匹配的字符串。 flags : 匹配旗标
findall、finditer 、search 的区别: search 只返回字符串中第一处匹配pattern的子串,findall和finditer 返回字符串中所有匹配pattern的子串
    re.fullmatch(pattern,string, flags=0) :要求整个字符串匹配 pattern,如匹配返回包含匹配信息的 _sre.SRE_Match对象,否则 None
    re.sub(pattern,repl,string,count=0,flags=0) :将string字符串中所有匹配pattern的内容替换成repl
            repl : 被替换的字符串,可是函数, count 控制替换的次数。
        import re
        date = '2019-08-22'
        print(re.sub(r'-','/',date))
            r'-' :是原始字符串, r : 代表原始字符串,通过原始字符串,避免对字符串中的特殊字符转译
                r'(?P<lang>\w+)' : 正则表达式用圆括号表达式创建一个组, '?P' 为该组起名为 lang , \w+ 是正则表达式的内容,代表一个或多个任意字符,
    re.split(pattern,string, maxsplit=0, flags=0) : 使用pattern对string进行分割,返回分割得到的多个子串组成的列表,maxsplit 参数控制分割的次数
    re.purge() : 清楚正则表达式缓存
    re.escape(pattern) :对模式中除ASCII字符、数值、下划线之外的其他字符转义
    re模块中的Match 对象是match 、search方法的返回值,包含了详细的正则表达式匹配信息,包含匹配的位置、子串
    sre.SRE_Match 对象包含如下方法或属性:
        match.group([group1,...]) : 获取该匹配对象中指定组所匹配的字符串
        match.__getitem__(g) : match.group(g)的简化写法。
        match.groups(default=None) : 返回match对象中所有组所匹配的字符串组成的元组
        match,groupdict(default=None) :返回match对象中所有组所匹配的字符串组成的字典
        match.start([group]) :获取该匹配对象中指定组所匹配的字符串的开始位置
        match.end([group]) :获取结束位置
        match,span([group]) :获取开始和结束位置,相当于同时返回 start 和 end方法的返回值
        match.pos : 该属性返回传给正则表达式对象的search , match等方法的pos参数
        match.lastindex : 返回最后一个匹配的捕获组的整数索引,如没有,则返回None
        match.lastgroup : 返回最后一个匹配的捕获组的名字,如没有,则返回None
        match.re : 返回执行正则表达式匹配时所用的正则表达式
        match.string : 返回执行正则表达式时所用的字符串

set 和 frozenset 集合操作

set 集合是可变容器,可改变容器中的元素, frozenset集合,是set的不可变版本,他的元素不可变

set集合:
    两个特征:
        1. set 不记录元素的添加顺序
        2. 元素不允许重复
        [e for e in dir(set) if not e.startswith('__')]
    add添加 、 remove 删除元素、discard 删除元素、clear 清空 
    remove 和 discard 区别: remove 报KeyError异常。discard 不报
set 支持的运算符:
    <= : 相当于调用 issubset() 方法,判断前面的set集合是否为后面set集合的子集合
    >= :调用issuperset 方法,判断是否为后面set集合的父集合
    - : 调用difference ,前面的set集合减去后面的set集合的元素
    & : 调用intersection,获取两个set集合的交集
    ^ : 计算两个集合异或的结果,即两个集合的并集减去交集的元素

frozenset 集合

是set的不可变版本,set集合中不改变集合本身的方法,fronzenset 都支持
作用:
    1. 当集合元素不需要改变时,使用frozenset 代替 set更安全
    2. 当某些api需要不可变对象时,必须用frozenset代替set

queue (双端队列) 模块

栈:一种特殊的线性表,允许一端进行插入、删除操作。这个端为栈定(top),另一端为栈底(botton)
从栈顶插入一个元素称为: 进栈, 压入栈。 push 
从栈顶删除一个元素称为: 出栈, 弹出栈。 pop
栈,陷入栈的元素位于栈底,上面元素出栈后,栈底的元素才能出栈。 栈 是一种 后进先出(LIFO)的线性表 
队列是一种特殊的线性表,只允许在表的前段(font) 删除,在后端(rear) 插入。 插入的操作的端为 队尾, 删除操作的端为 队头
    队列:元素是从队列的rear 端进。 队列是一种 先进先出FIFO 的线性表。 
双端队列deque 代表特殊的队列, 在两端同时进行插入、删除操作,deque 即可为队列使用,也可为 栈 使用
deque 位于 collections 包下,[e for e in dir(collections.deque)if not e.startswith('__')]
    from collections import deque
    双端队列的特征, deque 的左边 left 相当于 它的队头front, 右边right 相当于它的队列尾rear
        append 和 appendleft :在deque的右边或左边添加元素, 即在默认队列尾添加元素
        pop 和 popleft :在deque的右边或左边弹出元素,默认在队列尾弹出元素
        extend 和 extendleft : 在deque的右边或左边添加多个元素,默认在队列尾添加多个元素
    deque 中clear 方法用于清空队列,insert 方法是线性表的方法,指定位置插入元素
    deque 中 rotate 方法。将队列的队尾元素移动到队头,使之首位相连

heapq 堆操作

    小顶堆的任意子树是小顶堆,大顶堆的任意子树是大顶堆
    import heapq    -> heapq.__all__
    函数功能:
        heappush(heap,item) : 将item元素加入堆
        heappop(heap) : 将堆中最小元素弹出
        heapify(heap) : 将堆属性应用到列表上
        headpreplace(heap,x) : 将堆中最小元素弹出,并将元素x入堆
        merge(*iterables, key=None, reverse=False) : 将多个有序的堆合并为一个大的有序堆,然后输出
        headppushpop(heap,item) : 将item入堆,然后弹出并返回堆中最小的元素
        nlargest(n,iterable,key=None) : 返回队中最大的n个元素
        nsmallest(n,iterable,key=None) : 返回堆中最小的n个元素

ChainMap
    使用链的方式将多个 dict 链在一起,允许程序可直接获取任意一个dict所包含的key对应的value
    ChainMap 相当于把多个dict合并为一个大的dict,

Counter 类

    可自动统计容器中个元素出现的次数
    本质是一个特殊的dict,key是所包含的元素,value记录key出现的次数 
    Counter 继承了dict, 提供三个常用的方法:
        1. elements : 返回该counter 所包含的全部元素组成的迭代器
        2. most_common([n]) :返回Counter 中出现最多的n个元素
        3. subtract([iterable-or-mapping]) : 计算counter 的减法,计算减去之后各元素出现的次数
    可把Counter对象转换为 set集合、list列表、dict字典等,可对Counter执行 加、减、交、并运:
        加: 将两个Counter对象中各Key 出现的次数相加,保留为正的元素
        减: 相减,保留出现次数为正的元素
        并: 出现key且各key对应的次数的最小数
        求正: 只保留出现次数为0 或正数的key-value对
        求负: 保留次数为负的 key-value 对,将次数改为正数

defaultdict :
    是dict 的子类,与dict 的区别: 根据不存在的key访问dict中对应的value,会引发KeyError异常,defaultdict则提供default_factory属性, 指定的函数为不存在的key来生成value

namedtuple 工厂函数功能

    可创建一个tuple类的子类,为tuple的每个元素指定字段名,可根据字段名访问namedtuple的各元素,根据索引来访问namedtuple的各元素
        namedtuple(typename,field_names, * , verbose=False, rename=False,module=None)
            typename : 指定所创建的tuple子类的类名,等于用户定义一个新类
            field_names : 字符串序列,使用单个字符串代表所有字段名,用空格、逗号隔开
            rename : 如参数为True, 无效字段名会被自动替换为位置名
            verbose : 参数为True, 当子类被创建后,该类定义会被立即打印出来
            module : 自定义类的__module__属性将被设为该参数值
        Python 为命名元组提供的方法和属性:
            _make(iterable) :类方法,根据序列或可迭代对象创建命名元组对象
            _asdict() : 将当前命名元组对象转换为OrderdDict 字典
            _replace(**kwargs) : 替换命名元组中一个或多个字段的值
            _source : 返回定义该命名元组的源代码
            _fileds : 返回该命名元组中所有字段组成的元组

OrderdDict用法:
    是dict的子类,可 维护 添加 key-value 对的顺序, 先添加key-value对排的前面,后添加的key-value对排的后面
    两个方法:
        1. popitem(last=True) : 弹出并返回最左边的最后加入的key-value对,将last参数设为False,则弹出并返回最左边最先加入的key-value对
        2. move_to_end(key,last=True) : 将指定的key-value对移动到最右边最后加入,将last改为False,则将指定的key-value对移动到最左右最先加入

itertools模块:生成迭代器

先导入 import itertools 模块, [e for e dir(itertools) if not e.startswith('__')]
三个生成无限迭代器的函数:
    1. count(start,[step]) :生成start、start+step、start+2*step,...的迭代器,step默认为1。 count(10) 生成的迭代器包含: 10,11,12,13,14.。。
    2. cycle(p) : 对序列p生成无限循环p0,p1.。。的迭代器。cycle('ABCD') 包含:A,B,C,D,A,B,C,D,...
    3. repeat(elem [,n]) : 生成无限个 elem元素重复的迭代器. repeat(10,3) :10,10,10,
在itertools 模块中常用的迭代器函数:
        accumulate(p,[func]) : 生成根据序列p元素累加的迭代器
        chain(p,q,...) : 将多个序列里的元素 链 在一起生成新的序列
        compress(data,selectros) : 根据selectors序列的值对data序列的元素进行过滤
        dropwhile(pred,seq) : 使用pred函数对seq序列进行过滤,如计算为False,保留该元素到序列结束的全部元素
        takewhile(pred,seq) :使用pred函数对seq进行过滤,去掉从该元素序列结束的全部元素
        filterfalse(pred,seq):使用pred函数对seq序列进行过滤,保留seq中使用pred计算为True的元素
        islice(seq,[start,]stop[,step]) :类似于slice,返回seq[start:stop:step]的结果
        starmap(func,seq):使用func对seq每个元素进行计算,结果为新的序列元素
        zip_longest(p,q,...) :将p、q序列中元素按索引合并成元组,元组作为新序列的元素
在itertools 模块中生成序列排列的工具函数:
        product(p,q,...[repeat = 1]):用序列p、q,。。。进行排序组合,相当于嵌套循环组合
        permutations(p[,r]) :从序列p中取出r个元素组成排序,将排序得到的元组作为新迭代器的元素
        combinations(p,r) :从序列p中取出r个元素组成全组合,元素不重复,将组合得到的元组作为新迭代器的元素
        combinations with_replacement(p,r):从序列p中取出r个元素组成全组合,元素可重复,将组合得到的元组作为新迭代器的元素

functools 模块:

包含函数装饰器和便捷的功能函数, import functools 
    常用函数装饰器和功能函数:
        functools.cmp_to_key(func) :将老式的比较函数(func)转换为关键字函数(key function) py3不支持
        @function.lru

        [更多](http://c.biancheng.net/view/2443.html)

Tkinter (GUI图形洁面开发)

GUI :Graphics User Interface 图形用户界面。三要素:输入数据、处理数据、输出数据

常用库:
    wxPython : 跨平台GUI工具集
    PyQt : 是Py和Qt库的融合
    PyGTK : 基于老版本GTK+2的库提供绑定,借助于底层GTK+2提供的可视化元素和组件
    Pywin32 : 允许像VC使用Py开发win32应用
    Kivy : 开源库,使用同源代码创建的程序跨平台
    Flexx : 纯Py工具包,创建图形化界面程序,支持使用web技术进行界面渲染

Tkinet GUI 编程组件及用法

    学习GUI步骤为三步:
        1. 包含的组件
        2. 容器及容器对组件布局的方法
        3. 掌握各组件的用法
        [Tkinter GUI 关系](http://c.biancheng.net/view/2451.html)
    Tkinter的GUI组件有两个根父类,直接继承object类
        1. Misc : 所有组件的根父类
        2. Wm : 提供窗口管理器通行的功能函数
    BaseWidget : 所有组件的基类,派生类:Widget ,通用GUI组件,Tkinter 是所有GUI组件都是Widget的子类
    各GUI组件的功能
        Toplevel:        顶层            容器类
        Button :        按钮            按钮组件
        Canvas :        画布            绘图功能
        Checkbutton:    复选框            可勾选的复选框
        Entry  :        单行输入框        用户可输入容内
        Frame :        容器            装载其他GUI组件
        Label :        标签            显示不可编辑的文本或图标
        LabelFrame :    容器            容器组件,支持添加标题
        Listbox :        列表框            列出多个选项,供用户选择
        Menu    :        菜单            菜单组件
        Menubutton :    菜单按钮        包含菜单的按钮 包括下拉式、层叠式
        OptionMenu :    菜单按钮        Menubutton的子类
        Message :        消息框            类标签,显示多行文本,Lable代替,废弃
        PanedWindow:    分区窗口        该容器可划分为多个区域
        Radiobutton    :    单选钮            单选按钮
        Scale :        滑动条            可设置起始值和结束值,显示当前精准值
        Spinbox :        微调选择器        可通过组件向上、向下选择不同的值
        Scrollbar :    滚动条            用于为组件(文本域、画布、列表框、文本框)提供滚动
        Text :            多行文本框        显示多行文本
    initWidgets 方法实现的代码:
        1.创建 GUI 组件
        2.添加 GUI 组件
        3.配置 GUI 组件
    配置GUI组件的2种方法:
        1. 以关键字参数的方式配置
        2. 以字典语法进行配置
    [GUI通用选项](http://c.biancheng.net/view/2451.html)

TKinter Pack 布局管理器
    [常用选项及功能]()
    anchor : 空间大于组件所需求的大小,决定被放置在容器的位置
    expand : 指定当容器增大时是否拉伸组件
    fill :    组件是否沿水平或垂直方向填充
    ipadx :    指定组件在 x 方向上的内部留白
    ipady : 在 y 方向上内部留白
    padx :  在x方向上与其他组件的间距
    pady :    在y方向上的间距
    side :  设置组件的添加位置

Tkinter Grid 布局管理器
    Grid 把组件空间分解为一个网格进行维护
    Tkinter Grid 常用选项
        column : 指定将组件放哪列
        columnspan : 指定组件横跨多少列
        row :指定放入哪行
        sticky :类 pack方法的anchor选项

Tkinter Place 布局管理器

    绝对布局 : 要求程序显式指定每个组件的绝对位置或相对其他组件的位置
    常用选项:
        x            指定组件的X坐标, x 为 0 代表最左边
        y            Y 坐标                        最右边
        relx        组件的X坐标
        rely        组件的Y坐标
        width        组件的宽度
        height        组件的高度
        relwidth    组件的宽度
        relheight    组件的高度
        bordermode    设置组件的宽度、高度

Tkinter Command 和 Bind 事件处理
    command 绑定事件处理方法:
        可通过command 来绑定,可绑函数或方法,单击时,触发绑定的函数或方法
    bind 绑定事件处理方法:
        无法为具体事件绑定事件处理方法
        无法获取事件相关信息
    bind()方法: 可为 任意 事件绑定事件处理方法
        Tkinter 支持的鼠标、键盘事件

Tkinter ttk组件及用法
    是Tinkter 包下的模块,界面美化、包装

Tkinter Variable类用法
    支持GUI组件与变量进行双向绑定,
        1. 如改变变量的值,GUI组件的显示内容或值也改变
        2. 当GUI组件的内容改变时,值也改变
    Tinkter 不能讲组件和普通变量进行绑定,只能和tkinter 包下的Variable类的子类进行绑定
    1. StringVar() :    包装str值的变量
    2. IntVar() :        整形值的变量
    3. DoubleVar() :    浮点值的变量
    4. BooleanVar() :  包装bool值的变量

Tkinter compound 选项使用方法

如使组件同时显示文本和图片,可通过 compound 选型进行控制
    属性值:
        1. None : 图片覆盖文字
        2. LEFT 常量: 图片在左,文本在右
        3. RIGHT 变量: 图片在右,文本在左
        4. TOP 常量: 图片在上, 文本在下
        5. BOTTON 常量: 图片在底,文本在上
        6. CENTER 常量: 文本在图片上方

Tkinter Entry 和 Text 控件用法

    可接收用户输入的输入框组件,区别: Entry : 单行。 Text: 多行

Tkinter Radiobutton 和 Checkbutton 用法
    单选按钮,可绑定一个方法或函数。 将多个Radiobutton 编为一组,将多个Radiobutton绑定到同一个变量,当其中一个单选按钮被选中时,该变量随之改变。

Tkinter Listbox 和 Combobox 控件用法
    列表框,通过列表框选择一个列表项。
        创建 Listbox 的步骤:
            1. 创建Listbox 对象,设置listbox的选择模式
            2. 调用listbox的insert(self,index,*elements)添加选项

Tkinter Spinbox 控件
    通过两个小箭头调整该组件内的值

Tkinter Scale 和 LabeledScale用法
    代表一个滑动条,为滑动设置最大最小值
    Scale 组件选项:
        from : 最大值
        to : 最小值
        resolution : 滑动时的步长
        lable : 设置标签内容
        length : 设置轨道的长度
        width : 轨道的宽度
        troughcolor : 背景色
        sliderlength : 长度
        sliderrelief : 立体样式
        showvalue : 是否显示当前值
        orient : 设置方向
        digits : 设置有效数字位数
        variable : 与变量进行绑定
        command : 为该Scale 组件绑定事件处理,函数或方法

Tinkter LabelFrame 用法
    是Frame容器改进版,为容器添加标签,可为普通文字标签,也可为GUI组件为标签
    对标签进行定制:
        1. labelwidget : 将任意GUI组件作为标签
        2. labelanchor : 设置标签位置

Tkinter Panedwindow 控件

    管理窗口布局的容器,允许添加多个子组件,并为每个子组件划分一个区域,可用鼠标移动分隔线改变各子组件的大小
    操作Panedwindow 容器中子组件的方法:
        1. add(self,child,**kw) : 添加一个子组件
        2. insert(self,pos,child,**kw) : 在pos 位置插入一个子组件
        3. remove(self,child) : 删除一个子组件,所在区域也删除 

Tkinter OptionMenu控件
    构建带菜单的按钮,可在按钮的四个方向上展开,通过direction选项控制
        __init__(self,master,variable ,value,*values, **kwargs)
            1. variable ; 指定该按钮上的菜单与哪个变量绑定
            2. Value : 默认选择菜单中的哪一项
            3. values : 将收集为此参数传入的多个值,为每个值创建一个菜单项
            4. kwargs : 为 OptinoMenu配置选项

Tkinter 对话框创建及使用
    1. 对话框依赖类似于顶级窗口,创建时需指定master属性
    2. 对话框有非模式noo-modal和模式modal,某个模块对话框被打开,位于它依赖的窗口之上。
    Tkinter 在 simpledialog 和dialog 模式下分别提供了 SimpleDialog 类和 Dialog 类,可作为普通对话框使用
        使用simpledialog 和dialog 创建对话框可指定:
            1. title: 标题
            2. text :内容
            3. button: 按钮
            4. default:默认第几个按钮得到焦点
            5. cancel: 指定对话框上角的X按钮关闭对话框

Tkinter 自定义对话框
    自定义通过继承Toplevel 实现:
        1. 继承Toplevel 实现自定义对话框需要为对话框指定 master
        2. 调用Toplevel 的grab_set 方法 把对话框变为模式对话框,否则为非模式对话框

Tkinter 输入对话框

工具函数:
    1. askinteger ; 生成一个让用户输入正数的对话框
    2. askfloat : 输入浮点数的对话框
    3. askstring : 输入字符串的对话框

Tkinter 文件对话框创建和使用

    直接返回用户选择文件的输入/输出流:
        1. askiopenfile : 打开单个文件的对话框
        2. askopenfiles : 打开多个文件的对话框
        3. askopenfilename : 打开单个文件的对话框,返回选择文件的文件路径
        4. askopenfilenames : 多个文件的对话框
        5. asksavesfile : 生成保存文件的对话框
        6. asksaveasfilename : 保存文件的对话框,返回所选择文件的文件路径
        7. askdirectory : 生成打开目录的对话框
    生成打开文件的对话框工具函数:
        1. defaulttextension : 指定默认扩展名
        2. filetypes : 查看的文件类型
        3. initaldir : 初始化打开的目录
        4. parent : 指定该对话框的属主窗口
        5. title : 对话框的标题
        6. multiple : 允许多选

Tkinter askcolor 颜色选择对话框
    函数选项:    
        1. parent : 属主窗口
        2. title : 标题
        3. color : 颜色

Tkinter 消息框

    选项按钮
        1. icon  : 定制图标
        2. type : 定制按钮的选项
    showinfo 函数: 默认生成的消息框的图标是感叹号

Tkinter Menu 菜单 窗口菜单和右键菜单
    添加菜单项的方法:
        1. add_command() : 添加菜单项
        2. add_checbutton(): 复选框
        3. add_radiobutton(): 单选按钮
        4. add_separator() : 菜单分隔条
    添加菜单的三个方法选项:    
        1. label : 指定菜单项的文本
        2. command : 指定绑定的事件处理方法
        3. image : 指定菜单项的图标
        4. compound : 图标位于文字的哪个方位
    Menu窗口菜单:
        创建菜单后,将菜单设为窗口的menu选项即可
            add_command 为file_menu 添加多个菜单项
            add_cascade 再次为file_menu添加子菜单
            add_radiobutton 添加多个单选菜单项
    Menu 右键菜单:
        先创建菜单,为目标组件的右键菜单绑定处理函数, 点击右键,调用菜单post 方法即可

Tkinter Canvas 画布完全攻略

    绘制直线、矩形、椭圆等图形,提供create_rectangle 方法绘制和 create_oval 绘制椭圆,绘制方法:
        create_arc : 绘制弧
        create_bitmap : 位图
        create_image : 图片
        create_polygon : 多边形
        create_line : 直线
        create_text : 文本
        creat_window : 绘制组件
            绘制指定的选项:    
                fill : 填充颜色
                outline : 边框颜色
                width : 边框宽度
                dash : 边框虚线
                stipple : 位图平铺填充
                start : 开始角度
                extend : 绘制弧的角度
                style : 绘制弧样式
                arrow : 是否有箭头
                arrowshape : 箭头样式
                joinstyle : 连接点的风格
                anchor : 绘制文字
                justify : 文本对齐方式

Tkinter Canvas tag_bind :指定图形项绑定事件处理函数或方法
    tag_bind 方法: 用于为指定图形项绑定事件处理函数或方法,可用于响应用户动作

Tkinter Canvas 绘制动画
    小球转动; 循环显示多张转动的小球图片
    小球移动: 改变小球的坐标程序

文件操作I/O

文件基本操作

常见操作: 创建、删除、修改权限、读取、写入等
    1. 删除、修改权限:作用于文件本身,属于系统级操作
    2. 写入、读取: 文件常用操作,作用于文本的内容,属于应用级操作
文件操作实现函数:
    1. 打开文件: open 函数,返回文本对象
    2. 对已打开的文件做读/写操作,读写,使用 read 、readline readlines 函数,写入:write 函数
    3. 关闭文件: close 

open 函数:打开指定文件

如要操作文件,需创建或者打开指定的文件,并创建一个文件对象,内置的 open 函数
    file = open(file_name [, mode [, buffering]]) 
        file: 表示要创建的文件对象
        file_mode : 要创建或打开文件的文件名称,需用引号扩起来,注意路径
        mode : 可选参数;指定文件的打开模式,如不写,默认只读r
        buffing : 指定对文件做读写操作,是否使用缓存区

open 函数文件打开模式:

            r : 只读,指针在开头
            rb : 二进制格式,只读模式,指针位于开头,用于打开非文本文件,如图片
            r+ : 从头读取文件内容,从开头写入新的内容,新内容覆盖原有内容
            rb+ : 二进制格式读写模式打开,针对非文本文件,如音频文件
            w : 只读,清空文件原有内容
            wb :二进制格式、只读模式,音频文件
            w+ : 读写, 清空原有内容
            wb+ : 二进制格式、读写模式,非文本
            a :追加模式,只写权限,如文件不存在,则创建新文件
            a+ : 读写,指针位于末尾,如不存在,则新建
            ab+ : 二进制模式,追加模式,读写权限
[读写操作](http://c.biancheng.net/uploads/allimg/190228/2-1Z22QI61c59.gif)
    open 打开文件时,默认GBK编码,指定打开文件的编码格式; 
        file = open('xx.txt',encoding="utf-8")

open()是否需要缓冲区

    一般建议打开缓冲,open函数,第三个参数是0或False,是不带缓冲的,若是1或True,则带缓冲
open 文件对象常用属性:
    file.closed : 判断是否关闭
    file.mode : 返回访问模式
    file.name : 返回文件名

以文件格式和二进制格式打开文件的区别:
    相同点: 都是以二进制格式打开文件
    不同点: 对文件中换行符的处理不同
        Win: \r\n  转换为 \n 
        Unix/Linux : 默认换行符是 \n
        推荐使用 b 打开二进制文件

read 函数: 按字节、字符读取文件

        read 读取文件是字节、字符的区别: 取决于open函数打开文件时,是否使用 b 模式,如使用 b ,读取的是 字节, 如不是 b ,则是 字符
        file.read([size])
    read 抛出UnicodeDecodeErorr 异常的解决方案:
        文本的额字符集和操作系统的字符集不匹配,解决方案:
            1. 使用二进制模式读取, 然后用bytes 的decode 方法恢复为字符串
            2. 采用 codecs 模块的open函数打开文件时指定字符集
readline 和 readlines : 按行读取文件
    readline : 读取一行内容 。 readlines : 读取文件内的所有行 
        readline : 
            file.readline([size])
                file 为打开的文件对象, size 可选参数,指定读取每一行,一次最多读取的字符数,模式使用 r 或 r+ 读写
        readlines :
            file.readlines() : file 为文件打开对象, 模式使用r 或 r+

    write 和 writelines : 向文件中写入数据
        file.write(string) :向文件中写入指定内容。 file。write(string)
        writefiles() 函数: 
            将字符串列表写入文件中。 向文件中写入多行数据时,不自动给各行添加换行符

close : 关闭文件

        flie.close()
            关闭使用open函数打开的文件
                如不在关闭文件的前提下将数据写入到文件中,使用文件对象提供的flush 函数

seek 和 tell 函数
    tell :判断文件指针当前所处的位置
    seek :用于移动文件指针到文件的指定位置
        file.tell()
        file.seek(offset[, whence])
            file : 文件对象
            whence : 指定文件指针要放置的位置,0 开头,1当前位置,2文件尾
            offset : 相对于whence位置文件指针的偏移量

with as 用法

使用with as 语句操作上下文管理器 context manager,自动分配并且释放资源
    with 表达式 [as target]:
        代码块
即使没有关闭文件,修改文件内容的操作也能成功

上下文管理器, python with as 底层原理

    包含 __enter__() 和  __exit__() 方法的对象是上下文管理器,上下文管理器必须实现一下两个方法:
        1. __enter__(self): 进入上下文管理器自动调用的方法,会在with as 执行前执行,返回值被赋值给 as 子句后的变量,可返回多个值,在as子句后可指定多个变量,必须用 () 括起来
        2. __exit__(self,exc_typ`e,exc_value,exc_traceback ) : 退出上下文管理器自动调用的方法,在with as 代码执行后执行,如with as 因异常终止,程序自动调用该方法,使用 sys.exc_info 得到的异常信息将作为调用该方法的参数
    构建上下文管理器,实现的2种方式:
        1. 基于类的上下文管理器
            只要类实现 __enter__()  __exit__ 这两个方法,就可使用with as来管理, 通过 __exit__ 方法的参数,可判断with 代码块执行是否遇到了异常,
        2. 基于生成器的上下文管理器
            使用基于生成器的上下文管理器时,不需要定义 __enter__() 和 __exit__()方法,但必须添加 装饰器 @contextmanager 
        基于类的上下文管理器灵活,适用于大型的系统开发
        基于生成器的上下文管理器更方便、简洁、适用于小型程序
            切记: 用__exit__() 或是 finally 块中释放资源








fileinput模块:逐行读取多个文件
    把多个输入流合并在一起
        fileinput.input (files = "filename1,filenamex,...",inplace=False,backup=",bufsize=0,mode='r',openhook=None")
            files :多个文件的路径列表
            inplace : 指定是否将标准输出的结果写回到文件,默认值为 False
            backup : 指定备份文件的扩展名
            bufsize : 指定缓存区的大小,默认0
            mode : 打开文件的格式,默认 r
            openhook : 控制文件的打开方式,如编码格式
    fileinput 模块常用函数
        fileinput.filename() :返回读取文件的文件名
        fileinput.fileno() :返回文件描述
        fileinput.lineno() :返回读取的行号
        fileinput.filelineno() :返回读取的行在文件中的行号
        fileinput.isfirstline() : 读取的行在文件中是否为第一行
        fileinput.isstdin() : 是否从sys.stdin 读取
        fileinput.nextfile() : 关闭当前文件,开始读取下一个文件
        fileinput.close() : 关闭fileinput对象

linecache模块:随机读取文件指定行
    从源文件随机读取指定行,并在内部使用缓存优化存储,会使用utf-8字符集
        常用函数:
            linecache.getline(filename,lineno,module_globals=None):读取指定模块中指定文件的指定行,filename指定文件名,lineno指定行号
            linecache,clearcache() :清空缓存
            linecache.checkcache(filename=None) :检查缓存是否有效,如没有指定文件名filename参数,默认检查所有缓存的数据

pathlib模块
    提供了一组面向对象的类,代表各种操作系统上的路径
    PuraPath 的两个子类: PurePosixPath:Unix风格的路径  PureWindowsPath:Windows风格的路径
PurePath :使用此函数或他的子类来创建PurePath对象,创建时,可闯入单个路径字符串,也可传入多个路径字符串
PurePath类的属性和方法:
    操作路径字符串,[](http://c.biancheng.net/view/2541.html)    
Path类功能和用法:
    Path 是PurePath的子类,可访问底层的文件系统,判断Path对应的路径是否存在,可对文件进行读写

os.path 模块函数
    操作目录的方法,可操作系统的目录本身,如 exists():判断目录是否存在, getctime():创建时间 getmtime():修改时间  getatime():访问时间  getsize():文件大小

fnmatch模块:文件名的匹配
    匹配支持的通配符:
        * : 匹配任意个任意字符
        ? : 匹配一个任意字符
        [字符序列] :匹配中括号里字符序列中的任意字符,
        [!字符序列] : 匹配不在中括号里字符序列中的任意字符
    fnmatch.fnmatch(filename,pattern):判断指定文件名是否匹配指定pattern
    fnmatch.fnmatchcase(filename,pattern):匹配时不区分大小写
    fnmatch.filter(names,pattern) :对names列表进行过滤,返回names列表中匹配pattern的文件名组成的子集合。 
    fnmatch.translate(patteran):将Unix shell风格的pattern转换为正则表达式pattern

os模块:
    os模块与目录相关的函数:
        os.getcwd():获取当前目录
        os.chdir(path) : 改变当前目录
        os.fchdir(fd) :通过文件描述改变当前目录
        os.chroot(path):改变当前进程的根目录
        os.listdir(path):返回paht对应目录下的所有文件和子目录
        os.mkdir(path[,mode]):创建path对应的目录,mode指定目录的权限
        os.makedirs(path[,mode]):类似mkdir ,可递归创建目录,
        os.rmdir(path):删除path对应的空目录,如非空抛出 OSError异常,可先用os.remove()删除文件
        os.removedirs(path) :递归删除目录,类似rmdir
        os.rename(src,dst):重命名文件或目录,将src命名为dst
        os.renames(old,new) :对文件或目录进行递归重命名,类rename,

os模块与权限相关的函数
    os.access(path,mode):检查path对应的文件或目录是否具有指定权限,第二参数的四个状态
        os.F_OK : 判断是否存在
        os.R_OK : 是否可读
        os.W_OK : 是否可写
        os.X_OK : 是否可执行
    os.chrnod(path,mode) :更改权限,
        stat.S_IXOTH :其他用户有执行权限
        [更多](http://c.biancheng.net/view/2558.html)            
    os.chown(path,uid,gid) :更改文件的所有值,uid代表用户id,gid代表组id
    os.fchmod(fd,mode) :改变一个文件的访问权限,fd代表文件
    os.fchown(fd,uid,gid) :改变文件的所有者''

os模块与文件访问函数
    os.open(file,flags[,mode]) :打开一个文件,设置打开选项,flags表示打开文件的旗标
,支持多个选项
        os.O_RDONLY : 只读方式打开
        os.O_WRONLY : 只写方式
        os.O_RDWR : 读写方式
        os.O_NONBLOCK : 打开时不阻塞
        os.O_APPEND : 追加方式打开
        os.O_CREAT ;创建并打开一个新文件
        [更多](http://c.biancheng.net/view/2558.html)
    os.read(fd,n) :从文件描述符fd中读取最多n个字符,返回读到的字符串
    os.wirte(fd,str) :将字符串写入文件描述符fd,返回写入的字符串长度
    os.close(fd) : 关闭文件描述符fd
    os.lseek(fd,pos,how) : 用于移动文件指针,how指定从哪里开始移动,
    os.fdopen(fd[,mode[,bufsize]]) :通过fd打开,返回文件对象
    os.closerange(fd_low,fd_high) : 关闭从fd_low 包含 到 fd_high 不包含范围的所有文件描述符
    os.dup(fd) : 复制文件描述符
    os.dup2(fd,fd2) : 讲一个fd 复制到另一个文件描述符 fd2中
    os.ftruncate(fd,length) : 将fd对应的文件截断到length长度,length参数不超文件大小
    os.remove(path) :删除path对应的文件
    os.link(src,dst) : 创建从src 到dst的硬连接
    os.symlink(src,dst) :创建从src到dst的符号链接

tempfile模块:生成临时文件和临时目录
    常用函数:
        tempfile.TemporaryFile(mode='w+b',buffering=None,encoding=None,newline=None,suffix=None,prefix=None,dir=None) :创建临时文件,返回类文件对象,支持I/O
    [More](http://c.biancheng.net/view/2560.html)
        tempfile.gettempdir() : 获取系统临时目录
    创建临时文件的两种方式:
        1. 手动创建临时文件,读写临时文件后需主动关闭,程序关闭时文件自动删除
        2. 使用with语句创建临时文件,with语句自动关闭临时文件

数据库编程

数据库API(DB API)

全局变量
    3个全局变量:
        1. apilevel : 显示数据库模块的API版本号
        2. threadsafety : 指定数据库模块的线程安全等级,等级值为 0~3,3代表模块完全是线程安全的,1:部分安全 ,0:  完全不能共享该模块
        3. paramstyle : 指定SQL语句需要参数时,使用风格的参数,返回如下变量值:
            format : 格式化字符串代表参数,使用 %s
            pyformat : 使用扩展的格式代码代表参数
            qmark : 使用 ? 问号代表参数
            numeric : 使用数字占位符 :N 代表参数,1 代表一个参数,2 也代表参数
            named : 使用命名占位符 :name 代表参数

数据库API的核心类
    连接对象的方法和属性
        cursor() :            打开游标
        commit() :            提交事物
        rollback():        回滚事物
        close() :            关闭数据库连接
        isolation_level:    返回或指定数据库连接中事物的隔离级别
        in_transaction:        判断当前是否处于事物中
cursor : 返回游标对象,游标对象是 Python DB API的核心对象,用于执行各种SQL语句,包括DDL、DML、select 查询语句等,使用游标执行不同的SQL语句返回不同的数据。
    游标对象的属性和方法:
        execute(sql[,parameters]) : 执行SQL语句,parameters 参数用于为SQL语句中的参数指定值
        executemany(sql,seq_of_parameters) :重复执行SQL语句,通过第二个参数指定值,序列有多少个元素,SQL语句被执行多少次
        executescript(sql_script) :直接执行包含多条SQL语句的SQL脚本
        fetchone() : 获取查询结果集的下一行,如没有,则返回None
        fetchmany(size=cursor.arraysize) :返回查询结果集的下N行组成的列表,如没有,返回空
        fetchall() : 返回查询结果集的全部行组成的列表
        close() : 关闭游标
        rowcount : 只读属性返回受SQL语句影响的行数,修改的记录条数也可通过该属性获取
        lastrowid :获取最后修改行的rowid
        arraysize : 设置或获取fetchmany 默认获取的记录条数,默认为 1
        desciption : 获取最后一次查询返回所有列的信息,只读
        connection : 返回创建游标的数据库连接对象,属性只读

    操作数据库的基本流程
        1. 调用 connect 方法打开数据库连接,返回数据库连接对象
        2. 通过数据库连接对象打开游标
        3. 使用游标执行SQL语句 包括 DDL、DML、select查询语句,如执行的是查询语句,则处理查询数据
        4. 关闭游标
        5. 关闭数据库连接
        [图示](http://c.biancheng.net/uploads/allimg/190301/2-1Z301153400E3.gif)

SQLite 创建数据库表
    是一种嵌入式数据库,数据库是一个文件,SQLite将整个数据库包括定义表、索引以及数据本身,作为一个单独的、可跨平台使用的文件存储在主机中。不需要安装。直接导入
    连接数据库:
        connect() 函数
            conn = sqlite3.connect('xx.db')        // xx.db 是一个数据库,如不存在,在当前目录下创建对应的文件
    创建数据库:
        import sqlite3
        conn = sqlite3.connect('xx.db ')
        c = conn.cursor()
        c.execute(''' create table user_tb(
            id interger primary key autoincrement,
            name text,
            pass text,
        gender text)'''
                )
        c.execute(''' create table post_tb
                id integer primary key autoincrement,
                post_name text,
                post_author text,
                post_number real,
                user_id integer,
                foreign key(user_id) references user_tb(id)''')
        c.close()
        conn.close()
    SQLite 支持 NULL、INTEGER、REAL浮点数、TEXT文本、BLOD大二进制对象

SQLite execute 和 executemany 
    游标的execute 方法可执行DML 操纵语言 的 insert 、update、delete 语句,对数据库执行插入、修改和删除数据操作
    调用execute 方法执行insert 可向数据库插入数据
    向数据库插入一条数据:
        // 导入访问SQLite的模块
        import sqlite3
        // 打开或创建数据库, 可用 :memory: 代表创建内存中的数据库
        conn = sqlite3.connect('xx.db')            // xx.db 指创建时指定的数据库文件
        // 获取游标
        c = conn.cursor()
        //    调用执行 insert 语句插入数据
        c.execute('insert into user_tb values (null,?,?,?)', ('xxx','xxx','xxx'))
        c.execute('insert into xxx_tb values (null,?,?,?)' ('xx','xx','xx'))
        //    提交事物
        conn.commit()
        // 关闭游标
        c.close()
        // 关闭连接
        conn.close()
 executemany : 多次执行同一条SQl语句
        import sqlite3
        conn = sqlite3.connect('xx.db')
        c = conn.cursor()
        c.executemany('inert into xxx_tb values (null,?,?,?)',
            (    ('xx','xxx','xxxx'),
                ('aa','aaa','aaaa'),
                ('bb','bbb','bbbb'),
                ('zz','zzz','zzzz')
            ))
        conn.commit()
        c.close()
        conn.close()
update | delete
    import sqlite3
    conn = sqlite3.connect('xx.db')
    c = conn.cursor()
    c.execute(' update user_tb set xxx=? where xx=? ',
                (('aa',1),
                ('bb',2)
            ))
    print('change numbers : ', c.rowcount)
    conn.commit()
    c.close()
    conn.close()

SQLite : fetchone() , fetchmany() and fetchall:
    select 语句执行查询结果, 通过游标的 fetchone 、fetchmany、fecthall获取查询结果,fetchone 获取一条,fetchmany 获取多条, fetchall 获取全部
    import sqlite3
    conn = sqlite3.connect('xx.db')
    c = conn.cursor()
    c.execute('select * from user_tb where xx > ?',(2,))
    print('result : ', c.rowcount)
    for col in (c.description):
        print([col[0],end'\t'])
    print('\n------')
    where True:
        row = c.fetchone()
        if not row:
            break
        print(row)
        print(row[1] + ' -> ' + row[2])
    c.close()
    conn.close()
可修改部分代码:
    while True:
        // 指定抓起的条数记录,返回由条数组成的列表
        rows = c.fetchmany(3)
        //    判断rows是否为None
        if not row:
            break
        // 再次使用循环遍历获取的列表
        for r in rows:
            print(r)
    避免使用fetchall获取查询的全部记录,如数据量过大,会导致内存开销过大,导致系统崩溃!

SQLite: executescript 
    可执行一段SQL脚本
        import sqlite3
        conn = sqlite3.connect('xx.db')
        c = conn.cursor()
        c.executescript('''
            insert into user_tb values (null,'aaa','aaa','aaaa'),
            insert into user_tb values (null,'bbb','bbb','bbbb'),
            create table item_tb (id integer primary key autoincrement, name, price)
        ''')
        conn.commit()
        c.close()
        conn.close()
    简化: SQLite 提供了3个方法为数据库连接对象
        1.  execute(sql[,parameters]) : 执行一条SQL语句
        2. executemany(sql[, parameters]) : 根据序列重复执行SQL语句
        3. executescipt(sql_script) : 执行SQL脚本
        只是游标对象的3个方法的快捷方式                                 
SQLite: create_function 方法: 注册自定义函数
    create_function 方法包含的三个参数:
        1. name : 指定注册的自定义函数的名字
        2. num_params : 指定自定义函数所需参数的个数
        3. func : 指定自定义函数对应的函数
        为SQL语句注册一个自定义函数,可在SQL语句中使用该自定义函数
            import sqlite3
            def reverse_ext(st):
                return '[' + st[::-1] + ']'
            conn = sqlite3.connect('xx.db')            // xx.db 代表数据库文件
            conn.create_function('enc',reverse_ext)
            c = conn.cursor()
            c.execute('insert into user_tb values(null,?,enc(>),?)' ,
                        ('xx','xx','xxx'))
            conn.commit()
            c.close()
            conn.close()

SQLite create_aggregate() : 自定以聚集函数
    SQL提供的5个聚集函数:
        1. sum() : 统计总和
        2. avg()  :统计平均值
        3. count() : 统计记录条数
        4. max() : 统计最大数
        5. min() : 统计最小数
可使用数据库连接对象提供的 create_aggregate(name,num_params,aggregate_class)方法,用于注册一个自定义的聚集函数
        create_aggregate 方法包含3个方法:
                1. name : 指定自定义聚集函数的名字
                2. num_params : 指定聚集函数所需的参数
                3. aggregate_class : 指定聚集函数的实现类,该类必须实现 step(self,pargams,..) 和 finalize(self) 方法,step方法返回每条记录各执行一次,finalize 方法只在最后执行一次,返回值作为聚集函数最后的返回值

SQLite: create_collation : 创建自定义比较函数
    create_collation(name, callable) 注册一个自定义的比较函数
        2个参数:
            1. name : 指定自定义比较函数的名字
            2. callable : 指定自定义比较函数对应的函数,包含两参数,对两个参数进行比较,如返回正整数,第一个参数更大,如是负整数,第二个参数更大,如返回0,则相等
            import sqlite3
            def my_collate(str1,str2):
                if st1[1:-1] == str2[1:-1]:
                    return 0;
                elif ...
            conn = sqlite3.connect('xx.db')
            conn.creat_collation('sub_cmp',my_callate)
            c = conn.cursor()
            c.execute('seleft * from xxx_tb where field = ?', (1))
            for row in c:
                print(row)
            conn.commit()
            c.close
            conn.close()

MySQL 数据库
    查看已安装的模块: pip list
                       pip show packagename
                       pip show mysql-connector-python
    卸载已安装的模块: pip uninstall packagename
    安装模块: pip install packagename
                pip install mysql-connector-python
                pip install packagename == 1.0    // 可指定版本

    MySQL 数据库执行DDL 语句
        import mysql.connector
        conn = mysql.connector.connect(user='root',password='root', host='127.0.0.1|localhost',post='3306', database='python',use_unicode=True)
        c = conn.cursor()
        c.execute('''    create table user_tb (
                user_id int primary key auto_increment,
                name varchar(100),
                pass varchar(200),
                gender varchar(100)
            )''')
        c.execute(''' create table order_tb (
            order_id int primary key auto_increment,
            item_name varchar(100),
            item_price double,
            item_number double,
            user_id  int,
            foreign key(user_id) reference user_tb(user_id)
        )''')
        c.close()
        conn.close()

MySQL 数据库执行DML 语句
    可使用游标的execute 方法执行DML的 insert 、upadte、delete
        import mysql-connector
        conn = mysql-connector.connect(user='root',password='hale',host='localhost',port='3306',database='python',use_unicode=True)
        c = conn.cursor()
        c.execute('insert into user_tb values(null, %s,%s,$s)',('aa','aaa','aaaa'))
        c.executemany('insert into order_tb values (null,%s ,%s,%s,%s)', 
                (('a','aa','aaa'),('b','bb','bbb'),('c','cc','ccc')))
        conn.commit()
        c.close()
        conn.close()
    使用 %s 作为占位符
    update 
        c.executemany('update user_tb set name=%s where user_id = %s ', (('e','ee','eee'),('w','ww','www')))
        print('change : ', c.rowcount)
        conn.comm
    mysql数据库模块连接对象有一个 autocommit ,如属性设置为True ,则关闭连接的事物支持,每次执行DML语句后会自动提交,无需调用 commit 方法提交事物
        import mysql.connector 
        conn = mysql.connector.connect(user='root',password='hale',host='localhost',port='3306',database='python',use_unicode=True)
        conn.autocommit = True

MySQL 数据库执行查询语句
    import mysql.connector
    conn = mysql.connector.connect(user='root',password='hale',host='localhost',port='3306',database='python',use_unicode=True)
    c = conn.cursor()
    c.execute('selecet * from user_tb where user_id > %s', (1,))    
    for col in (c.description):
        print(col[0],end='\t')
    print('\n -------')
    for row in c:
        print(row)
        print(row[1] + ' -> ' + row[2])
    c.close()
    conn.close()

    游标对象支持 fetchone() fetchmany() fetchall() 
    c.execute('select * from ueer_tb where user_id > %s',(1,))
    where True:
        rows = c.fetchmany(3)
        if not rows:
            break
        for r in rows:
            print(r)

MySQl callproc : 调用数据库存储过程
    callproc(self,procname,args=0)
        procname : 代表存储过程的名字, args 参数用于存储过程传入参数
        result_args = c.callproc('add_pro',2,1,0)

PyMySQl模块下载和安装
    类Connector/Python、PyMySQL ,称为接口程序,通过此对象,可对另外一个对象操作
    安装PyMySQL模块:
        pip install PyMySQL
            import pymysql

    import pymysql
    conn = pymysql.connect(host='localhost',root='root',password='pass',db='python',charset='utf8mb4')
    c = conn.cursor()
    c.execute('select  Version()')
    while True:
        rows = c.fetchmany(3):
            if not rows:
                break

            for i in rows:
                print(i)
    c.close()
    conn.close()
创建数据库:    
    import pymysql
    conn = pymysql.connect('localhost','root','root','python')
    cursor = conn.cursor()
    cursor.execute('Drop table if exists tb_name')
    sql = ''' create table user_tb (
        user_id int primary key auto_increment,
        name varchar(100),
        email varchar(10),
        pass varchar(100)
    )'''
    cursor.execute(sql)
    cursor.close()
    conn.close()

数据库插入操作
    import pymysql
    conn = pymysql.connect('localhost','root','root','py_db')
    cursor  = conn.cursor()
    sql = ''' insert into user_tb (name,pass) values ('%s','%s') % ('aa','aa')'''
    try: 
        cursor.execute(sql)
        conn.commit()
    except:
        conn.rollback()
    conn.close()

并发编程(多进程、多线程)

进程和线程 区别

进程: 操作系统资源分配的基本单位,通常是一个程序
线程: 任务调度和执行的基本单位,是进程的组成部分
    可运行多个进程(程序),同一进程可多个线程同时执行(通过CPU调度,每个时间片中只有一个线程执行
内存方面:进程分配不同的内存控件,线程不分配
开销方面: 进程有独立的代码和数据空间程序上下文,进程切换开销大,线程是轻量级的进程,同一类线程共享代码和数据空间,有独立的运行栈和计数器,线程切换开销小

单线程: 当一个进程中只有一个线程时
多线程: 当一个进程中有多个线程时 

创建线程的两种方式:

相关模块:
    1. _thread : 提供低级别的原始的线程支持,及简单的锁,功能有限,不建议使用
    2. threading : 提供丰富的多线程支持,推荐使用
创建方式:
    1. 使用 threading 中的 Thread 类的构造器创建线程,直接对类 threding.Thread 进程实例化,并调用对象的 start 方法创建线程
    2. 继承 threading 模块中的 Thread 类创建线程类,用 threading.Theread 派生出一个新的子类, 将新建类实例化,并调用 start 方法创建线程

 调用Thread 类的构造器创建线程:
    直接调用 threading.Thread 类构造器创建线程:
        __init__(self,gourp=None,target=None,name=None,args=(),kwargs=None,*,daemon=None)
            group: 指定该线程所属的线程组,
            target: 指定该线程要调度的目标方法
            args : 指定一个元组,以位置参数形式为target 指定的函数传入参数,元组的第一个参数传给target函数的第一个参数,第二个传给target第二个参数,以此类推
            kwargs :指定一个字典,以关键字参数的形式为target指定的函数传参
            daemon : 指定所构建的线程是否为后代线程
    通过Thread 类的构造器创建并启动多线程的步骤:
        1. 调用Thread类的构造器创建线程对象,创建时,target参数指定的函数将作为线程执行体
        2. 调用线程对象的start() 方法启动该线程
            import threading
            // 定义普通的action函数,作为线程执行体
            def action(max):
                for i in range(max):
                    print(threading.current_thred().getName() + " " + str(i))
            // 主程序、祝线程的执行体
            for i range(100):
                print(threading.current_thread().getName() + " " + str(i))
                if i == 20:
                    t1 = threading.Thread(target=action,args=(100,))
                    t1.start()
                    t2 = threading.Thread(target=action,args=(100,))
                    t2.start()
            print('master thread is run over !')
            1. 创建一个Thread对象,线程的target 为 action, 将action函数作为线程主体执行,接下来的程序调用start 来启动t1线程
            2. 再次创建线程,创建和启动与第一个线程完全相同
            显式创建并启动了两个线程,但实际上有三个,当程序运行后,至少创建一个主线程,主线程的线程执行代码就是程序中的主程序,没放在任何函数中的代码
                用到的函数和方法:
                    threading.current_thread(): 是threading 模块的函数,总是返回当前正在执行的线程对象
                    getName :Thread类的实例方法,返回调用他的线程名字
                在Threading模块中,经常用到的函数:
                    threading.enumerate() : 正运行线程的list
                    threading.activeCount : 返回正在运行的线程数量,与 len(threading.enumerate())

继承Thread类创建线程类
    步骤:
        1. 定义Thread 类的子类,并重写run方法,run方法方法体代表线程需要完成的任务,因此 run方法称为 线程执行体
        2. 创建Thread 子类的实例,即创建线程对象
        3. 调用线程对象的start 方法来启动线程
            import threading
            class FKThread(threading.Thread):
                def __init__(self):
                    threading.Thread.__init__(self)
                    self.i = 0 
                def run(self):
                    while self.i < 100:
                        print(threading.current_thread().getName() + " " + str(self.i))
                    self.i += 1
            for i in range(100):
                print(threading.current_thread().getName() + " " + str(i) )
                if i == 20:
                    ft1 = FKThread()
                    ft1.start()
                    ft2 = FKThread()
                    ft2.start()

            print('threading is ok !')

Python 线程的生命周日(新建、就绪、运行、阻塞、死亡)

线程的新建和就绪状态

        [具体](http://c.biancheng.net/view/2606.html)
            程序新建Thread对象或Thread子类的对象后,线程处于新建状态。 线程对象调用start 方法,会处于就绪状态,解释器会为其创建方法调用栈和程序计数器。启动线程用start 方法,不是run方法。 start 方法启动线程,系统把该run方法当成线程执行体处理。
        import threading
        def action(max):
            for i in range(max):
                print(threading.current_thread().name + " " + str(i))

        for i in range(100):
            print(threading.current_thread().name + " " + str(i))
            if i == 20:
                threading.Thread(target=action,args=(100,)).run()
                threading.Thread(target=action,args=(100,)).run()
        如果程序对同一线程重复调用start方法,会引发 RuntimeError警告

    线程运行和阻塞状态
        如处于就绪状态的线程获得了CPU,执行run方法的线程执行体,该线程处于运行状态
        在一个具有多处理器的机器上,会有多个线程并行执行 Parallel。当线程数大于处理器时,依然存在多个线程在同一CPU上轮换
    进入阻塞状态的情况:
        1. 线程调用 sleep 方法主动放弃其所占用的处理器资源
        2. 线程调用了一个阻塞式I/O方法,返回之前,线程被阻塞
        3. 线程试图获取一个锁对象,但锁对象被其他线程持有
        4. 线程在等待某个通知 Notify
    解除阻塞,重新进入就绪状态的情况:
        1. 调用sleep方法的线程经过了指定的时间
        2. 线程调用的阻塞模式I/O方法已经返回
        3. 线程成功获得了试图获取的锁对象
        4. 线程等待某个通知时,其他线程发出通知Notify,

线程死亡
    线程死亡的方式:
        1. run方法或代表线程执行体的target 函数执行完成,线程正常结束
        2. 线程抛出一个未捕获的Exception 或 Error
        测试某个线程是否四万过的方法:调用线程对象的is_alive()方法,当线程处于就绪、运行、阻塞三种状态,方法返回True,当线程处于新建、死亡状态时,方法返回False
            import threading
            def action(max):
                for i in range(100):
                    print(threading.current_thread().name + " " + str(i))
            // 创建线程对象
            sd = threading.Thread(target=action,args=(100,))
            for i in range(300):
                print(threading.current_thread().name + " " + str(i))
                if i == 20:
                    sd.start()
                    print(sd.is_alive())
            //    试图启动死亡线程
                if i > 20 and not(sd.is_alive()):
                    sd.start()

Thread join() 用法
    让一个线程等待另一个线程完成的join方法,当在某个程序执行流中调用其他线程的join方法,调用线程将被阻塞,直接被join方法加入额join线程执行完成
    import threading
    def action(max):
        for i in range(max):
            print(threading.current_thread().name + " " + str(i))

    threading.Thread(target=action,args=(100,),name="new threading").start()
    for i in range(100):
        if i == 20:
            jt = threading.Thread(target=action, args=(100,), name="join threading")
            jt.start()
            jt.join()
        print(threading.current_thread().name + " " + str(i))

Python守护线程及作用
    后台线程Daemon Thread: 后台运行,任务时为其他线程提供服务。又称 "精灵线程"。 特征:所有前台死亡,后台会自动死亡
    调用Thread对象的daemon属性可将指定线程设置为后台线程,
        import threading
        def action(max):
            for i in range(max):
                print(threading.current_thread().naem + " " + str(i))
    t = threading.Thread(target=action, args=(100.), name="daemon threading")
    // 将此线程设置为后台线程
    t.daemon = True
    t.start()
    for i in range(100):
        print(threading.currenct_thread().name + " " + str(i))
    创建线程的两种方法:
        1. 主动将线程的daemon 属性设置为True
        2. 后台线程启动的线程默认时后台线程
    将daemon 属性设为True,必须在start方法调用之前进行,否则引发 RuntimeError异常

sleep 函数用法: 线程睡眠
    如需让当前线程暂停一段时间,并进入阻塞状态,可调用 time 模块的 sleep(secs)函数实现,secs参数为指定线程阻塞多少秒
    import time
    for i in range(10):
        print("now time : %s" % time.ctime())
        time.sleep(1)

互斥锁Lock: 解决多线程安全问题
    多线程优势在于并发性,可同时运行多个任务, 互斥锁解决数据不同步问题
        类银行取钱:
            1. 用户输入账号、密码,系统判断用户输入信息是否匹配
            2. 输入金额
            3. 系统判断余额是否大于取款金额
            4. 若大于,则成功,如小于,则失败
        并发取款:
            class Account:
                def __init__(self,account_no,balance):
                    self.account_no = account_no
                    self.balanace = balance

        取钱:
            import threading
            import time 
            import Account
            def draw(account,draw_amount):
                if account.blance >= draw_amount:
                    print(threading.current_thread().name + " get RMB successful " + str(draw_amount)) 
                    account.balance -= draw_amount
                    print('yu e ' + str(accnout.balance))
                else:
                    print(threading.current_thread().name + 'get RMB error ')
            user = Account.Account('12345',1000)
            threading.Thread(name='A',target=draw, args=(user,100)).start()
            threading.Thread(name='B',target=draw, args=(user,200)).start()
            [LOCK](http://c.biancheng.net/view/5107.html)

互斥锁同步线程
    python的threading模块引入互斥锁,解决run方法体不具有线程安全性,threading模块提供了lock和Rlock两个类,提供了两个方法来加互斥锁和释放互斥锁:
        1. acquire(blocking=True, timeout=-1):请求对lock或Rlock加锁,timeout参数指定加锁的秒数
        2. release() : 释放锁
    lock 和 Rlock 的区别:
        threading.lock: 一个基本的锁对象,每次只能锁一次,其余的锁请求,徐等待锁释放后才能获取
        threading.Rlock: 代表可重入锁,同一线程可多次锁定,也可多次释放,如使用Rlock,acquire和release 方法必须成对出现
    class X:
        def m():
            self.lock.acquire()
            try:
                paxx
            finally:
                self.lock.release()
    线程安全的类具有的特征:
        1. 该类的对象可被多个线程安全地访问
        2. 每个线程在调用该对象的任意方法后,可得到正确的结果
        3. 每个线程在调用该对象的任意方法后,该对象依然保持合理的状态

    减少线程安全带来的负面影响的策略:
        1. 不对线程安全类的所有方法进行同步,只对改变竞争资源方法进行同步
        2. 在单线程环境中使用线程不安全版本保证性能,在多线程环境中使用线程安全版本

死锁: 如何避免死锁
    当两个线程相互等待对方释放同步监视器时会发生死锁,在进行多线程编程时采取措施避免死锁
    [死锁](http://c.biancheng.net/view/2620.html)
避免死锁的方式:
    1. 避免多次锁定,避免同一个线程对多个Lock进行锁定
    2. 具有相同的加锁顺序
    3. 使用定时锁
    4. 死锁检测

Python condition实现线程通信
    线程调度具有一定的透明性,程序无法准确控制线程的轮换执行。如有需要,可通过线程通信保证线程协调运行,
    Condition对象保持协调。Condition可让那些已经得到lock对象却无法继续执行的线程释放Lock对象,Condition可唤醒其他处于等待状态的线程
    Condition类提供的方法:
        1. acquire([timeout])/release(): 调用Condition 关联的lock的acquire或release 方法
        2. wait([timeout]) : 导致当前进程进入Condition的等待池等待通知并释放锁,知道其他调用该Condition的Notify或notify_all() 方法来唤醒该线程
        3. notify() : 唤醒在该Condition 等待池中的单个线程并通知它,收到通知自动调用acquire 方法尝试加锁。
        4. notify_all() : 唤醒在该Condition 等待吃中等待的所有线程并通知他们
        [More](http://c.biancheng.net/view/2622.html)    

Queure 队列实现线程通信
    queue模块提供阻塞队列,用于实现线程通信。主要提供三个类: 主要区别在于进队列、出队列的不同
        1. queue.Queue(maxsize=0): 代表FIFO 先进先出的常规队列,maxsize 可限制队列的大小。 如maxsize为0 或负数,表示该队列的大小无限制
        2. queue.LifoQueue(maxsize=0): 代表LIFO 后进先出,与Queue区别是出队列的顺序不同
        3. PriorityQueue(maxsize=0): 代表优先级队列, 优先级最小的元素先出队列
    三个队列类的属性和方法提供的属性和方法:
        Queue.qsize(): 返回队列的实际大小,即该队列中包含的元素个数
        Queue.empty(): 判断是否为空
        Queue.full() : 判断队列是否已满
        Queue.put(item,block=True,timeout=None): 向队列中放入元素,如已满,且block参数为True阻塞,timeout指定阻塞时间,如timeout为None,则代表一直阻塞,
        Queue.put_nowait(item): 向队列中放入元素,不阻塞。
        Queue.get(item,block=True,timeout=None):从队列中取出元素,如队列已满,则block参数为True阻塞。
        Queue.get_nowait(item): 从队列中取出元素,不阻塞
    普通的Queue的功能和用法:
        import queue
        bq = queue.Queue(3)
        bq.put('Python')
        bq.put('PHP')
        print('123213')
        bq.put('Queue')
        print('2222')

    利用Queue实现线程通信
        import threading
        import time 
        import queue
        def product(qu):
            str_tuple = ('python','golang','php ')
            for i in range(9999):
                print(threading.current_thread().name + " Product Env")
                time.sleep(0.2)
                qu.put(str_tuple[i % 3])
                print(threading.current_thread().name + 'product ok')
        def consume(qu):
            while True:
                print(threading.current_thread().name + 'take money' )
                time.sleep(0.2)
                t = qu.get()
                print(threading.current_thread().name + 'take ok %s '% t)
        qu = queue.Queue(maxsize = 1)
        threading.Thread(target=product,args=(qu,)).start()
        threading.Thread(target=product,args=(qu,)).start()
        threading.Thread(target=product,args=(qu,)).start()
        threading.Thread(target=consume,args=(qu,)).start()

Event 实现线程通信
    间的线程通信机制,一个线程发出一个Evnet,另一个线程可通信该Evnet 被触发
    Event 是一个内部旗标,可通过Event 的set方法将旗标设置为True,可调用clear方法将旗标设置为False,可调用wait 方法来阻塞当前进程。
    Event方法:
        1. is_set(): 返回Event 的内部旗标是否为True
        2. set() : 把Event的内部旗标设置为True,并唤醒所有处于等待状态的进程
        3. clear() : 将Event的内部旗标设置为False,调用wait方法阻塞当前进程
        4. wait(timeout=None): 阻塞当前进程
            import threading
            import time
            event = threading.Event()
            def cal(name):
                print('%s start ' % threading.currentThread().getName())
                print('%s status' % name)
                event.wait()
                print('%s get message' % threading.currentThread().getName())
                print('%s is status' % name)
            threading.Thread(target=cal,args=('Ai',)).start()
            threading.Thread(target=cal,args=('Bi',)).start()
            time.sleep(3)
            print('=====')
            print('master thread event')
            event.set()
            [More](http://c.biancheng.net/view/2626.html)








线程池及其原理和使用
    当启动新线程的时,使用线程池可提升性能 。线程池在系统启动时即创建大量空闲的线程。
    线程池的使用:
        基类是concurrent.futures模块中的Executor。提供两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor。 ThreadPollExecutor 用于创建线程池,ProcessPoolExecutor创建进程池
        Executor提供的常用方法:
            submit(fn,args,**kwargs): 将fn函数提交给线程池, *args 代表传给fn函数的参数,*kwargs 代表以关键字参数的形式给fn函数传入参数
            map(func,*iterables,timeout=None,chunksize=1):类全局函数
            map(func,*iterables),该函数加你个启动多个线程,以异步方式立即对iterables执行map处理
            shotdown(wait=True): 关闭线程池
        submit方法返回Future对象,Future 提供的方法
            cancel():取消该Future代表的线程任务,如任务正在执行,不可取消,返回False
            cancelled :返回线程任务是否被成功取消
            running : 如正在执行,不可取消,返回False
            done ;如任务被成功取消,返回True
            result(timeout=None) : 获取线程返回的结果,如任务还未完成,该方法会阻塞当前线程,timeout指定组赛的秒数
            exception(timeout=None):任务引发的异常,如任务成功完成,没异常,则返回None
            add_done_callback(fn):为该Future 的线程注册一个 回调函数,任务完成,自动出发fn函数。
    使用线程池执行线程任务的步骤:
            1. 调用ThreadPoolExecutor 类的构造器创建一个线程池
            2. 定义一个普通函数作为线程任务
            3. 调用ThreadPoolExecutor对象的submi方法来提交线程任务
            4. 调用ThreadPoolExecutor对象的shutdown方法来关闭线程池
                from concurrent.futures import ThreadPoolExecutor
                import threading
                import time

                def action(max):
                    sum = 0
                    for i in range(max):
                        print(threading.current_therad().name() + ' ' + str(i))
                        sum += i
                    return sum
                pool = ThreadPoolExecutor(max_workers=2)
                future1 = pool.submit(action,50)
                future2 = pool.submit(action,100)
                print(future1.done())
                time.sleep(3)
                print(future2.done())
                print(future1.result())
                pool.shutdown()
    获取执行结果
        1.调用result方法获取线程任务的返回值。
        2.通过Future的add_done_callback()方法添加回调函数
          线程池实现了上下文管理协议Context Manage Protocol,程序可用with语句来管理线程池,避免手动关闭线程池
          map方法会为iterables的每个元素启动一个线程,以并发方式执行func函数,相当于启动len(iterables)个线程,并收集每个线程的执行结果。
    threading local函数:返回线程局部变量
            threding 提供local函数,可返回一个线程局部变量,使用线程局部变量可很简捷隔离多线程访问的竞争资源。
            线程局部变量Thread local Variable,为每个使用该变量的线程提供一个变量的副本,
    线程局部变量的作用:
        import threading
        from concurrent.futures import ThreadPoolExecutor
        data = threading.local()
        def action(max):
            for i in range(max):
                try:
                    data.x += i
                except:
                    data.x = i
                print(" data %d" % (threading.current_thread().name,data.x)) 
        with ThreadPoolExecutor(max_workers=2) as pool:
            pool.submit(action,10)
            pool.submit(action,10)

Timer 定时器:控制函数在特定时间执行
    Thread类的子类Timer,可用于控制指定函数在特定时间内执行一次,
        from threading import Timer
        def hi():
            print('hi')
        t = Timer(10.0, hi)
        t.start()
    取消Timer的调度 cancel 函数

schedule 任务调度及用法
    如需执行更复杂的任务调度,使用sched模块,提供了 sched.scheduler类,该类代表一个任务调度器
    sched.scheduler(timefunc=time.monotonic,delayfunc=time.sleep) 构造器支持两个参数:
        1. timefunc : 指定生成时间戳的时间函数,默认使用time.monotonic 生成时间戳
        2. delayfunc; 指定阻塞程序的函数,默认使用 time.sleep 函数阻塞程序
        [More](http://c.biancheng.net/view/2630.html)
    sched.scheduler 调度器常用属性和方法:
        scheduler.enterabs(time,priority,action,argument=(),kwargs={}): 指定time时间点执行action函数,argument 和 kwargs 用于向 action函数传入参数,arg...使用位置的形式传入参数。 kwargs 使用关键字传入参数
    scheduler.enter(delay,priority,action, argument=(),kwargs={}): delay 指定多少秒后执行action任务。作用同上
    scheduler.cancel(event): 取消任务
    scheduler.empty(): 判断调度器队列是否为空
    scheduler.run(blocking=True): 运行所有需要调度的任务
    scheduler.queue: 只读属性返回该调度器的调度队列
        import sched,time
        improt threading
        s = sched.scheduler()
        def print_time(name='default'):
            print(' %$ de time %s' % (name,time.ctime()))
        print('master threading time', time.ctime())
        s.enter(10,1,print_time)
        s.enter(3,2,print_time,argument=('wei zhi hanshu '))
        s.enter(5,2,print_time,kwargs=['name':'guanjianzi hanshu '])
        s.run()
        print('master ', time.ctime())

os.fork方法:创建新进程
    多进程实现并发编程
    fork 方法作用: 程序会启动两个进程(一个主进程,一个fork出来的子进程)来执行从os.fork() 开始的所有代码
        fork方法不需要函数,有返回值表明哪个进程在执行:
            1. 如果fork返回0, 表明fork出来的子进程在执行
            2. 如fork返回非0, 表明父进程在执行,返回fork出来的子进程的进程ID
                import os
                print('master %s ' % os.getpid())
                pid = os.fork()
                print('worker in %s ' % os.getpid())
                if pid == 0:
                    print('origin id  %s  master  id %s' % (os.getpid(),os.getppid()))
                else:
                    print('me %s makr son id %s' % (os.getpid(),pid))
                print('thread over %s ' % os.getpid())

Process 创建进程的2种方法
    1. 指定函数作为target ,创建Process对象即可创建新进程
    2. 继承Process 类,重写run方法来创建进程类,创建process子类的实例作为进程
    Process 类的属性和方法:
        1. run() : 实现进程的执行体
        2. start : 启动进程
        3. join([timeout]) : 类线程的join方法
        4. name : 设置和访问进程的名字
        5. is_alive :判断进程是否活着
        6. daemon : 判断是否设置进程的后台状态
        7. pid : 返回进程的ID
        8. authkey :返回进程的授权key
        9. terminate : 中断该进程
    以指定函数作为target 创建新进程
            import multiprocessing
            import os
            def action(max):
                for i in range(max):
                    print('%s subprocess paterprocess %s  id %d ' % (os.getpid(),os.getppid(), i))
            if __name__ == '__main__':
                for i in range(100):
                    print('parent %s id %d' % (os.getpid(), i))
                    if i == 20:
                        mp1 = multiprocessing.Process(target=action,args=(100,))
                        mp1.start()
                        mp2 = multiprocessing.Process(target=action,args=(100,))
                        mp2.start()
                        mp2.join()
                print('master process is ok')

    继承Process 类 创建子进程
        步骤:
            1. 定义继承Process 的子类,重写run方法准备作为进程执行提
            2. 创建Process 子类的实例
            3. 调用 Process 子类的实例的start方法来启动进程
                import multiprocessing
                import os
                class MyProcess(multiprocessing.Process):
                    def __init__(self,max):
                        self.max = max
                        super().__init__()
                    def run(self):
                        for i in range(self.max):
                            print('%s subprocess %s parent process %d ' % (os.getpid(),os.getppid(),i))
                if __name__ == '__main__':
                   for i in range(100):
                        print('%s master process %d ' % (os.getpid(),i))
                        if i == 20:
                            mp1 = MyProcess(100)
                            mp1.start()
                            mp2 = MyProcess(100)
                            mp2.start()
                            mp2.join()
                    print('master process is ok!')

设置进程启动的3种方式
    1. spawn :父进程启动解释器进程,子进程继承run方法所需的资源。不必要的文件描述和handler都不被继承,效率比fork或forkserver方式要低得多。 Windows 只支持spawn方式
    2. fork: 通过os.fork 启动解释器, 子进程继承父进程所有资源,子进程等效于父进程
    3. forkserver : 启动一个服务器进程,当再次启动新进程,父进程会连接到该服务器进程。请求由服务器进程来fork新进程
    multiprocessing 模块提供set_start_method 函数,用于设置启动进程的方式,必须将这行设置代码放在所有与多进程相关代码之前。 
    if __name__ == '__main__':
        multiprocessing.set_start_method('spawn')
        q = multiprocessing.Queue()
        mp = multiprocessing.Process(target=foo,args=(q,))
        mp.start()
        print(q.get())
        mp.join()

多进程和多线程优缺点
    都使用并行机制提升系统运行效率,区别在于运行时所占内存分布不同,多线程共用一套内存的代码块区间,而多进程是各用一套独立的内存区间
    多进程有点在于 稳定性好,一个子进程奔溃,不影响主进程和其余进程,此特性多用多进程来实现守护服务器的功能
    多进程创建进程的代价非常大,操作系统会给每个进程分配固定的资源,会对进程的总数有一点的限制。
    多线程效率高 ,用于批处理任务等功能。 不足:一个线程奔溃整个进程奔溃。
    场景: 计算密集型的任务,多线程效率更高。 IO密集型的任务,如文件操作,网络爬虫,采用多线程
    IO密集型操作,消耗时间是等待时间,Python会释放GIL供新的线程使用,实现线程间的切换。    将多进程程序分布运行在不同的计算机上协同工作,每一进程内部,由多个线程并行工作
    最佳线程数量 = ()(线程等待时间+线程CPU时间) / 线程CPU时间) * CPU数量

使用进程池管理进程
    如需启动多个进程,可使用进程池管理进程,程序可通过multiprocess模块的pool函数创建进程池: multiprocessing.pool.Pool类
    进程池常用方法:
        1. apply(func[,args[,kwds]]) : 将func函数提交给进程池处理,args 传给func的位置参数, kwds代表传给func的关键字参数,会被阻塞直到func函数执行完成
        2. apply_async(func[,args[,kwds[,callback[,error_callback]]]]): 异步,不被阻塞。callback指定func函数完成后的回调函数,error_callback 指定fun指定回调函数
        3. map(func,iterable[,chunksize]) : 类python的map全局函数,使用新进程对iterable的每个元素执行func函数
        4. imap(func,iterable[,chunksize]): map方法的延迟版本
        5. imap_unordered(func,iterable[,chunksize]):类imap,不保证元素顺序一致
        6. starmap(func,iterable[,chunksize]): 类map方法,要求iterable的元素是iterable对象,
        7. close : 关闭进程池,不再接收新任务,把进程池中的所有任务执行完后再关闭自己
        8. terminate : 立即中止进程池
        9. join : 等待所有进程完成
        import multiprocessing
        import time
        import os
        def action(name='default'):
            print('%s process param %s ' % (os.getpid(),name))
            time.sleep(3)
        if __name__ == '__main__':
            pool = multiprocessing.Pool(processes=4)
            pool.apply_async(action)
            pool.apply_async(actino,args=('location parame:',))
            pool.apply_async(action,kwds={'name':'kwords params'})
            pool.join()
        线程池同样实现上下文管理协议,可使用with子句来管理进程池,避免程序主动关闭进程池
            import multiprocessing
            import time
            import os
            def action(max):
                sum = 0
                for i in range(max):
                    print('%s %d ' % (os.getpid(),i))
                    sum += i
                return sum
            if __name__ == '__main__':
                with multiprocessing.Pool(processes=4) as pool:
                    results = pool.map(action,(50,100,200))
                    print('---')
                    for r in results:
                        print(r)

进程间通信的2种实现方法 Queue Pipe
    进程通信提供的2种机制:
        1. Queue : 一个进程向Queue中放入数据,另一个进程从Queue中读取数据
        2. Pipe : Pipe代表连接两个进程的管道,程序可调用Pipe函数时会产生两个连接段,分别交给两个进程,进程可从连接端读取数据,也可向该连接端写入数据
    使用Queeu实现进程间通信
        multiprocessing 模块下的Queue和queue 模块下的Queue类似,都提供qsize 、empyt、full、put、put_nowwait、get、get_nowait 等方法,区别: multiprecessing 模块下的Queue为进程提供服务, 而queue模块下的Queue为线程提供服务
        import multiprecessing
        def f(q):
            print('%s ' % multiprocessing.current_process().pid)
            q.quit('Python')
        if __name__ == '__main__':
            q = multiprocessing.Queue()
            p = multiprocessing.Process(target=f,args=(q,))
            p.start()
            print('%s ' % multiprocessing.current_process().pid)
            print(q.get())
            p.join

使用Pipe实现进程间通信
    程序调用 multiprocessing.Pipe() 创建一个管道,返回两个PipeConnection对象,代表管道的两个连接端,一个管道有两个连接端,分别用于连接通信的两个进程
    PipeConnection对象包含的常用方法:
        1. send(obj) : 发送一个obj给管道的另一端,另一端使用 recv方法接收, 该obj需是可picklable的python序列化机制,如该对象序列化超过32MB,可引发ValueError异常
        2. recv :接收另一端通过send方法发送过来的数据
        3. fileno:关于连接所使用的问价描述器
        4. close : 关闭连接
        5. poll([timeout]):返回连接中是否有数据可读取
        6. send_types(buffer[,offset[,size]]: 发送字节数据,使用recv_bytes 或 recv_bytes_into 方法接收
        7. recv_bytes([maxlength])):通过send_bytes方法发送的数据,maxlength指定最多接收的字节数,返回接收到的字节数据
        8. recv_bytes_into(buffer[,offset]): 类recv_bytes方法,将接收到的数据放在buffer中
            import multiprocessing
            def f(conn):
                print('%s ' % multiprocessing.current_process().pid)
                conn.send('Python')
            if __name__ == '__main__':
                parent_conn,child_conn = multiprocessing.Pipe()
                p = multiprocessing.Process(target=f, args=(child_conn,))
                p.start()
                print('%s get data' % multiprocessing.current_process().pid)
                print(parent_conn.recv())
                p.join()

网络编程

计算机网路的功能:
    1. 资源共享
    2. 信息传输与集中处理
    3. 均衡负荷与分布处理
    4. 总和信息服务
常见的类型有: 局域网LAN、城域网MAN、广域网WAN。
通信协议:负责对传输速率、传输代码、代码结构、传输控制步骤、出错控制等指定处理标准。
    通信协议由三部分组成:
        1. 语义: 决定双方对话的类型
        2. 语法: 决定双方对话的格式
        3. 交换: 决定通信双方的应答关系
        OSI : Open System Interconnection :将网络简化,以模块化的方式来设计网络
        OSI七层: 物理层、链路层、网络层、传输层、会话层、表示层、应用层。
    通信协议:是网络通信的基础,IP:Internet Protocol 称为 网际协议,支持网间互联的数据报协议,提供来网间连接的完善功能。TCP:Transmission Control Protocol,传输控制协议,规定一种可靠的数据信息传送服务,可单独使用,功能是互补的,两个协议统称为 TCP/IP 协议
IP地址和端口号
    IP地址用于唯一标识网络中的一个通信实体。 IP地址是数字型,是一额32位整数
    NIC:Internet Network Information Center : 统一负责全球IP地址的规划和管理,分为 InterNIC、APNIC、RIPE 三个网络信息中心负责IP地址分配。亚太地区通过APNIC,总部设在日本东京大学
    IP地址分为 A、B、C、D、E五类。每个类别的网络标识和主机标识各有规则:
        1. A 类: 10.0.0.0 ~ 10.255.255.255
        2. B 类: 172.16.0.0 ~ 172.31.255.255
        3. C 类: 192.168.0.0 ~ 192.168.255.255
        IP地址是一个通信实体,每个通信实体可有多个通信程序同时提供网络服务,还需提供使用端口
        端口:是一个16位整数,用于将数据交给哪个通信程序处理。端口是应用程序与外界交流的出入口,是一种抽象的软件结构,包括数据结构和 I/O 缓冲区
        端口分为三类:
            1. 公认端口(Well Known Port): 端口号为 0~1023,绑定特定的服务
            2. 注册端口(Registered Port): 端口号为 1024~49151
            3. 动态和/或私有端口(Dynamic and / or Private Port): 端口号为 49152~65535,是应用程序使用的动态端口

网络编程模块
    网络模型大致分为四层,各有对应的网络协议提供支持
        1. 网络接口层: LAN、MAN、WAN
        2. 网络层: ICMP、IGMP、IP、ARP、RARP
        3. 传输层: TCP、UDP
        4. 应用层: SMTP、FTP、DNS、SNMP、NFS、HTTP、TELNET
    网络层协议主要是IP,是互联网协议的基础,ICMP、IGMP、ARP、RARP等协议是IP协议族的子协议,很少直接基于网络层进行应用程序编程
    Python标准库中的网络相关模块
        socket : 基于传输层TCP、UDP协议进行网络编程的模块
        asyncore : socket 模块的异步版,支持基于传输层协议的异步通信
        asynchat : asyncore 的增强版
        cgi : 基于CGI:Common Gateway Interface,早期动态网站的技术支持
        email : E-mail 和MIME消息处理模块
        ftplib : 支持FTP协议的客户端模块
        httplib、http.client : 支持HTTP协议以及HTTP客户端的模块
        imaplib : 支持IMAP4协议的客户端模块
        mailbox : 操作不同格式邮箱的模块
        mailcap : 支持Mailcap文件
        nntplib : NTTP协议
        smtplib : SMTP 协议,发送邮件的客户端模块
        poplib : 支持POP3协议
        telnetlib : 支持TELNET 协议
        urllib及其子模块: 支持URL处理的模块
        xmlrpc、xmlrpc.server、xmlrpc.client :支持XML-RPC协议的服务和客户端模块

urllib.parset 模块:
    URL:Uniform Resource Locator 对象代表统一资源定位器,指向互联网资源的指针。资源可以是文件、目录、或复杂对象的引用。 URL可由协议名、主机、端口和资源路径组成
        protocol://host:port/path
        1. urllib.request:最核心的子模块,包含打开和读取URL的各种函数
        2. urllib.error : 包含urllib.request 子模块所引发的各种异常
        3. urllib.parset : 解析URL
        4. urllib.robotparset : 解析robots.txt 文件
        通过使用urllib模块可打开任意URl所指向的资源,可完整下载远程页面,与re模块结合使用,可提取页面中的各种信息,即网络爬虫的初步原理
            urllib.parse 子模块中用于解析URL地址和查询字符串的函数:
                1. urllib.parse.urlparset(urlstring,scheme=",allow_fragments=True"):解析URL字符串,返回ParseResult 对象,获取解析出的数据
                2. urllib.parse.urlunparse(parts): 是上一函数的反向操作,解析结果反向拼接URL地址
                3. urllib,parse.parse_qs(qs,keep_blank_values=False,strict_parsing=False,encoding='utf-8',errors='replace'):解析查询字符串(application/x-www-forn--=urlencoded 类型的数据),以dict形式返回解析结果
                                                                                                        4. urllib.parse.urlencode(query,deseq=False,safe='',encoding=None,errors=None,quote_via=quote_plus): 将字典形式或列表形式的请求参数恢复成请求字符串。相当与parse_qs、parse_qsl 的逆函数
                                                                                                                                                                                                5. urllin.parse.urljoin(base,url,allow_fragments=True): 将一个base_url和另一个资源URL连接成代表绝对地址的URl
                from urllib.parse import *
                result = urlparse('https://www.baidu.com')
                print(result)
                print('scheme:', result.scheme,result[0])
                print('hostname and port:', result.netloc,result[1])
                ...
                print(result.geturl())
        ParseResult 各属性与元组索引的对应关系
                scheme        0        返回URL的scheme                scheme参数
                netloc        1        网络位置部分主机名和端口    空字符串
                path        2        资源路径                    空字符串
                params        3        资源路径的附加参数    
                query        4        查询字符串
                fragment    5        Fragment标识符
                username            用户名                        None
                password            密码
                hostname            主机名
                port                端口                        None
        urlunparse : 把一个 ParseResult 对象或元组恢复成URL字符串
        result = urlunparse(('http','www.baidu.com:80','index.php','text','name=hale',frag))
        print(result)
            parse_qs() parse_qsl() : l代表list,用于解析查询字符串,返回值不同,
        urljoin 负责将两个URL拼接在一起,返回代表绝对值的URL,可出现三种情况:
            1. URL只是一个相对路径path
            2. 被拼接的URl是一个根路径path
            3. URL是一个绝对的path

urllib.requset 模块读取资源用法
    urllib.request.urlopen(url,data=None):用于打开url指定的资源,并从中读取数据,根据url的不同,返回值发生变化,如url是一个HTTP地址,该方法返回一个http.client.HTTPResponse对象
    from urllib.request import *
    // 打开URL对应的资源
    result = urlopen('http://www.baidu.com/index.php')
    // 按字节读取数据
    data = result.read(333)
    // 将字节数据恢复成字符串
    print(data.decode('utf-8'))
    // 用context manager 管理打开的URL资源
    with urlopen('http://www.baidu.com/index.php') as f:
        // 按字节读取数据
        data = f.read(333)
        // 将字节数据恢复成字符串
        print(data.decode('utf-8'))

    使用urlopen函数时,可通过data属性向被请求的URL发送数据:
        from urllib.request import * 
        with urlopen(url='https://www.baidu.com/index.php',data='test'.encode('utf-8')) as f:
            print(f.read().decode('utf-8'))
    通过urlopen 函数发送POST请求参数,可通过data 属性来实现:
        import urllib.parse
        params = urllib.parse.urlencode({'name':'hale','password':'password'})
        params = params.encode('utf-8')
        with urlopen('https://www.baidu.com/index.php',data=params) as f:
            print(f.read().decode('utf-8'))
    urllib.request.Request对象的构造函器:
        urllib.request.Request(url,data=None,headers={},origin_req_host=None,unverifiable=False,method=None)
            Request 可通过method指定请求方法,也可通过data指定请求方法,可通过 headers 指定请求头
            from urllib.request import *
            params = 'put request'.encode('utf-8')
            req = Request(url='https://www.baidu.com/index.php',data=params,method='PUT') 
            with urlopen(req) as f:
                print(f.status)
                print(f.read().decode('utf-8'))
    使用Request对象添加请求头
        // 创建Request对象
        req = Request('https://www.baidu.com/index.php')
        // 添加请求头
        req.add_header('Referer','https://www.baidu.com')
        with urlopen(req) as f:
            print(f.status)
            print(f.read().decode('utf-8'))
        通过Request 的add_header 方法添加一个Referer 请求头,
    实现多线程下载的步骤:
        from urllib.request import * 
        import threading
        class DownUtil:
                def __init__(self,path,target_file,thread_num):
                    self.path = path
                    self.thread_num = thread_rum 
                    self.threads = []
                def download(self):
                    req = Request(url=self.path,method='GET')
                    ...


        1. 使用 urlopen 方法打开远程资源
        2. 获取指定的URL对象所指向资源的大小,通过Content-Length响应头获取
        3. 计算每个线程应该下载网络资源的哪个部分,从哪个节点开始,到哪个字节结束
        4. 依次创建并启动多个线程
            from DownUtil import * 
            du = DownUtil('https://www.baidu.com/' + 'from/logo.png','a.png',3)
            du.download()
            def show_process():
                print('ok : %.2f' % du.get_complete_rate())
                global t
                if du.get_complete_rate() < 1:
                    t = threading.Timer(0.1,show_process)
            t = threading.Timer(0.1,show_process)
            t.start()

http.cookiejar模块:管理cookie
    如使用urllib.request 模块来访问被保护页面,维修与服务器之间的sesion,借助于 cookie 管理器
    使用OpenerDirector 对象来发送请求,步骤:
        1. 创建 http.cookiejar.CookieJar 对象或其子类的对象
        2. 以CookieJar对象为参数,创建urllib.rquest.HTTPCookieProcessor对象,该对象负责调用CookieJar来管理cookie
        3. 以HTTPCookieProcessor对象为参数,调用urllib.reques.build_opener()函数创建OpenerDirector对象
        4. 使用OpenerDirector对象来发送请求,通过HTTPCookieProcessor调用CookieJar管理cookie
            from urllib.request import *
            import http.cookiejar, urllib.parse
            cookie_jar = http.cookiejar.MozillaCookieJar('a.txt')
            cookie_processor = HTTPCookieProcessor(cookie_jar)
            opener = build_opener(cookie_processor)
            user_agent = r'Mozialla ...'
            header = {'User-Agent':user_agent,'Connection':'keep-alive'}


        [More](http://c.biancheng.net/view/2646.html)

TCP协议、IP协议的关系
    TCP/IP通信协议是可靠的网络协议,在通信的两端各建立一个socket,形成虚拟的网络链路,建立虚拟的网络链路,两端的程序可通过该链路进行通信。 使用socket对象来代表两端的通信端口并通过socket进行网络通信
    IP是Internet 的关键协议,全称:Internet Protocol,即Internet协议,简称:IP协议。IP协议负责将消息从一个主机传送到另一个主机,信息被分割成一个个小包
    TCP:端对端协议,TCP协议让他们之间建立一个虚拟链路,用于发送和接收数据
    TCP协议负责收集数据包,并按照顺序传送,接收端接收到数据包后再将其正确地还原。TCP协议保证数据包传送无误,采用重发机制,即当一个通信实体发送一个消息给另一个通信实体后,需要接收到的另一个通信实体的确认信息,如没有收到确认信息,则会重发信息
    只有把TCP和IP两个协议结合,才能保证Internet在复杂的环境下正常运行。

socket 建立TCP连接

在使用socket之前,须建立socket对象,通过该类的构造器来创建socket实例:
    socket.socket(family=AF_INET,type=SOCK_STREAM, proto=0,fileno=None)
        1.family 参数用于指定网络类型,socket.AF_UNIX:UNIX网络、socket.AF_INET 基于IPv4协议的网络 和socket.AF_INET6 基于IPv6协议的网络 这三个变量
        2. type参数用于指定网络的Sock类型,支持SOCK_STREAM默认值,创建基于TCP协议的socket、SOCK_DGRAM 创建基于UDP协议的socket 和SOCK_RAW 创建原始socket。常用 SOCK_STREAM和SOCK_DGRAM
        3. proto参数用于指定协议号, 默认0,可忽略
        socket 对象提供的常用方法:
            1. socket.accept: 作为服务端使用的socket调用该方法接收来自客户端的连接
            2. socket.bind(address):将该socket绑定到指定address,address可是一个元组,包含IP地址和端口
            3. socket.close : 关闭连接,回收资源
            4. socket.connect(address): 连接远程服务器
            5. socket.connect_ex(address): 当程序出错时,不抛出异常,返回错误标识符
            6. socket.listen([backlog]): 服务器使用socket调用该方法进行监听
            7. socket.makefile(mode='r',buffering=None,*,encoding=None,errors=None,newline=None): 创建和该socket关联的文件对象

            8. socket.recv(bufsize[,flags]): 返回值是(bytes,address)元组
            9. socket.recvmsg(bufsize[,ancbufsize[,flags]]): 不仅接收来自socket的数据,还接收来自socket的辅助数据,返回值是一个长度为4的元组(data.ancdata,msg_flags,address).
            10. socket.recvmg_into(butters[,nbytes[,flags]]):类socket.recvmsg ,将接收的数据放入buffers中
            11. socket.recvfrom_into(buffer[,nbytes[,flags]]):将接收到的数据放入buffer中
            12. socket.recv_into([buffer[,nbytes[,flags]]]): 类recv方法,将接收到的数据放入buffer中
            13. socket.send(bytes[,flags]): 向socket发送数据,该socket必须与远程socket建立连接,基于TCP协议的网络中发送数据
            14. socket.sendto(bytes,addresss):向socket发送数据,没有与远程socket连接,基于UDP协议发送数据
            15. socket.sendfile(file,offset=0,count=None):将整个文件内容发送出去,直到遇到EOF
            16. socket.shutdown(how):关闭连接,how用于设置关闭方法
    TCP通信的服务器端编程的基本步骤:
        1. 服务器端先创建一个socket对象
        2. 服务器端socket将自己绑定到指定IP地址和端口
        3. 服务器端socket调用listen 方法监听网络
        4. 程序采用循环不断调用socket 的accept方法接收来自客户端的连接
            // 创建socket对象
            s = socket.socket()
            // 将socket 绑定到本机IP地址和端口
            s.bind('192.168.0.123',8888)
            // 服务器开始监听客户端的连接
            s.listen()
            while True:
                c,addr = s.accept()
    客户端先创建一个socket对象,将该socket绑定到指定的ip地址和端口号,然后调用connect方法建立与服务器的连接,就可建立一个基于TCP协议的网络连接
        TCP通信的客户端的基本步骤如下:
            1. 客户端先创建一个socket对象
            2. 客户端socket调用connect方法连接到远程服务器
    socket提供大量方法发送和接收数据:
        1. 发送数据:使用send方法,注意:sendto方法用于UDP协议的通信
        2. 接收数据:使用recv_xxx方法
            import socket
            s = socket.socket()
            s.bind(('192.168.1.88',999))
            s.listen()
            while True:
                c, addr = s.accept()
                print(c)
                print('connect addrss', addr)
                c.send('himessge '.encode('utf-8'))
                c.close()
    从socket中获取服务器发送的数据
        import socket
        s = socket.socket()
        s.connect(('192.168.1.88',999))
        print('--%s--'s.recv(1024).decode('utf-8'))
        s.close()

多线程实现socket通信
    由于socket的recv方法在成功读取到数据之前。线程会被阻塞,因此,服务器为每个socket单独启动一个线程,每个线程负责与一个客户端进行通信
    服务器端使用list来保存所有的socket
    import socket
    import threading
    socket_list = []
    ss = scoket.socket()
    ss.bind(('192.168.1.14',999))
    ss.listen()
    def read_from_client(s):
        try:
            return s.recv(2048).decode('utf-8')
        except:
            socket_list.remove(s):
    def server_target(s):
        try:
            while True:
                content = read_from_client(s)
                print(content)
                if content is None:
                        break
                for client_s in socket_list:
                    client_s.send(content.encode('utf-8'))
        except e:
            print(e.strerror)
    while True:
        s,addr = ss.accept()
        socket_list.append(s)
        threading.Thread(target=server_target,args=(s,)).start()

socket shutdown 方法
    以bytes对象作为通信的最小数据单位,服务器端在处理信息时是针对每个bytes进行的,一些协议中,通信数据单位可需多个bytes对象
    shutdown(how)方法,可只关闭socket的输入或输出部分,用以表示数据已经发送完成
    shutdown方法的how参数的参数值:
        SHUT_RD: 关闭socket的输入部分,可通过socket输出数据
        SHUT_WR: 关闭socket的输出部分,通过该socket读取数据
        SHUT_RDWR: 全关闭,该socket既不能读取数据,也不能写入数据
        服务器端先向客户端发送数据发送多条数据,当数据发送完成后,该socket对象调用shutdown方法来关闭输出部分
        import socket
        s = socket.socket()
        s.bind(('192.168.1.88',999))
        s.listen()
        skt,addr = s.accept()
        skt.send('server first data'.encode('utf-8'))
        skt.send('server second data'.encode('utf-8'))
        skt.shutdown(socket.SHUT_WR)
        while True:
            line = skt.recv(2048).decode('utf-8')
            if line is None or line == '':
                break
            print(line)
        skt.close()
        s.close()

selectors 模块:实现非阻塞式编程
    selectors 允许以非阻塞方式进行通信,selector相当于一个事件注册中心,只要将socket的所有事件注册给selectors管理,当检测到socket中的特定事件后,程序调用相应的监听方法进行处理
    selectors主要支持两种事件:
        1. selectros.EVENT_READ: 当socket有数据可读时触发该事件,有客户端连接时也触发
        2. selectors.EVENT_WRITE: 当socket将要写数据时触发该事件
    selectors实现非阻塞式编程的步骤如下:
        1. 创建selectors对象
        2. 通过selectors对象为socket的selectors.EVENT_READ或selectors.EVENT_WRITE事件注册监听器函数,当socket有数据读写时,系统负责触发所注册的监听器函数
        3. 在监听器函数中处理socket通信
    使用seletros实现非阻塞通信的服务器端:
        import seletors, socket
        sel = seletors.DefaultSelector()
        def read(skt,mask):
            try:
                data = skt.recv(1024)
                if data:
                    for s in socket_list:
                        s.send(data)
                else:
                    print('close ',skt)
                    sel.unregisters(skt)
                    skt.close()
                    socket_list.remove(skt)
            except:
                print('close')
                sel.unregister(skt)
                skt.close()
                socket_list.remove(skt)
        socket_list = []
        def accept(sock, mask):
            conn,addr = sock.accept()
            socket_list.append(conn)
            conn.setblocking(False)
            sel.register(conn,selectors.EVENT_READ,read)
        sock = socket.socket()
        sock.bind(('192.168.1.1',999))
        sock.listen()
        sock.setblocking(Flase)

UDP协议及优缺点
    UDP:User Datagram Protocol:用户数据报协议
    UDP 面向非连接的协议,是在正式通信前不必与对象先建立连接,不管对方状态,直接发送数据, UDP协议无法控制,是一种不可靠的协议
    UDP协议适用于一次只传送少量数据、对可靠性要求不高的应用环境
    作用:完成网络数据流和数据报之间的转换在信息的发送端,UDP协议将网络数据流封装为数据报,然后将数据发送出去,在信息的接收端,UDP协议将数据报转换为实际数据内容
    UDP协议和TCP协议的简单对比:
        1. TCP协议:可靠,传输大小无限制,需要建立连接,差错控制开销大
        2. UDP协议:不可靠,差错控制开销小,传输大小限制在64Kb以下,不需要建立连接。

socket发送和接收数据:基于UDP协议
    创建socket,通过type参数指定socket的类型,将参数指定为 SOCK_DGRAM,即创建基于UDP协议的socket
    通过两个方法发送和接收数据
        1. socket.sendto(bytes,address): 将bytes数据发送到address地址
        2. socket.recvfrom(bufsize[,flags]):接收数据,返回socket中的数据和数来源地址
            import socket
            PORT = 999
            DATA_LEN = 4096
            books = ('one','tow','three')
            s = socket.socket(type=socket.SOCK_DGRAM)
            s.bind(('192.168.1.1',PORT))
            for i in range(1000):
                data ,addr = s.recvfrom(DATA_LEN)
                print(data,decode('utf-8'))
                send_data = books[i % 4].encode('utf-8')
                s.sendto(send_data,addr)
            s.close()
    来自服务器端的数据,客户端程序代码:
            import socket
            PORT = 3000
            DATA_LEN = 4096
            DEST_IP = '192.168.1.1'
            s = socket.socket(type=socker.SOCK_DGRAM)
            while True:
                line = input('')
                if line is None or line == 'exit':
                    break
                data = line.encode('utf-8')
                s.sendto(data,(DEST_IP,PORT))
                data = s.recv(DATA_LEN)
                print(data.decode('utf-8'))
            s.close()

UDP 多点广播原理及实现
    多点广播,将数据以广播方式发送到多个客户端
    创建socket对象后,将该socket加入指定的多点广播地址中,socket使用setsockopt 方法加入指定组
    创建仅发送数据报的socket对象, 使用默认地址、随机端口即可。 如创建接收数据报的socket对象,将对象绑定到指定端口
多点广播可设置广播信息的TTL(Time-To-Live),TTL参数用于设置数据报最多可跨过的网络个数:
    1. TTL的值为0:指定数据报应停留在本地主机中
    2. 1: 指定将数据报发送到本地局域网中,此值为默认值
    3. TTL的值为 32 时: 只能将数据报发送到本站点的网络上
    4. 。。64 : 数据报应被保留在本地区
    5. 128 : 被保留在本大洲
    6. 255 : 数据可被发送到所有地方
    socket实现一个基于广播的多人聊天室,只需要一个socket、两个线程,socket用于发送数据、接收数据。主线程负责读取用户的键盘输入内容,并向socekt发送数据,子线程负责从socket中读取数据
        import time,socket,threading, os
        senderIP  = '192.168.1.99'
        senderPORT = 999
        myGroup = '230.0.0.1'
        s = socket.socekt(type=socket.SOCK_DGRAM)
        s.bind (('0.0.0.0',senderPORT))
        s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICASE_TTL,64)
        s.setsockopt(socket.SOL_SOCKET. socket.SO_REUSEADDR, 1)
        status = s.setsockopt(socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(mygroup))
        ...
        [More](http://c.biancheng.net/view/2663.html)

smtplib 模块: 发送邮件
    底层的处理由smtplib封装,3步发送邮件:
        1. 连接STMP服务器,使用用户名、密码登录服务器
        2. 创建EmailMessage 对象,该对象代表邮件本身
        3. 调用代表与SMTP服务器连接的对象的sendmail方法发送邮件
            import smtplib
            from email.message import EmailMessage
            smtp_server = 'smtp.qq.com'
            from_addr = 'mr_hale@qq.com'
            password = 'password'
            to_addr = 'mr_hale@163.com'
            conn = smtplib.SMTP_SSL(smtp_server,465)
            conn.set_debuglevel(1)
            conn.login(from_addr,password)
            msg = EmailMessage()
            msg.set_content('hi','plain','utf-8')
            conn.sendmail(from_addr,[to_add],msg.as_string())
            conn.quit()
        基于SSL的SMTP服务器的默认端口是465
        常见参数:
            1. maintype :指定附件的主类型,如image代表附件图片
            2. subtype : 附件的子类型,如指定为png,代表附件是png图片,子类型受主类型的限制
            3. filename : 指定附件的文件名
            4. cid = img : 指定附件的资源ID,可通过资源ID引用该资源
                import smtplib, email.utils
                from emial.message import EmailMessage
                smtp_server = 'smtp.qq.com'
                from_arrd = 'mr_hale@qq.com'
                password = 'pass'
                to_addr = 'mr_hale@163.com'
                conn = smtplib.SMTP_SSL(smtp_server,465)
                conn.set_debuglevel(1)
                conn.login(from_addr,password)
                msg = EmailMessage()
                first_id ,second_id  = email.util.make_msgid(), email.utils.make_msgid()
                msg.set_connect('<h1>hi</hi>')
                msg['subject'] = 'subject'
                msg['from'] = 'mr_hale@qq.com'
                msg['to'] = 'mr_hale@163.com'
                with open('file_Path:/xx.png','rb') as f:
                    msg.add_attchment(f.read(),maintype='image',
                        subtype='jpeg',filename='xxx.png',cid=first_id)
                with open('path_dir/xx.gif','rb') as f:
                    msg.add_attachment(f.read(),maintype='image',subtype='gif',filename='xxx.gif',cid=second_id)
                with open('xxx.pdf','rb') as f:
                    msg.add_attachment(f.read(),maintype='application',subtype='pdf',filename='xxx.pdf')
                conn.sendmail(from_arrd,[to_addr],msg.as_string())
                conn.quit()

poplib 模块: 收取邮件
    提供poplib.POP3 和poplib.POP3_SSL 两个类,用于连接POP服务器和基于SSL的POP服务器
        POP3 协议属于请求,响应式交互协议,当客户端连接服务器后,客户端向POP服务器发送请求,而POP服务器对客户端生成响应数据,客户端可通过响应数据下载得到邮件内容
    POP3的命令和数据都基于ASCII文本,以 CR 和 LF(/r/n)作为行结束符,响应数据包括一个表示返回状态的符号(+/)和描述信息
    请求和响应的标准格式:
        请求标准格式: 命令[参数] CRLF
        响应标准格式: +OK /[-ERR] description CRLF
    POP3协议客户端的命令和服务器端对象的响应数据:
        1. user name : 向POP服务器发送登录的用户名
        2. pass string : 向POP服务器发送登录的密码
        3. quit: 退出POP服务器
        4. stat :统计邮件服务器状态,包括邮件数和总大小
        5. list[msg_no] : 列出全部邮件或指定邮件,返回邮件编号和大小
        6. retr msg_no : 获取指定邮件的内容,编号从1开始
        7. del msg_no : 删除指定邮件
        8. noop : 空操作,仅用于于服务器保持连接
        9. rset : 用于撤销dele命令
    接收邮件的步骤:
        1. 使用poplib.POP3 或 poplib.POP3_SSL 按 POP3 协议从服务器下载邮件
        2. 使用 email.parser.Parset 或 email.parser.BytesParser解析邮件内容,得到EmailMessage对象,从EmailMessage 对象中读取邮件内容
            import poplib,  os.path , mimetypes
            from email.parser import BytesParser, Parser
            from email.policy import default
            emial = 'mr_hale@163.com'
            password = 'password'
            pop3_server = 'pop.qq.com'
            conn = poplib.POP3_SSL(pop3_server, 995)
            conn.set_debuglevel(1)
            print(conn.getwelcome().decode('utf-8'))
            conn.user(email)
            conn.pass_(password)
            message_num, total_size = conn.stat()
            print('email num %s total %s' % (message_num,total_size))
            resp, mails, octets = conn.list()
            print(resp, mails)
            resp, data, octets = conn.retr(len(mails))
            msg_data = b'\r\n'.join(data)
            mg = BytesParser(policy=default).parsebytes(msg_data)
            [More](http://c.biancheng.net/view/2667.html)

            程序在创建BytesParser 解析字节串格式的邮件数据 或 Parser 解析字符串格式的邮件数据时,必须指定 policy=default, 否则 BytesParser或Parser 解析邮件数据得到的就是过时的Message对象
1
2
3
@Author : Hale Lv
@Created Time : 2019-08-27 20:41:18
@Description :

pydoc 模块:查看、生成帮助文档

在控制器中使用help 函数和 __doc__ 属性查看函数、类、方法的文档
借助自带的pydoc模块,方便查看、生成帮助文档,是HTML格式
创建名为 HI_doc.py 的文件
    MY_NAME = 'Python DOC'
    def say_hi(name):
        '''
            定义一个say hi的函数
            返回对指定用户打招呼的字符串
        '''
        print('run hi func')
        return name + 'welcome !'
    def run(load):
        '''
            走路ing
        '''
        print('run run func')
        return 'run ' + load
    class User:
        NAME = 'Hale'
        '''
            定义一个用户的类
            命名名字为Hale
        '''
        def __init__(self,name,age):
            '''
                name 为初始化的name值
                age 初始化该用户的age
            '''
            self.name = name
            self.age = age
        def eat(food):
            '''
                定义用户在吃东西的方法
                food 代表正在吃的东西
            '''
            print('%s eat %s' % (self.user, food))
        定义了一个HI_doc.py 源文件,即定义了一个HI_doc 模块, 该模块为函数、类、方法都提供文档

    pydoc在控制台中查看文档
        使用pydoc模块在控制台查看HTML文档
            python -m pydoc 模块名
                -m : 选项,表示运行指定模块,运行pydoc模块,模块名参数代表程序要查看的模块
                python -m pydoc HI_doc 
            pydoc 模块中的全部内容:
                CLASSES 部分: 列出该模块所包含的全部类
                FUINCTIONS : 列出包含的全部函数
                DATA : 全部成员变量
                FILE: 源文件

    pydoc 生成HTML文档
        python -m pydoc -w 模块名
            -m :代表模块0
            -w 选项: 代表 write,表明输出HTML文档
    pydoc 为指定目录生成HTML文档,为指定目录下的所有模块生成HTML文档:
        python -m pydoc -w 目录名

启动本地服务器查看文档信息
    启动本地服务器产看文档信息的两个命令
        1. python -m pydoc -p 端口号
    指定端口启动HTTP服务器,通过浏览器查看Python的所有模块的文档信息:
        2. python -m pydoc -b

    第一部分:显示python内置的核心模块
    第二部分:显示当前目录下的所有模块
    第三部分:显示当前目录下的所有模块,

pydoc 查找模块
    python -m pydoc -k 被搜索的模块的部分内容

doctest模块: 文档测试

文档测试工具可以提取说明文档中的测试用例,其中 ">>>" 之后的内容表示测试用例,下一行代表测试用例的输出结果
def squre(x):
    '''
    计算平方的函数
    eg :
    >>> squre(2)
    4
    >>> squre(4)
    16 
    '''
    return x * 2 # 故意写错的

class User:
    '''
    定义一个代表用户的类,包括属性:
    name - 代表名字
    age - 代表年龄
    例如:
    >>> u = User('hale',23)
    >>> u.name
    'hale'
    >>> u.age
    23ß
    '''

if __name__ == '__main__':
    import doctest
    doctest.testmdo()

测试结果包含的内容:
    1. 第一部分:显示在哪个源文件的哪一行
    2. 第二部分:Failed example,显示是哪个测试用例出错了
    3. 第三部分:Expected 显示程序期望的输出结果,即在">>>命令"的下一行给出的运行结果
    4. 第四部分:Got ,显示程序实际运行产生的输出结果,只有输出结果与期望结果一致,才表明测试用例通过
可见:为文档注释提供doctest模块,程序只要导入该模块,并调用模块的testmod 函数即可。 testmod 自动提取模块的说明文档的测试用例,并执行这些测试用例,最终生成测试报告。

unittest(PyUnit)单元测试框架

    PyUnit unittest 是单元测试框架,编写和运行可重复的测试。 PyUnit 是xUnit体系的一个成员,xUnix是众多测试框架的总称。主要用于进行白盒测试和回归测试
    好处: 
        可以使测试代码与产品代码分离
        针对某一个类的测试代码只需要进行较少的改动,便于另一个类的测试
        开源,方便对PyUnit的扩展

    特征:
        1. 使用断言方法判断期望值和实际值的差异,返回bool值
        2. 测试驱动设备可使用共同的初始化变量或实例
        3. 测试包结构便于组织和继承运行

    PyUnit(unittest)用法:
        通过给定参数执行函数,判断函数的实际输出结果和期望的输出结果是否一致,测试用例提供执行函数和方法, 获取他们的执行结果,然后使用断言方法来判断
        开发方式称为:测试驱动开发,强调先编写测试用例,然后在编写函数和方法,例:开发A功能的fun_a函数,采用测试驱动开发的步骤:
        1. 为fun_a 函数编写测试用例,使用大量不同的参数组合来执行fun_a函数,并断言该函数的执行结果与业务期望的执行结果匹配
        2. 编写、修改fun_a 函数
        3. 运行fun_a函数的测试用例,如不能完全通过,则重复第2步和第3步,直到fun_a的所有测试用例全部通过
            开发一个简单的 fk_math.py 程序,包含两个函数,分别用于计算一元一次方程的解和二元一次方程的解
                def one_equation(a,b):
                    '''
                    一元一次方程的解
                    返回方程解
                    '''
                    if a == 0:
                        raise ValueError('参数错误')
                    else:
                        return b / a
                def two_equation(a,b,c):
                    '''
                    一元二次方程 

                    '''
                    if a == 0:
                        raise ValueError("参数错误")
                    elif b * b - 4 * a * c < 0:
                        raise ValueError('方程在有理数范围内无解')
                    elif b * b - 4 * a * c == 0:
                        return -b / (2 * a)
                    else:
                        r1 = (-b + (b * b - 4 * a * c) ** 0.5 ) / 2 /a 
                        r2 = (-b + (b * b - 4 * a * c) ** 0.5 ) / 2 /a 
                        return r1, r2

                unittest要求单元测试类必须继承 unittest.TestCase,该类中的测试方法需要满足:
                    1. 测试方法应该没有返回值
                    2. 测试方法不应该有任何参数
                    3. 测试方法应以test开头
                        import unittest
                        from fk_math import * 

                        class TestFkMath(unittest.TestCase):
                            def test_one_equation(self):
                                self.assertEqual(one_equation(5,9), -1.8)

                                with self.assertRaises(ValueError):
                                    one_euqation(0,9)
                            def test_two_equation(self):
                                r1,r2 = two_equation(1,-3,2)
                                self.assertCountEqual((r1,r2),(1.0,2.0),'求解出错')
                                ...
                                with self.assertRaises(ValueError):
                                    two_equation(0,9,3)
                                    ...






            unittest.TestCase内置大量assertXxx方法执行断言:
                assertEqual(a,b)            a == b
                assertNotEqual(a,b)            a != b
                assertTrue(x)                bool(x) is True
                assertFalse(x)                bool(x) is False
                assertIsNot(a,b)            a is not b
                assertIs(a,b)                a is b
                assertIsNone(x)                x is None
                assertIsNotNone(x)            x is not None
                [More](http://c.biancheng.net/view/2679.html)
            TestCase 包含断言方法
                assertAlmostEqual(a,b)        round(a-b,7) == 0
                assertNotAlmostEqual(a,b)    round(a-b,7) != 0
                assertGreater(a,b)            a > b 
            TestCase 包含针对特定类型的断言方法
                assertMultiLineEqual(a,b)    字符串string
                assertSequenceEqual(a,b)    序列sequence
                assertListEqual(a,b)        列表list
                assertTupleEqual(a,b)        元组tuple
                assertSetEqual(a,b)            集合set 或 frozenset
                assertDictEqual(a,b)        字典dict

    运行测试
        两种方式:
            1. 通过代码调用测试用例,通过调用unittest.main() 运行当前源文件中所有测试用例        
                if __name__ == '__main__':
                    unittest.main()
            2. 使用unittest 模块运行测试用例,语法:
                python -m unittest 测试文件
        测试结果:
            1. . :代表测试通过
            2. F : 代表失败,F : failure
            3. E : 出错    E : error
            4. s : 跳过该测试    s : skip

TestSuite 测试包及用法
    可组织多个测试用例, 还可嵌套测试包,使用测试运行器 TestRunner 来运行该测试包所包含的所有测试用例
    // 开发一个程序 hello.py
    def say_hello():
        return 'hi'
    def add(a,b):
        return a + b
    // 为上面程序提供测试类 test_hello.py
        import unittest
        from hello improt *
        class TestHello(unittest.TestCase):
            def test_say_hell(self):
                self.assertEqual(say_hello(),'Hello world.')
            def test_add(self):
                self.assertEqual(add(2,1),5)
                self.assertEqual(add(3,6),7)
    将 test_fk_math 和 test_hello.py 放在同一目录,可通过TestSuite将他们组织在一起,然后使用TestRunner来运行该测试包
        import unittest
        from test_fk_math import TestFkMath
        test_cases = (TestHello, TestFkMath)
        def whole_suite():
            // 创建测试加载器
            loader = unittest.TestLoader()
            // 创建测试包
            suite = unittest.TestSuit()
            // 遍历所有测试类
            for test_class in test_cases:
            // 从测试类中加载测试用例
                tests = loaders.loadTestsFromTestCase(test_class)
            // 将测试用例添加到测试包
                suite.addTests(tests)
            return suite
        if __name__ == '__main__':
            // 创建测试运行器
            runner = unittest.TexTestRunner(verbosity=2)
            runner.run(whole_suite())
    把测试报告输出到该类文件对象中
        修改__main__ 部分代码:
            if __name__ == '__main__':
                with open('fk_test_report.txt','a') as f:
                    runner = unittest.TextTextRunner(verbosity=2,stream=f)
                    runner.run(whole_suite())

单元测试setUp 和 tearDown 用法
    测试用例类:TestCase的子类、测试包TestSuit、测试运行器TestRunner、测试固件TestFixture的概念:
        1. 测试用例类:是单个的测试单元,负责检查特定输入和对应的输出是否匹配,
        2. 测试包: 组合多个测试用例, 可嵌套测试包
        3. 测试运行器:负责组织、运行测试用例,向用户呈现测试结果
        4. 测试固件: 代表执行一个或多个测试用例所需的准备工作,及相关联的准备操作,包括:创建临时数据库、创建目录、开启服务器进程等。
        unittest.TestCase包含setUp 和 tearDown 两个方法,其实setUp方法用于初始化测试固件, tearDown 方法用于销毁测试固件。运行每个测试用例以 test_ 开头的方法之前自动执行setUp方法来初始化测试固件,并在每个测试用例结束后自动执行tearDown方法销毁测试固件
        import unittest
        from hello import * 
        class TestHello(unittest.TestCase):
            def test_say_hello(unittest.TestCase):
                self.assertEqual(say_hello(),'Hello world')
            def test_add(self):
                self.assertEqual(add(3,5),7)
            def setUp(self):
                print('---- setUp model--- ')
            def tearDown(self):
                print('----tearDown-----')
        python -m unittest -v fixture_test.py

unittest跳过测试用例
    unittest 自动测试每一个测试用例 以test开头的方法,如跳过某个测试用例,通过两种方式:
        1. 使用 skipXxx 装饰器 跳过测试用例,unittest 提供3个装饰器,分别是 @unittest.skip(reason) , @unittest.skipif(condition,reason), @unittest.skipUnless(condition,reason),其中 skip 代表无条件跳过,skiplf 代表当condition为True时跳过,skipUnless 代表当condition为False时跳过
        2. 使用TestCase 的skipTest 方法来跳过测试用例
            import unittest
            from hello import * 
            class TestHello(uniitest.TestCase):
                def test_say_hello(self):
                    self.assertEqual(say_hello(),'hi hello')
                @unittest.skip(say_hello(),'hello world')
                def test_add(self):
                    self.assertEqual(add(3,4),7)
                    ...
            @unittest.skip装饰器跳过了test_add 测试方法
            python -m unittest skip_test.py
            测试结果中,s 代表跳过第一个测试用例,. 代表第二个测试用例通过
        可使用TestCase 的skipTest 方法跳过测试用例:
            import unittest
            from hello import * 
            class TestHello(unittest.TestCase):
                def test_say_hello(self):
                    self.assertEqual(say_hello(),'hi')
                def test_add(self):
                    self.skipTest(add(3,4),5)
                    ...
            python -m unittest -v skip_test.py
                -v : 生成更详细的测试报告
1
2
3
@Author : Hale Lv
@Created Time : 2019-08-28 12:01:48
@Description :

打包和发布 zipapp 和 PyInstaller

用于将Python 应用打包为一个 .pyz 文件,无论多少个源文件和依赖包,使用zipapp可将他们大包为一个 .pyz 文件。
zipapp 模块: 将一个 python模块或多个模块打包为一个Python应用,可发布为Windows 的可执行程序

生成可执行的Python档案包
    zipapp 是一个可直接运行的模块,将单个python文件或整个目录下的所有文件打包为可执行的档案包
        python -m zipapp source [options]
            source :代表要打包的Python源程序或目录,参数可是单个的pyhton文件,也可是文件夹。        
            option 选项:
                -o <output>, --output=<output>: 指定输出档案包的文件名,如不指定该选项,生成的档案包的文件名默认是source 参数值,并加上 .pyz后缀
                -p <interpreter>, --python=<interpreter> : 指定Python解释器,
                -m <mainfn>, --main=<mainfn> : 指定Python程序的入口函数,该选项应为: pkg.mod:fn 形式,pkg.mod 是一个档案包中的包或模块,fn是指定模块中的函数,如不指定,默认从模块中的 __main__.py 文件开始执行
                -c , --compress : 指定是否对档案包进行压缩来减少文件的大小,默认不压缩
                --info : 用于诊断时显示档案包中的解释器
                -h , --help : 该选项用于显示 zipapp 模块中的帮助信息

    建立一个app 子目录, 包办多个python程序,如:say.py
            def say(name):
                return 'hi' + name
        在该目录下开发一个app.py 程序来使用 say 模块:
            from say import *
            def main():
                    print('start run')
                    print(say('hale'))
        在命令行中进入该目录(app目录的父目录):
            python -m zipapp app -o first.pyz -m "app:main"
                指定将当前目录下的app子目下的所有Py源文件打包为一个档案包,-o 选项指定生成档案包的文件名为 first.pyz; -m : 指定使用app.py 模块中的main函数作为程序入口
            python -m zipapp app -m "app:main"
                没有-o, 使用默认的输出文件名,生成一个 app.pyz文件

zipapp 创建独立应用

如需使用第三方模块和包
创建独立启动的应用 自带依赖模块和包,执行2个步骤:
    1. 将应用依赖的模块和包下载到应用目录中
    2. 使用zipapp将应用和依赖模块一起打包为档案包
    在app目录中创建一个dbapp子目录,在dbapp中创建一个__main__.py 文件作为程序入口,打包档案包时不需指定入口
        from exec_select import *
        query_db()
    exec_select.py 文件:
        import mysql.connector
        def query_db():
            conn = conn.connector.connect(user='user','passwor'='pass',host='localhost',port='3306',database='dbname',use_unicode=True)
        c = conn.cursor()
        c.execute('select * from user_tb where user_id > %s',(2,))
        for col in (c.description):
            print(col[0],end='\t')
        print('\n-----')
        for row in c:
            print(row)
            print(row[1] + '-->' + row[2])
        c.close()
        conn.close()
    1.    将dbapp子目录下的应用打包为独立应用:
        python -m pip install -r requirements.txt --target dbapp
            使用 pip 安装模块,python -m pip install 表示安装模块, -target 指定要安装到指定目录下, 此处安装到dbapp子目录下。-r 指定安装哪些模块,使用requirements.txt 列出要安装的模块和包 -r 选项支持两个值:
                1. 直接指定要安装的模块或包
                2. 使用清单文件指定要安装的模块和包
        如:pip 模块提示找不到 requirements.txt 文件,需在当前目录下添加一个requirements.txt 文件,在该文件中增加:  mysql-connector-python
            如模块需要依赖多个模块,则在requirements.txt 文件中定义多行,每行定义一个模块。
    2.    如pip在dbapp子目录生成 .dist-info 目录,删除即可
    3.  使用zipap 模块执行打包操作,如dbapp子目录下包含了 __main__.py 文件,则不需指定 -m 选项 
        python -m zipapp dbapp
    卸载在python目录下安装的mysql-connector-python 模块:
        pip uninstall mysql-connector-python

PyInstaller 安装和使用

    默认不包含PyInstaller 模块, 需自行安装
        pip install pyinstaller

PyInstaller生成可执行程序
    pyinstaller 选项 Python 源文件
        单文件、多文件,使用pyinstaller 编译作为程序入口的python 程序即可
        先创建一个app目录,在该目录下创建一个 app.py 文件
            from say_hello import *
            def main():
                print('start run')
                print(say_hello('hale'))
            if __name__ == '__main__':
                main()
            进入此app目录,执行:
                pyinstaller -F app.py 
                    执行完毕,会生成 dist目录、app.exe 文件
                    -F 选项: 指定生成单独的EXE 文件, —D :指定生成一个目录作为程序
                pyinstaller -D app.py
                    执行完毕,生成dist目录, 包含大量.dll 文件和 .pyz 文件
        PyInstaller 支持的常用选项
            -F, -onefile :                产生单个的可执行文件
            -D, --onedir :                产生一个目录(包含多个文件)作为可执行程序
            -a, --ascii  :                不包含Unicode 字符集支持
            -d, --debug  :                产生debug 版本的可执行文件
            -w, --windowed,--noconsolc : 指定程序运行时不显示命令行窗口(仅对windwos有效)
            -c, --nowindowed,-console :    指定命令行窗口运行程序
            -o DIR, --out=DIR :            指定spec文件的生成目录,如没指定,默认使用当前目录来生成spec文件
            -p DIR,--path=NAME    :            设置python导入模块的路径,可用路径分隔符(win:',',unix:':'来分割多个路径)
            -n NAME,--name=NAME :            指定项目的名字 如省略,第一个脚本的主文件名将作为spec的名字
            -h :        查看选项的详细信息

    创建带图形的用户界面,可访问数据库的应用
        在app所在目录创建一个dbapp目录,并在该目录下创建python程序,exec_select.py 负责查询数据, main.py 负责创建图形用户界面来显示查询结果
            exec_select.py 代码:
                import mysql.connector
                def query_db():
                    conn = mysql.connector.connect(user='root',password='pass',host='localhost',port='3306',database='dbname',use_unicode=True)
                    c = conn.cursor()
                    c.execute('select * from user_tb where user_id > %s',(2,))
                    description = c.description
                    rows = c.fetchall()
                    c.close()
                    conn.close()
                    return description, rows
            main.py 文件代码:
                from exec_select import *
                from tkinkter import * 
                def main():
                    description,rows = query_db()
                    win = Tk()
                    win.title('query db')
                    for i, col in enumerate(description):
                        lb = Button(win,text=col[0],padx=50,pady=0)
                        lb.grid(row=0,colunm=i)
                    for i, row  in enumerate(rows):
                        for j in range(len(row)):
                            en = Label(win, text=row[j])
                            en.grid(row=i+1,column=j)
                    win.mainloop()
                if __name__ == '__main__':
                   main()
            Pyinstaller -F -w main.py
1
2
3
@Author : Hale Lv
@Created Time : 2019-08-28 15:32:33
@Description :

数据可视化 Matplotlib, Pygal

Matplotlib

是Python 2D 绘图库,符号格式的数据,通过Matplotlib 方便制作折线图、柱状图、散点图等各种高质量的数据图
安装:
    1. pip install matplotlib
    2. python -m pip install matplotlib
python -m pydoc -p 8899        //    文档

Matplotlib plot 函数: 生成折线图
    给出对应的 x轴、y轴数据, 调用pyplot子模块下的plot函数即可生成简单的折线图
    如:生成2013年~2019年的销售数据
        import matplotlib.pyplot as plt
        x_data = ['2013','2014','2015','2016','2017','2018','2019']
        y_data = [123000,1232131,2131421,23532,453,645363,54654645]
        plt.plot(x_data,y_data)
        plt.show()
    plot函数支持创建具有单条折线的折线图,也支持多条折线复式折线图,调用plot函数传入多个分别x轴和y轴数据的list列表即可。
        import matplotlib.pyplot as plt 
        x_data = ['2018','2019']
        y_data = [5555555,666666]
        y_data1 = [777777,8888888]
        plt.plot(x_data,y_data,y_data1)
        plt.show()
    plot函数可传参数指定折线的样子,如 线宽、颜色、样式等,例:
        import matplotlib.pyplot as plt
        x_data = ['2016','2017','2018','2019']
        y_data = [100000,200000,300000,400000,500000]
        y_data1 = [900000,800000,70000,40000,32222]
        plt.plot(x_data,y_data,color='red',linewidth=2.0, linestyle='--')
        plt.plot(x_data,y_data1,color='blue',linewidth=3.0,linestyle='-.')
        plt.show()
            color: 颜色,  linewidth : 线宽 、 linestyle : 折线样式
            linestyle 折线样式,字符串参数值:
                -  : 代表实线,默认值
                -- : 代表虚线
                .  : 代表点线
                -. :  代表短线、点相同的虚线    

Matplotlib legend  : 为每条折线添加图例
    对于复式折线图,应为每条折线添加图例,通过legend 函数实现,可传入两个list参数,第一个list参数 handles参数,用于引用折线图赏的每条折线,第二个list参数labels 代表为每条折线所添加的图形
    为两条折线添加图例:
        import matplotlib.plot as plt 
        x_data = ['2018','2019']
        y_data = [200000,3000000]
        y_data1 = [30000,500000]
        ln1 = plt.plot(x_data,y_data,color='red',linewidth=2.0, linestyle='--')
        ln2 = plt.plot(x_data,y_data1,color='gary',linewidth=3.0,linestyle='-.')
        plt.legend(handler=[ln2,ln1],labels=['Python','PHP'],loc='lower right')
        plt.show()
loc参数指定图例的添加位置,参数支持的参数值:
    'base':     自动选择最佳位置
    'upper right': 将图例放在右上角
    'upper left':    左上角
    'lower left':    左下角
    'lower right':    右下角
    'right':    右边
    'center left':    左边剧中    
    'center right':    右边剧中
    'lower center':    底部剧中
    'upper center': 顶部剧中
    'center':    将图例放在中心
    在程序中修改 Matplotlib 的默认字体,步骤:
        1. 使用 matplotlib.fnot_manager 子模块下的FontProperties 类加载中文字体
        2. 在调用legend 函数时通过 prop 属性指定中文字体
        improt matplotlin.font_manager as fm
        my_font = fm.FontProperties(fname='/usr/font/powerline.ttf')
        plt.legend(handles=[ln2,ln1], labels=['你好','Python基础'],loc='lower right',prop=my_font)
        使用 legend 函数时可不指定handles参数,只传入labels参数
            plt.legend(labels=['Python 中文','PHP基础'],loc='center',proc=my_font)
        Matplotlib 可在调用 plot 函数时为每条折线分别传入label参数,
        import matplotlib.pyplot as plt
        x = ['2018','2019']
        y1 = [1233,312321]
        y2 = [123213214,32131]
        plt.plot(x,y1,color='red',linewidth=2.0,linestyle='-.',label='Python你好')
        plt.plot(x,y2,color='blue',linewidth=3.0,linestyle='--',lebale='PHP再见')
        import matplotlib.font_manager as fm
        my_font = fm.FontProperties(fname='font dir')
        plt.legend(loc='best')
        plt.show()

Matplotlib time, xlabel, ylabel , xticks, yticks : 设置坐标轴

调用 xlabel 、 ylabel 函数设置x轴、y轴,通过title 函数设置整个数据图的标题,调用xticks 、yticks 改变x轴、y轴的刻度值
    为数据图添加名称、标题、坐标轴刻度值
    import matplotlib.pyplot as plt
    x = ['2018','2019']
    y1 = [111111,2222]
    y2 = [1233213,3213213213]
    plt.plot(x,y1,color='red',linewidth=2.0,linestyle='--',label='Hi PYthon你好')
    plt.plot(x,y2,color='blue',linewidth=3.0,linestyle='-.',label='PHP 再见')
    import matplotlib.font_manager as fm
    font = fm.FontProperties(fname='font dir .ttf')
    plt.legend(loc='best')
    plt.xlabel('年份')
    plt.ylabel('教程销量')
    plt.title('Python 开发指南')
    plt.yticks([1000,100000,1000000],[r'挺好',r'优秀',r'火爆')
    plt.show()
    如要对x轴、y轴进行更细致的控制,可调用 gca 函数来获取坐标轴信息对象,对坐标轴进行控制
        对坐标轴的详细控制:
            import matplotlib.pyplot as plt
            x = ...
            plt.yticks([1000,100000,10000],[r'justsoso',r'good',r'great'])
            ax = plt.gca()
            ax,xaxis.set_ticks_position('bottom')
            ax.yaxis.set_ticks_position('left')
            ax.spines['right'].set_color('none')
            ax.spines['top'].set_color('none')
            ax.spines['buttom'].set_position(('data',8000))
            plt.show()

    Matplotlin subplot : 创建子图
        subplot(nrows,ncols,index,**kwargs) : nrows: 指定将数据区域分为多少行,ncols :将数据图区域分为多少列,index 指定获取第几个区域。
        subplot 支持直接传入一个三位数的参数, 第一位数为 nrows 参数,第二位数为 ncols,第三位数为 index 参数
            import matplotlib.pyplot as plt
            import numpy as np
            plt.figure()
            [More](http://c.biancheng.net/view/2711.html)    

    Matplotlib pie : 绘制饼图
        [More](http://c.biancheng.net/view/2713.html)

    Matplotlib 绘制柱状图 bar 、barh 函数
        [More](http://c.biancheng.net/view/2716.html)

    Matplotlib scatter : 绘制散点图
        [More](http://c.biancheng.net/view/2718.html)    

    Matplotlib contour 、 contourf : 绘制等高线
        [More](http://c.biancheng.net/view/2718.html)





    plot_surface(Axes3D): 绘制3D图形
            X、Y数据决定坐标点, Z轴数据决定X、Y坐标点对应的高度
            [More](http://c.biancheng.net/view/2720.html)

Pygal 模块安装和使用
    数据图库,以面向对象的方式创建各种数据图
    Pygal 模块安装:
        1. pip install pygal
        2. python -m pip install pygal
    查看文档:
        python -m pydoc -p 9990

    Pygal 数据图入门
        生成数据图的步骤:
            1. 创建Pygal 数据图对象,不同的数据图提供不同的类,如:柱状图使用pygal.Bar 类, 饼状图:pygal.Pie 类, 折线图:pygal.Line类,等等
            2. 调用数据图对象的add 方法添加数据
            3. 调用Config 对象的属性配置数据图
            4. 调用数据图对象的render_to_xxx 方法将数据图渲染到指定的输出节点

            import matplotlib.pyplot as plt
            import numpy as np
            from mpl_toolkits.mplot3d import Axes3D
            fig = plt.figure(figsize=(12, 8))
            ax = Axes3D(fig)
            delta = 0.125
            // 生成代表X轴数据的列表
            x = np.arange(-3.0, 3.0, delta)
            生成代表Y轴数据的列表
            y = np.arange(-2.0, 2.0, delta)
            对x、y数据执行网格化
            X, Y = np.meshgrid(x, y)
            Z1 = np.exp(-X**2 - Y**2)
            Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
            计算Z轴数据(高度数据)
            Z = (Z1 - Z2) * 2
            绘制3D图形
            ax.plot_surface(X, Y, Z,
                        rstride=1,  # rstride(row)指定行的跨度
                            cstride=1,  # cstride(column)指定列的跨度
                                cmap=plt.get_cmap('rainbow'))  # 设置颜色映射
            设置Z轴范围
            ax.set_zlim(-2, 2)
            设置标题
            plt.title("3D图")
            plt.show()"")''))

Pygal 模块安装和使用
        以面向对象的方式创建各种数据图
    Pygal 模块安装
        1. pip install pygal
        2. python -m pip install pygal
        python -m pydoc -p 9999
    Pygal 数据图
        使用面向对象的方式生成数据图的步骤:
            1. 创建pygal数据图对象,pygal 为不同的图像提供不同的类, 柱状图:pygal.Bar 。 饼状图:pygal.Pie类, 折线图: pygal.Line类
            2. 调用数据图对象的add 方法添加数据
            3. 调用 Config 对象的属性配置数据图
            4. 调用数据对象的render_to_xxx 方法将数据渲染到指定的输出节点
                import pygal
                x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
                y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
                y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700]
                bar = pygal.Bar()
                bar.add('C语言基础', y_data)
                bar.add('Python语言基础', y_data2)
                bar.x_labels = x_data
                bar.title = '编程教程的历年销量'
                bar.x_title = '年份'
                bar.y_title = '销量'
                bar.render_to_file('fk_books.svg')'')'''''''')'')'''''''''''''']
                虽然没有X、Y轴没有名称,都可通过pygal.Bar 对象来配置,为pygal.Bar对象的title、x_labels、x_title、y_title属性赋值
                [More](http://c.biancheng.net/view/2721.html)

    Pygal 常见数据图:折线图、柱状图、饼图、点图、仪表图、雷达图等
        折线图:pygal.Line 类
            [More](http://c.biancheng.net/view/2731.html)

Python读取csv文件
    csv: 以文本存储的表格数据,每行代表一行数据,每行数据中每个单元格内的数据以逗号隔开
        使用csv模块读取csv文件:
            1. 创建csv模块的读取器
            2. 循环调用csv读取器的next方法逐行读取csv文件内容,next返回list列表代表一行数据,list列表的每个元素代表一个单元格数据
            import csv
            filename = 'xxx.csv'
            with open(filename) as f:
                reader = csv.reader(f)
                header_row = next(reader)
                print(hreader_row)
                first_row next(reader)
                print(first_row)
            [More](http://c.biancheng.net/view/2734.html)

Python 读取 JSON 文件
    JSON格式的数据会被转换为Python的list列表或dict字典
        import json
        filename = 'xxx.json'
        with open(filename) as f:
            xx_list = json.load(f)
        for xx_dict in xx_list:
            if xx_dict['Year'] == 2016 and xx_dict['Country Code'] == 'CHN':
                print(xx_dict['Country Name'],xx_dict['Value'])
        调用json的load函数加载JSON数据,返回一个list列表,遍历该list列表即可访问到制定年份、国家的值

Python 读取网络数据 request库和re模块
    网络支持库:urllib.通过该库下的request模块向远程发送HTTP请求,获取服务器响应,思路:使用urllib.request 向 网站发送请求,获取该网站的响应,然后使用python的re模块来解析服务器响应,从中获得数据
        import re 
        from datatime import datetime
        from datatime import timedelta
        from matplotlib import pyplot as plt
        from urllib.request import * 
        def get_html(city,year,month):
            url = 'http://lishi.tianqi.com/' + 'city' + '/' + str(year) + str(month) + '.html'
            request = Request(url)
            request.add_header('User-Agent','xx')
            response = urlopen(request)
            return response.read().decode('gbk')

        dates,highs,lows = [], [], []
        city = 'guangzhou'
        year = '2019'
        months = ['01','02',...,'12']
        prev_day = datetime(2019,2,14)
        for month in months:
            html = get_html(city,year,month)
            text = ''.join(html.split())
            pattern = re,compile('<divclass="tqtong"')
            ...
1
2
3
@Author : Hale Lv
@Created Time : 2019-08-29 13:27:28
@Description :

Python Scrapy 爬虫框架

网络爬虫:自动获取多个页面中的所有天气信息,使用正则表达式、XPath 来提取页面中所有的链接 <a.../>元素,顺着这些链接递归打来对应的页面,然后提取页面中的信息
    网路爬虫具体的核心工作:
        1. 通过网络向指定的URL发送请求,获取服务器响应内容
        2. 使用如正则表达式、XPath等提取页面中需要的信息
        3. 高效地识别响应页面中的链接信息,顺着这些链接递归执行第1、2、3步
        4. 使用多线程有效地管理网络通信交互
    网络爬虫的核心工作:
        1. 向URL发送请求,获取服务器响应内容,此核心工作是所有网络爬虫都需要做的通用工作,通用工作由爬虫框架来实现,可提供更稳定的性能.提高开发效率
        2. 提取页面中感兴趣的信息.使用XPath提取信息的效率更高,正则表达式效率比较低
        3. 识别响应页面中的链接信心,使用XPath效率高,正则表达式则底
        4. 多线程管理: 核心工作是通用,由框架完成

    Scrapy 是一个专业,高效的爬虫框架.试用专业的Twisted包,基于事件驱动的网络引擎包,使用lxml专业的XML处理包,cssselect高效地提取HTML页面的有效信息,同时也提供了有效的线程管理

Scrapy 安装
    pip install scrapy
    python -m pip install scrapy 
    Scrapy需要依赖的第三方包:
        1. pyOpenSSL: 用于支持SSL:Security Socket Layer 
        2. cryptography: 用于加密的包
        3. CFFI :调用C的接口库
        4. zope.interface : 为Python缺少接口而提供扩展的库
        5. lxml :一个处理XML、HTML文档的库,比python内置的xml模块更好用
        6. cssselect : 处理css选择器的扩展包
        7. Twisted : 为python提供的基于事件驱动的网络引擎包
        pip install Twisted-xxx-xxx-xxx.whl

Scrapy 项目创建
    创建一个名为 xxxSplider的项目:
        scrapy startproject xxxSplider
        scrapy 是Scrapy框架提供的命令; startproject 是scrapy 的子命令,用于创建项目; xxxSplider是要创建的项目名
            scrapy 提供的子命令: 
                startproject: 创建项目
                fetch :从指定URL获取响应
                gensplider :生成蜘蛛
                shell :启动交互式控制台
                version : 查看Scrapy版本
        项目目录和文件:
            scrapy.cfg: 项目的总配置文件,无需修改
            xxxSplider :项目的Python模块,程序将从此处导入Python代码
            xxxSplider/items.py :用于定义项目用到的Item类,Item是一个DTO数据传输对象,定义N个属性,该类需由开发者来定义
            xxxSplider/pipelines.py :项目的管道文件,负责处理爬取到的信息
            xxxSplider/settings.py : 项目的配置文件
            xxxSplider/spiders : 存放项目所需的蜘蛛,负责抓取项目感兴趣的信息
    Scrapy包含的核心组件:
        调度器: 由Scrapy框架实现,负责调度下载中间件从网络上下载资源
        下载器: 由Scrapy框架实现,负责从网络上下载数据,下载得到的数据会由Scrapy引擎自动交给蜘蛛
        蜘蛛:由开发者实现,负责从下载数据中提取有效信息,提取到的信息会由Scrapy引擎以Item对象的形式转交给Pipeline
        Pipeline:该组件由开发者实现,该组件接收到Item对象,包含蜘蛛提取的信息后,可将这些信息写入文件或数据库中

    Scrapy Shell 调式工具及用法
        使用shell调式工具抓取页面中的信息:
            scrapy shell https://wwww.zhihu.com/xxx/xxx/
        让Scrapy伪装为浏览器,需在发送请求时设置 User-Agent头
            scrapy shell -s USER_AGENT='Mozilla/5.0/xxx/xxx' 
            https://www.zhipin.com/xxx/xxx
        使用XPath 或 CSS 选择器提取感兴趣的信息
        XPath简化写法:
            nodename            匹配此节点的所有内容
            /                    匹配根字节
            //                    匹配任意位置的节点
            .                    匹配当前节点
            ..                    匹配父节点
            @                    匹配属性
        使用//div匹配页面中任意位置处的<div.../>元素,也可使用//div/span匹配页面中任意位置处的 <div...>元素内的<span.../>子元素
        XPath支持谓词,在节点后增加一个方括号,在方括号内放一个限制表达式对该节点进行限制
        使用//div[@class]来匹配页面中任意位置处、由class属性的 <div.../>元素,也可使用 //div/span[1]匹配页面中任意位置处的 <div.../>元素内的最后一个 <span.../>子元素
        使用 //div/span/[last()]来匹配页面中任意位置处的 <div.../>元素内的最后一个 <span.../>子元素
        使用 //div/span[last()-1] 匹配页面中任意位置处的 <div.../>元素内的倒数第二个 <span.../>子元素 
        XPath :
            //div[@class="job-primary"]
        extract() 提取节点的内容
        [More](http://c.biancheng.net/view/2750.html)

Scrapy 爬虫项目开发过程详解
    步骤:
        1. 定义 Item 类,该类仅用于定义项目需要爬虫的N个属性,如:名称、工资、公司等信息,可在items.py 中增加如下类定义:
            import scrapy
            class xxSpliderItem(scrapy.Item):
                title = scrapy.Field()
                salary = scrapy.Field()
                company = scrapy.Field()
                url = scrapy.Field()
                addr = scrapy.Field()
                industry = scrapy.Field()
                publish = scrapy.Field()
        2. 编写Spider类,将该Spider 类文件放在spiders目录下, 需要使用XPath或CSS选择器来提取HTML中感兴趣的信息
            创建Splider:
                scrapy genspider [options] <name> <domain>
            在命令行中进入xxxSpider 目录下,执行如下命令行创建一个Spider:
                scrapy genspider job_position "xxx.com"
            可在xxSpider项目的xxxSpider/spider 目录下找到一个job_position.py,包含的内容如下:
                import scrapy 
                class JobPositionSpider(scrapy.Spider):
                    name = 'job_position'
                    allowd_domains = ['zhipin.com']
                    start_urls = ['https://zhipin.com/xxx/xx/']
                def parse(self,response):
                    pass
                是Spider类的模板,该类的name属性用于指定该Spider的名字,allow_domains用于限制该Spider所爬取的域名,start_urls 指定该Spider会自动爬取的页面URl
                Spider 需继承scrapy,Spider,并重写parse(slef,response)方法, 注意字符集问题
                开发者要做的两件事情:
                    1. 将要爬取的各页面URL定义在start_urls列表中
                    2. 在parse(self,response)方法中通过XPath或CSS选择器提取项目感兴趣的信息
                    import scrapy

        3. 编写pipelines.py 文件,该文件负责将所爬取的数据写入文件或数据库中
            [More](http://c.biancheng.net/view/2753.html)

scrapy 爬虫数据保存到MySQL数据库
    将爬虫的信息写入到文件中之外,也可通过修改Pipeline文件将数据库存到数据库中
    创建数据库:
    CREATE TABLE job_inf (
        id int(11) not null primary key auto_increment,
        title varchar(100),
        salary varchar(100)
        url varchar(100),
        ...
    )
    修改Pipeline文件,将爬取到的信息保存到MySQL数据库中:
        improt mysql.connector
        class xxxPipeline(object):
            def __init__(self):
                self.conn = mysql.connector.connect(user='root',password='pass',host='localhost',port='3306',database='dbname',use_unicode=True)
                self.cur = self.conn.cursor()
            def close_spider(self,spider):
                print('---------close mysql----')
                self.cur.close()
                slef.conn.close()
            def process_item(self,item,spider):
                self.cur.execute('insert into job_inf values(null,%s,%s,%s,%s,...%s)',(item['title'],item['salary'],item['company'],...item['xxx']))
                self.conn.commit()
        程序为该Pipeline类定义了构造器,用于初始化数据库链接、游标,还为该Pipeline类重写了close_spider方法, 负责关闭构造器中初始化的数据库资源

Scrapy 突破反爬虫机制

本文标题:Python

文章作者:

Hale Lv

发布时间:2019年08月09日 - 18:08

最后更新:2019年08月29日 - 21:08

原始链接:http://yoursite.com/2019/08/09/Python-Note/

许可协议: 转载请保留原文链接及作者。

<center> Hale Lv </center> wechat
扫一扫,公众号!
文能提笔安天下,武能上马定乾坤!
-------------本文结束感谢您的阅读-------------