python 笔记

python

基础概念

一、程序的格式框架

代码高亮: 编程的色彩辅助体系,不是语法要求。

缩进: 一行代码开始前的空白区域,表达程序的格式框架

  • 分类:单层缩进、多层缩进

  • 特点:严格明确:缩进是语法的一部分,缩进不正确程序运行错误

    ​ 所属关系:表达代码间包含和层次关系的唯一手段。

    ​ 长度一致:程序内一致即可,一般用四个空格或者一个TAB

注释: 用于提高代码的可读性的辅助文字,不被执行

  • 单行注释:以#开头
  • 多行注释:以’''开头和结尾

二、命名与保留字

变量: 程序中用于保存和表示数据的占位符号

  • 变量采用标识符来表示,关联标识符的过程叫命名
  • 可以使用符号”=“向变量赋值或修改值,”=“被称为赋值符号

命名: 关联标识符的过程

  • 命名规则:大小写字母、数字、下划线和中文等字符及组合
  • 注意事项:大小写敏感,首字符不能是数字,不与保留字相同

保留字: 被编程语言内部定义并保留使用的标识符

  • python语言有35个保留字(也叫关键字)

  • 保留字是编程语言的基本单词,大小写敏感

    andelifimportraiseglobal
    aselseinraisenonlocal
    assertexceptisreturnTrue
    breakfinallylambdatryFalse
    classfornotwhileNone
    continuefromorwithasync
    defifpassyieldawait
  • 保留字含义

    import、from:用于导入模块(第三方库)

    in:判断变量是否存在序列中

    not:表示“不是”,可用于逻辑判断非操作,表达式运算符

    and:表达式运算,逻辑与操作

    if、elif、else:分支语句

    while:用于循环

    def:定义函数或方法

    lambda:生成简写函数的lambda表达式

    as:名称转换

    is:表示“是”,用于表达式操作

    or:表示“或”,用于逻辑或和表达式运算

    for:用于循环

    try、except、finally:用于异常处理

    with:用于上下文管理

    assert:表示断言,用于判断一个变量或一个表达式的值是否为真

    break:表示中断

    class:用于定义类

    continue:用于执行下一次循环

    del:用于删除变量或序列的值

    return:用于函数返回结果

    yield:用于函数依次返回值

    raise:用于抛出异常

    nonlocal:用于函数嵌套定义时外层数据在内层的表示

    global:表示全局变量

    None:表示“空”

    True:表示“真”

    False:表示"假"

三、库的引用和常用函数

库名引用

  • inport <库名>

    <库名>.<函数名>(函数参数)

  • from <库名> import <函数名>/from <库名> import *

    <函数名>(函数参数)

  • import <库名> as <库别名>

    <库别名>.<函数名>(参数名)

输入函数 input()

  • 格式: <变量>=input(提示信息字符串)
  • 用户输入的信息以字符串类型保存在<变量>中

输出函数 print()

  • 格式:print(拟输出字符串或字符串变量)
  • 字符串类型的一对引号仅在程序内部使用,输出无引号

评估函数 eval()

  • 去掉参数最外侧引号并执行余下语句的函数
  • 格式: eval(字符串或字符串变量)

pass 关键字

​ pass 无实际意义,只是单纯的用来占位,保证语句的完整性使程序不报错

​ 例如

if age > 18 :
    pass     #占位,无意义,使程序完整不报错
print("hello")

基本数据类型及其用法

一、数字类型及关系

1、整数类型
  • 与数学中整数的概念一致

  • 可正可负,没有取值范围的限制

  • pow(x,y)函数:计算X^y

  • 四种进制表示形式

    ​ 十进制:1010、99、-217

    ​ 二进制:以0b或0B开头:0b010、0B101

    ​ 八进制:以0o或0O开头:0o123、0O456

    ​ 十六进制:以0x或0X开头:0x9a、0X89

2、浮点数类型
  • 与数学中概念一致

  • 带有小数点及小数的数字

  • 浮点数取值范围和小数精度都存在限制,但常规计算可忽略

  • 取值范围数量级约 − 1 0 3 07 至 1 0 3 08 -10^307 至 10^308 1030710308,精度数量级 1 0 − 16 10^-16 1016

  • 浮点数间运算符存在不确定尾数,不是bug

  • round()函数

    ​ round(x,d):对x四舍五入,d是小数截取位数,不确定位数发生在 1 0 − 16 10^-16 1016 左右,round十分有效

  • 浮点数科学计数法表示

    ​ 使用e或E作为幂的符号,以10位基数,格式如下

 ​	<a> e <b>    #表示a*$10^b $
3、复数类型
  • 与数学中复数的概念一致

  • 定义j= − 1 \sqrt{-1} 1 ,以此为基础,构建数学体系

  • a+bj被称为复数,其中,a是实部,b是虚部

    例如

    ​ z=1.23 e − 4 e^-4 e4+5.6e+89i

  • real获得实部,imag获得虚部

4、数字类型的关系

​ 数字类型间可进行混合运算,生成结果为“最宽”类型,三种类型存在一种逐渐“扩展”或“变宽”的关系:

	整数 ——> 浮点数 ——> 复数               例如:整数 + 浮点数 = 浮点数

二、数值运算操作符与运算函数

1、操作符概念
  • 操作符是完成运算的一种符号体系
2、操作符

一元操作符

  • +、-、*(乘)、/(除)
  • // #整数除,10//3整数商为3
  • +x #x本身 -x #x的负值
  • % #取余数,模运算。10%3 结果为1
  • x**y #幂运算,即 x y x^y xy,当y大于零小于一是就是根式

二元操作符有对应的增强赋值操作符

x op = y   #即 x = x op y ,其中op为二元操作符
例 
	x += y 等价于 x = x+y 
    x**=3  等价于 x = x**3
3、数值运算函数
  • abs(x) #绝对值,x的绝对值

  • divmod(x,y) #商余,(x//y,x%y),同时输出商和余数

  • pow(x,y[,z]) #幂余,(x**y)%z,[····]表示参数z可省略

  • round(x[,d]) #四舍五入,d是保留小数位数,默认值为0

  • max( x 1 x_1 x1, x 2 x_2 x2,···, x n x_n xn) #最大值,返回 x 1 x_1 x1, x 2 x_2 x2,···, x n x_n xn中最大值,n不限

  • min( x 1 x_1 x1, x 2 x_2 x2,···, x n x_n xn) #最小值,返回 x 1 x_1 x1, x 2 x_2 x2,···, x n x_n xn中最小值,n不限

  • int(x) #将x别为整数舍去小数部分

  • float(x) #将x变为浮点数,增加小数部分

  • complex(x) #将x变成复数,增加虚数部分

三、字符串

1、字符串类型及操作
  • 由0个或多个字符组成的有序字符类型

    • 用一对单引号或一对双引号表示
    • 字符串是字符的有序序列,可以对其中字符进行索引
  • 字符串序号

    		<----------- 反向递减序号
        -11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1
         请  输  入  带 有 符 号  的 温  度 值
         0,  1,  2, 3, 4, 5, 6, 7, 8, 9, 10
             ----------> 正向递增序号
    
  • 字符串的使用

    • 索引:返回字符串中单个字符 <字符串>[M],其中M表序号

    • 切片:返回字符串一段字符子串 <字符串>[M:N],左开右闭

      字符串切片的高级用法:

    • <字符串>[M,N],M缺失表示至开头,N缺失表示至结尾

    • <字符串>[M:N:K],根据步长K对字符串切片

  • 字符串的特殊字符

    转义符 \

    • 转义字符表达特定字符的本意

      例如:"双引号(\")"  结果为 双引号(")
      
    • 转义符形成一些组合,表达一些不可打印的含义

      "\b" 回退		"\n" 换行,光标移动到下行首		"\r" 回车,光标移动到本行首
      
    • r 方法原样输出

      输入: str=""" ab             输出:  ab
                   cd                    cd
                   ef                    ef
                """
      
2、字符串操作符
	x + y				#连接两个字符串x和y
    n*X 或 x*n		   #复制n次字符串x
    x in s              #如果x是s的子串,返回True,否则返回False
                         注意:not inin 相反
3、字符串处理函数
	len(x)				#长度,返回字符串x的长度
    str(x)				#任意类型所对应的字符串形式
    hex(x)oct(x)	   #整数x的十六进制或八进制小写形式字符串
    chr(x)				#x为Unicode编码,返回其对应的字符
    ord(x)				#x为字符,返回其对应的Unicode编码

unicode编码

  • 统一字符编码,即覆盖几乎所有字符的编码方式(包括汉字)
  • 从0到1114111(0x10FFFF)空间,每一个编码对应一个字符
  • python字符串中每一个字符都是Unicode编码字符
4、字符串处理方法
	str.lower()str.upper()	#返回字符串的副本,全部字符小写/大写
    str.split(sep=None) 		 #返回一个列表,由str根据sep被分隔的部分组成
    						       例:"A,B,C",split(",")=>['A','B','C']
    str.count(sub)				 #返回子串sub在str中出现的次数
    str.replace(old,new)		 #返回字符串str副本,所有old子串被替代为new
    							   例:"python".replace("n","n123.io")=>"python123.io"
    str.center(width[,fillchar]) #字符串str根据宽度width居中,fillchar可选
    							   例:"python".center(20,'=')  结果为:
        							"==========python=========="
    str.strip(char)               #从str中去掉在其左侧和右侧char中列出的字符
    							    例:"=python=".strip("=") => "python"
    str.join(iter)				  #在iter变量除最后元素表外每一个元素后增加一个str
    								例:",".join("12345") => "1,2,3,4,5"
     补充:
    	字符串zfill()方法,在字符串前面补0
        例如:str(<需要转化为字符串的文本>).zfill(4)   #补4个0
5、字符串类型的格式化
  • 用法:<模板字符串>.format(<逗号分隔的参数>)

    例如:
    	"{}":计算机{}的cpu占用率为{}%".format("2018-10-10","c",10)
    序号: 0        1            2             0           1   2
    
    	"{1}":计算机{0}的cpu占用率为{2}%".format("2018-10-10","c",10)
         #字符串槽里{}序号可自定义,匹配format()默认序号。例如:{1}为"c"
    
  • 槽内部格式化的配置方法(可用于print()输出)

    {<参数序号>:<格式控制标记>}
    
    :<填充><对齐><宽度><,><.精度><类型>
    引导符号用于填充< 左对齐槽设定的数字的4浮点数小数整数类型
    的单个字符> 右对齐输出宽度位分隔符精度或字符b,c,d,o,x,X
    ^ 居中对齐串最大输出长度浮点数类型
    e,E,f,%
    例如:
    	>>>"{0:=^20}".format("PYTHON")
        	'==========PYTHON=========='
        >>>"{0:*>20}".format("BIT")
        	'****20个***BIT'
        >>>"{:10}".format("BIT")
        	'BIT     10个空格 '
        >>>"{0:,.2f}".format(12345.6789)
        	'12,345.68'
        >>>"{0:b},{0:c},{0:d},{0:o},{);x},{0:X}".format(425)
        	'110101001,∑,425,651,1ag,1A9'
        >>>"{0:e},{0:E},{0:f},{0:%}".format(3.14)
        	'3.140000e+00,3.140000E+00,3.140000,314.000000%'
    
  • 补充:字符串格式化

    示例:
    	name="小明"
        print("%c"%name 同学) ===> 小明同学
    
    • %c #格式化字符及其ASCLL码
    • %s #格式化字符串
    • %d #整数
    • %u #无符号整数
    • %o #无符号八进制
    • %x #无符号十六进制
    • %X #无符号十六进制(大写)
    • %f #格式化浮点数,可指定精度
    • %e %E #科学计数法格式化浮点数
    • %g #%f + %e
    • %G #%F + %E
    • %p #十六进制格式化变量的地址

程序的控制结构

一、程序的分支结构

1、单分支结构
	if <条件>:
        <语句块>
2、二分支结构
  • 一般形式

    	if <条件>:
            <语句块1>
        else:
            <语句块2>
    
  • 紧凑形式:适用于简单表达式的一分支结构

    	<表达式1> if <条件> else <表达式2>
    	注意:条件正确执行表达式1,错误执行表达式2
    
3、多分支结构
	if <条件1>:
        <语句块1>
    elif <条件2>:                 #注意:多条之间的包含关系
        <语句块2>                        变量取值范围的覆盖
     ········
    else:
        <语句块n>

二、程序的循环结构

1、遍历循环

含义:遍历某个结构形成的循环运动方式

	for <循环变量> in <遍历结构>:
        <语句块>

    注意:
		从遍历结构中遍历逐一提取元素,放在循环变量中
		由保留字 forin 组成,完整遍历所有元素后结束
	 	每次循环,所获得元素放入循环变量,并执行一次语句块
  • 计数循环

    1、循环N次
    	for i in range(N):         #遍历由range()函数产生的数字序列,产生循环
            <语句块>
     注意:
    	循环N次,变量i从0到N-1 
    
    2、循环特定次
    	for i in range(M,N,K):		#range()产生从M到N-1的数列,以k为步长
            <语句块>
    
  • 字符串遍历循环

    	for c in s:					#s是字符串,遍历字符串每一个字符,产生循环
            <语句块>
    
  • 列表遍历循环

    	for item in ls:				#ls是一个列表,遍历其每个元素,产生循环
            <语句块>
    
  • 文件遍历循环

    	for line in fi:             #fi是一个文件标识符,遍历其每行,产生循环
            <语句块>
    
2、无限循环

含义:由条件控制的循环运行方式

	while <条件>:					   #反复执行语句块,直到条件不满足时为止
        <语句块>
    注意:
        当条件设置不当,陷入死循环时。CTRL+C退出执行
3、循环控制保留字
  • break 跳出并结束当前循环,执行循环后面的语句
  • continue 结束当次循环,继续执行后续次数循环
4、循环的高级用法

循环与else

	for <变量> in <遍历结构>:            while <条件>:
        <语句块1>						 	<语句块1>
    else:								else:
        <语句块2>						  	<语句块2>

注意:

  • 当循环未被break语句退出时,执行else语句块
  • else语句块被作为“正常”完成循环的奖励
  • 这里的else语句块的用法异常处理中else用法类似

三、条件判断及组合

1、条件判断
	<			#小于
    <=			#小于等于
    >=			#大于等于
    >			#大于
    ==			#等于。注意:程序中“=”为赋值符号
    !=			#不等于
2、条件组合
	and			#逻辑与
    or			#逻辑或
    not			#逻辑非

四、程序的异常处理

1、异常处理的基本使用
	try:
        <语句块1>		#若语句块1异常则执行语句块2,避免出现异常提示
    except:
        <语句块2>
        
     try:
        <语句块1>		#标注异常处理后,仅响应此类异常。异常类型名字等同于变量
     except<异常类型>:
        <语句块2>
2、异常处理的高级用法
	try:
        <语句块1>
    except:
        <语句块2>		#finally 对应的语句块4一定执行,else对应语句块3在不发生异常时执行
    else:
        <语句块3>
    finally:
        <语句块4>
3、自定义异常
  • 使用raise关键字可以抛出一个异常

    格式:raise <异常类型>		#异常类型为系统内置异常类型。后面可加具体错误与括号中
    
  • 基于面向对象编程自定义

    例:	class <异常类型(自定义名)>(Exception):
        	def __init__(self,····):
                pass
            def __str__(self):
                pass   
    

函数与代码复用

一、函数的定义与使用

1、函数的定义
  • 函数是一段具有特定功能的、可重用的语句组。函数是一段代码的表示

  • 函数是一种功能的抽象、一般函数表达特定功能

  • 两个作用:降低编程难度和代码复用

    格式:
        def <函数名>(<参数,0个或多个>):
            <函数体>
            return <返回值>
    注意:
    	函数定义时,所指定的参数是一种占位符
        函数定义后,如果不经过调用,不会被执行
        函数定义时,参数是输入,函数体是处理,结果是输出。(ipo)
    
2、函数的使用及调用过程
  • 函数的调用

    • 调用是运行函数代码的方式
    • 调用时要给出实际参数,实际参数替换定义中的参数
    • 函数调用后得到返回值
  • 调用过程

    :
        def fact(a):		#定义函数fact,返回值为变量s
            s=1
            ····
            renturn s
        a=fact(10)			#调用函数fact(),给定参数10.并把函数返回值s的值赋给a
        print(a)
    
3、函数的参数传递
  • 参数个数

    ​ 函数可以有参数也可以没有,但必须保留括号

  • 可选参数传递

    ​ 函数定义时可以为某些参数指定默认值,构成可选参数

    格式:
    	def <函数名>(<非可选参数>,<可选参数>):
            <函数体>
            return <返回值>
    
    例:计算n!//m
    	def fact(n,m=1):
            s=1
            for i in range(1,n+1):
                s*=i
            return s//m
    注意:m为可选参数,但调用fact()函数而不给m参数传值时,默认m=1
    
  • 可变参数传递

    ​ 函数定义时可以设计可变数量参数,即不规定参数总数量

    格式:
    	def <函数名>(<参数>,*b):
            <函数体>
            return <返回值>
        
    例:计算n!
    	def fact(n,*b):
            ······
            return s
    调用:
    	fact(10,3)
        fact(10,3,5,8)
    
  • 列表、字典作为参数传递,巧用*号

    参数前面加上*号 ,意味着参数的个数不止一个,另外带一个星号*参数的函数传入的参数存储为一个元组(tuple),带两个*号则是表示字典(dict

    	def t1(param1, *param2):		 def t2(param1, **param2):
            print(param1)			     	 print param1
            print(param2)                    print param2
    	t1(1,2,3,4)                      t2(1,a=2,b=3)
    输出:                            输出:
    	# 1                              # 1
    	# (2,3,4)					     # {a:2, b:3}  
    	 
    
    此外,一个*号还可以解压参数列表:		同时使用***
        def t3(p1, p2):			         def t4(a, b=10, *args, **kwargs):
            print(p1, p2)                	 print(a)
    	args = [1, 2]                        print(b)
    	t3(*args)                            print(args)
    输出                                	    print(kwargs)
    	# 1 2	                         t4(1, 2, 3, 4, e=5, f=6, g=7)
    							     输出:
    									 # 1
    									 # 2
    									 # 3 4
    									 # {'e': 5, 'g': 7, 'f': 6}
    
  • 参数传递的方法

    参数传递方法有两种,位置传递和名称传递。

    例:
    	def fact(n,m=1):
            ······
            return s//m
    调用:
    	fact(10,5)		#位置传递
        fact(m=5,n=10)  #名称传递
    
4、函数的返回值
  • 函数可以返回0个或多个结果,多个结果为元组类型。例:return s//m,n,m
  • return 保留字用来传递返回值
  • 函数可以有返回值也可以没有,即return可有可无
5、局部变量和全局变量
  • 含义:局部变量是指仅在程序局部(例如一个函数内部)起作用,全局变量变量是指在整个程序任何地方都起作用即可以影响全局

    <语句块1>					#定义在语句块1、2中的全局变量可以被函数内部调用
        def <函数名>(<参数>):	   #定义在函数体内的局部变量只能被函数内部调用
            <函数体>
            return <返回值>
        <语句块2>
    
    注意:
    	局部变量是函数内部的占位符,与全局变量可能重名但不同
        函数运算结束后,局部变量被释放
        可以使用global 保留字在函数内部使用全局变量
    
  • 规则1:局部变量和全局变量是不同变量

    例
    	n,s=10,100
        def fact(n):
            s=1					#fact()函数中s是局部变量与全局变量s不同
            for i in range(1,n+1):
                s*=i
                return s		#局部变量s
         print(fact(n),s)		#全局变量s
        
        n,s=10,100
        def fact(n):
            global s			#fact()函数中使用global保留字声明此处是全局变量S
            ······
            return s			#此处s指全局变量
        print(fact(n),s)		#此处全局变量被函数修改
    
  • 规则2:局部变量为组合数据类型且未创建,等同于全局变量

    例
    	ls=["F","f"]		#真实创建全局变量列表
        def func(a):
            ls.append(a)	#此处ls是列表类型且未真实创建(指在函数内部未真实创建),等同于全局变量
            return
        func("c")
        print(ls)			#输出结果:["F","f","c"]
        
        ls=["F","f"]		#真实创建全局变量ls
        def func(a):
            ls=[]
            ls.append(a)	#此处ls为真实创建的列表类型,局部变量
            return 
        func("c")
        print(ls)			#运行结果:["F","f"]
    
6、lambda函数

注意:lambda函数返回函数名作为结果

  • lambda函数是一种匿名函数,即没有名字的函数
  • 使用lambda保留字定义,函数名是返回结果
  • lambda函数用于定义简单的、能够在一行内表示的函数
格式
	<函数名>=lambda<参数>:<表达式>
    等价于
    def <函数名>(<参数>):
        <函数体>
        return <返回值>
例
	f=lambda x,y:x+y
    f(10,15)			#结果为25
    f=lambda : "lambda函数"
    print(f())			#结果为:lambda函数
注意:
	lambda函数有一些固定的使用方法,建议逐步掌握
    lambda函数主要用作一些特定函数的或方法
    一般情况下,建议用def定义普通函数
7、filter
  • pyhon2内置类,python3改为内置函类

  • 作用:对可迭代对象进行过滤,得到一个filter对象(可迭代)

  • 用法:filter可以给定两个参数,第一个参数是函数,第二个参数是可迭代对象

    例:
    	age=[12,23,30,17,16,22,19]
        x=filter(lambda ele:ele>18,ages)
        print(x)	==>	<filter object at 0x000002670373E908>
        for a in x:
            print(a)	==>	23 30 22 19
        adult=list(x)
        print(adult)	==> [23,30,22,19]
    
8、map
  • 内置类

  • 作用:对可迭代对象中的每个元素,进行函数(参数)操作。与filter类似

    例:
    	ages=[12,23,30,17,16,22,19]
        m=map(lambda ele:ele+2,agee)
        print(list(m))	==>	[14,25,32,19,18,24,21]  #m为可迭代对象
    
9、reduce
  • 内置函数,from functools import reduce

    例:
    	def foo(x,y):				#对列表中的数进行累加
            return x+y				#第一次:x=100.y=89
        scores=[1000,89,76,87]		#第二次:x=189,y=76,依照此规律依次累加
        print(reduce(foo,scores))	==> 352(第四次:265+87)
        
        students=[
            {'name':'zhangsan','age':'18':'score':'98'},
            {'name':'lisi','age':'21':'score':'97'},
            {'name':'jack','age':'22':'score':'100'}
        ]
        def bar(x,y):
            return X+y['age']
        print(reduce(bar,students,0))	#0为x初始值
    
10、sort方法(排序)的使用
  • sort方法排序

    例:
    	nums=[4,8,2,1,7,6]
        nums.sort()		#列表的sort方法,直接对列表进行排序
        print(nums) ===> [1,2,4,6,8]
    
  • sorted,内置函数,不改变原有数据,而生成一个新的有序的列表

    ints=(5,9,2,3,1,3,8,7,4)
    x=sorted(ints)
    print(ints) ===> (5,9,2,1,3,8,7,4)
    print(x) ====> [1,2,3,4,5,6,7,8,9]
    
  • 注意

    • 字典与字典间不能使用比较运算符
    • 需要传递参数key指定规则
    • key参数类型是函数
例:
	students=[{'name':'zhangsan','age':18,'score':98},
              {'name':'lisi','age':21,'score':97},
              {'name':'jack','age':22,'score':100},
              {'name':'tony','age':20,'score':90}
              ]
    def foo(ele):	#foo函数可无参数,但调用时,在sorta()内部传递了一个参数给foo
        return ele['age']
    students.sort(key=ele)	#在sort内部实现时,调用foo方法,传入一个参数,参数为列表元素
    
    #利用匿名函数,简化上述函数
    students.sort(key=lambda ele:ele['age'])
11、高阶函数

​ 高阶函数有三种形式

  • 一个函数作为另一个函数的参数。例:lambda函数的使用

  • 把一个函数当作另一个函数的返回值

    例:
    def test():					def demo():					def bar():
    	print('我是test函数')     	 print('我是demo函数')	       print('我是bar函数')
        retuen 'hello'				return test					return test()
    	
        x=test;print(x)	==> 我是test函数  hello
        y=demo()	#y是test函数	print(a=bar()) ==> hello
        z=y();print(z) ==> hello
    
  • 在一个函数中定义另一个函数(即函数的嵌套)

    	def outer(x):
        	print('我是outer函数')
        	def inner():			#inner函数是定义在函数内部的函数
            	print('我是inner函数')
        	if x>18:
            	inner()
     	return 'hello'
    	print(12)	==> 我是outer函数
        print(21)	==> 我是outer函数
        				我是inner函数
    
12、闭包
  • 概念:由函数及其相关的引用环境组合而成的实体(闭包=函数块+引用环境)

  • 构成条件:函数嵌套,外部函数返回内部函数。

    ​ 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就认为是闭包

    def outer():
            x=10
            def inner():
                nonlocal x		#nonlocal关键字调用外部函数变量
                y=x+1
                print('inner里的y=',y)
                x=20			#若无nonlocal调用,此处则为创建一个新变量X
        return inner
    	print(outer()()) ==> 11
    
13、装饰器
  • 定义:利用闭包实现功能,使用@(语法糖)符号

    例:计算程序耗时
    	import time 
        def  cal_time(fn):
            def inner():		#可加参数 inner(x)
                start=time.time()
                fn()			#s=fn(x)
                end=time.time()
                print('代码耗时',end - start)
             return inner		#return x
        @cal_time				#调用cal_time,并把被修饰的函数传递给fn
        def demo():				#x传给demo(),即demo(x)。还可写多个参数
            x=0					#再次调用demo()函数时,此时demo()函数不再是原来的demo()
            ·					#而是cal_time中内部函数inner,cal_time中fn()为demo()函数
            ·
            ·
    
  • 装饰器的高级使用

    开发中:开放封闭原则,利用装饰器,在不改变功能模块源码的前提下,扩展功能。

    例:
    	def can_play(fn):
            def inner(x,y,*args,**kwargs):
                if args[0]<=22:
                    f(n)(x,y)
                else:
                    print('太晚了,赶紧睡')
            return inner
        @can_play
        def play_game(name,game):
            print('{}正在玩{}'.format(name,game))
        play_game('张三''王者荣耀'18)
    改进:
    	clock=kwargs['clock']
        if clock<=22:
            ·
            ·
        play_gane('李四''绝地求生',clock=18)		#参数(clock=18)在kwargs保存为键值对 
        #若无clock参数会报错,解决方案如下:
        clock=kwargs.get('clock',23)			 #若无clock为空值,使用默认值(23)
    

二、代码复用于函数递归

1、代码复用与模块化设计
  • 代码复用——把代码当成资源进行抽象

    • 代码资源化:程序代码是一种用来表达计算的“资源”

    • 代码抽象化:使用函数等方法对代码赋予更高级的定义

    • 代码复用:同一份代码在需要时可以被重复使用

      函数和对象是代码复用的两种主要形式

      函数:将代码命名在代码层建立了初步抽象

      对象:属性和方法.()在函数之上再次组织进行抽象

      抽象级别:对象大于函数

  • 模块化设计——分而治之

    • 通过函数或对象封装将程序划分为模块及模块间的表达

    • 具体包括:主程序、子程序和子程序间的关系

    • 分而治之:一种分而治之、分层抽象,体系化的设计思想

紧耦合:两个部分之间交流很多,无法独立存在

松耦合:两个部分之间交流较少,可以独立存在 #模块内部紧耦合,模块间松耦合

2、函数递归的理解
  • 递归的定义——函数定义中调用函数自身的方式

    • 两个关键的特征

      链条:计算过程存在递归链条

      基例:存在一个或多个不需要再次递归的基例

    • 类似数学归纳法

      数学归纳法

      ​ 证明当n取第一个值 n 0 n_0 n0时命题也成立

      ​ 假设当 n k n_k nk时命题成立,证明n=nk+1时命题也成立

      递归是数学归纳法思想的编程体现

  • 函数递归的调用过程

    例:
    n ! = { 1 , n = 0 n ( n − 1 ) ! , o t h e r w i s e n!=\begin{cases} 1,n=0 \\ n(n-1)!, otherwise\end{cases} n!={1n=0n(n1)!otherwise

    	def fact(n):
            if n==0:			#基例
                return 1
            else:
                return n*fact(n-1)		#递归链条
    
    • 递归的实现——函数+分支语句

      注意:递归本身是一个函数,需要函数定义方式描述

      ​ 函数的内部,采用分支语句对输入参数进行判断

      ​ 基例和链条,分别编写对应代码

      过程:

      ​ fact(5) ==>fact(4) ==>fact(3) ==>fact(2) ==>fact(1) ==fact(0)

      ​ 120 <== 24 <== 6 <== 2 <== 1 <== 1

    • 递归实例

      例:字符串反转输出
      	def rvs(s):
              if s=="":
                  return s
              else:
                  return rvs(s[1:])+s[0]
         斐波那契数列——F(n)=F(n+1)+F(n-2)
      	def f(n):
              if n==1 or n==2:
                  return 1
              else:
                  return f(n-1)+f(n-2)
      

三、模块

1、加密模块
  • hashlib

    • 两个重要算法:md5和sha加密

      方法:单向加密:只有加密过程,不能解密 md5/sha

      ​ 对称加密

      ​ 非对称加密:rsa

    • md5加密 #需要将要加密内容转化为二进制

      例:
      	x=hashlib.md5()		#生成一个md5对象
          x.update('abc'.encode('utf8'))   #括号内将'abc'转化为二进制,utf-8编码
          print(x.hexdigest())	#x为md5对象,其后面的方法将其转化为十六进制数字
      
    • sha加密

      例:
      	h1=hashlib.sha1('123456'.encode())
          h2=hashlib.sha224('123456'.encode())  #sha后面的数字表示加密长度,安全性高于md5
          print(h2.hexdigest())  #生成的十六进制数共56位,一位十六进制占224中的4位,
          				 	   #224/56=4,相当于4位二进制
      
  • hmac

    • 特点:hmac加密可以指定秘钥

      :
          h=hmac.new('h'.encode(),'你好'.encode())   #'h'为秘钥
          result=h.hexdigest()
          print(result)    #获得加密的结果
      
2、自定义模块
  • 概述

    • 模块本质是一个py文件

    • 模块命名规则:由数字、字母下划线组成,不能以数字开头

    • 导入一个模块就能使用这个模块的变量和函数

  • 注意

    • 使用:from <模块名> import* 本质上读取模块里的_ all_属性

      例:
      	_all_=['m','test']
          m='yes'
          n=100
      
    • 以一个下划线开始的变量,建议只在本模块里使用,不导入其他模块。强制性禁止使用变量

      例:
      	def _bar():
              pass
          del bar    #删除bar
          del (bar,···,···,)  #可删元组
      
    • _name _

      当直接运行这个py文件(模块)的时候,值是_ main_

      如果这个py文件作为一个模块导入的时候,值是文件名

      例:
      	if _name_=='_main_':
              程序               #只有直接运行这个py文件时才会执行程序
      
3、包的使用
  • 概述

    ​ 包(多个模块集合)是一个可将多个具有相似或都有关联的多个模块放到一个文件夹,这个文件夹即为包

  • 注意

    ​ python包里会有一个_init _.py文件

    例:
    	<>
        	_init_.py
            x.py      ==>  _init_.py中        ==> import <>
            p.py           from . import X         可直接调用x模块
    
4、自定义模块及使用demo

直接调用类里的方法,需要自定义方法别名

	class <类名>:                  例:
        def <方法1>:                  def eat():	#在p类里定义eat方法
            ····
        def <方法2>:                  eat=p.eat   #在类里为eat方法取别名eat
 直接调用:
	_p=<类名>   #创建对象,_表示对象私有化
    <方法1别名>=_p.<方法1>   #给方法1起别名

四、可迭代对象和迭代器

1、可迭代对象
  • 可迭代对象例如:list/tuple/dict/set/str/range/filter/map

  • for ····· in循环的本质是调用了可迭代对象的 _ _ iter _ _ 方法,获取到这个方法得到返回值。这个返回值是一个对象,然后再调用这个对象 _ _ next _ _ 方法

    例:
    	class Foo(object):
            def __next__(self):	#__next__可与iter方法写在同一个类里
                return 1
        
        class Demo(object):
            def __init__(self,x):
                self.x=x
            
            def __iter__(self):
                f=Foo()
                return f
    	
        d=Demo(100)
        for i in d:
            print(x)	#调用next方法,打印1
    
    优化例子:
    	class Demo(object):
            def __init__(self):
                self.x=x
                self.count=0
            
            def __iter__(self,x):
                return self
            
            def __next__(self):
                self.count+=1
                if self.count<=self.x:
                    return 'hello'
                else:
                    raise stopIteration		#迭代器停止
    	
        修改例子
        return 'hello' 改为 return self.count-1
    	for i in Demo(10):
            print(i)
            
     注意:
    	d=Demo(10)
        #运行过程
        i=d.__iter__()
        i.__next__()
        i=iter(d)	#内置函数iter可以获取到一个可迭代对象的迭代器
        第一个 print(next(i))=0,第二个为1
    
2、迭代器
  • 定义:一个实现了iter方法和next方法的对象,就是迭代器

  • demo

    例:斐波那契数列的迭代器实现
    	class Fibonacci(object):
            def __init__(self,n):
            	seelf.n=n
            	self.num1=self.num2=1
            	self.count=0
    		def __iter__(self):
                return self
            def __next__(self):
                self.count+=1
                if self.count<=self.n:
                    x=self.num1
                    self.num1,self.num2=self.m2,self.num1+self.num2
                    return x
                else:
                    raise stopcteration
    

五、生成器

1、定义

​ 生成器本质上是一个特殊的迭代器

2、最简单的生成器(demo)
例:
	nums=[i for i in range(10)]	#列表生成式(推导式)/字典推导式·····
    print(nums)	===> [0,1,2,3,4,5,6,7,8,9]
    
    g=(i for i in range(10))	#g为生成器
    for i in g:
        print(i)	#依次打印结果:===> 0 2 3 4 5 6 7 8 9 
3、生成器

​ 是一个对象,定义class。生成器在写法上像一个函数。

例:
	def my_gen(n):
        i=0
        while i < n:
            yield i		#yield关键字将函数变成生成器
            i+=1		#执行yeild i 后,执行这一步
    G=my_gen(10)
    for i in G:

组合数据类型

一、集合类型及操作

1、集合类型定义
  • 集合类型与数学中的集合概念一致

  • 集合元素之间无序,每个元素唯一,不存在相同元素

  • 集合元素不可更改,不能是可变数据类型

  • 集合用大括号{}表示,元素间用逗号分隔

  • 建立集合类型用{}或set()

  • 建立空集合类型,必须使用set()

    >>>A={"python",123,("python",123)}
    {123,"python",("python",123)}
    >>>B=set("pypy123")
    {'1','p','2','3','y'}
    
2、集合操作符
  • s|t #并,返回一个新集合,包括在集合s和t中的所有元素

  • s-t #差返回一个新集合,包括在集合s但不在集合t中的元素

  • s&t #交,返回一个新集合,包括同时在集合s和t中的元素

  • s^t #补,返回一个新集合,包括集合s和集合t中的非相同元素

  • s<=t或s<t #返回True/False,判断s和t的子集关系

  • s>=t或s>t #返回True/False,判断s和t的包含关系

    4个增强操作符

  • s|=t #并,更新集合s,包括在集合s和t中的所有元素

  • s-=t #差,更新集合s,包括在集合s但不在t中的元素

  • s&=t #交,更新集合s,包括在集合s但不在t中的元素

  • s^=t #补,更新集合s,包括集合s和t中的非相同元素

3、集合处理方法
s.add(x)		#如果x不在集合s中,将x增加到s
s.discard(x)	#移除s中元素x,如果x不在集合s中,不报错
s.remove(x)		#移除s中元素x,如果x不在集合s中,产生keyError
s.clear()		#移除s中所有元素
s.pop()			#随机返回s的一个元素,更新s,若s为空产生keyError异常
s.copy()		#返回集合s的一个副本
len(s)			#返回s的元素个数
x in s			#判断s中元素x,x在集合s中,返回True,否则返回False
x not in s		#判断s中的元素x,x不在集合s中,返回True,否则返回False
set(x)			#将其他类型变量转变为集合类型	
4、应用
  • 数据去重:利用集合类型特性
  • 包含关系比较

二、序列类型及操作

1、序列的含义
  • 序列是具有先后关系的一组元素
  • 序列是一维元素向量,元素类型可以不同
  • 类似数字元素序列:s0,s1,·······,sn-1
  • 元素间由序号引导,通过下标访问序列的特定元素
2、序列是一个基类类型
  • 序列类型:字符串类型,元组类型,列表类型

  • 序列的定义

    	-5      -4   -3     -2    -1   #反向递减序号
        "BIT" 3.1415 1024 (2,3) ["中国"9]
        0       1     2      3     4   #正向递增序号
    
3、序列处理函数的方法
  • 通用操作符

    x in s		#如果x是序列s的元素,返回True,否则返回False
    x not in s	#如果是序列s的元素,返回False,否则返回True
    s+t			#链接两个序列s和t
    s*n或n*s		#将序列s复制n次
    s[i]		#索引,返回s中第i个元素,i是序列的序号
    s[i:j]或s[i:j:k]	#切片,返回序列s中第i列,以k为步长的元素子序列
    
  • 5个函数和方法

    len(s)		#返回序列s的长度,即元素个数
    min(s)		#返回序列s的最小元素,s中元素需要可比较
    max(s)		#返回序列s的最大元素,s中元素需要可比较
    s.index(x)或s.index(x,i,j)	#返回序列s从i开始到位置中第一次出现元素x的位置
    s.count(x)	#返回序列s中出现k的总次数
    

三、元组类型及操作

1、元组类型定义
  • 元组是一种序列类型,一旦创建就不能被修改,是序列类型的一种拓展

  • 使用小括号()或tuple()创建,元素间用逗号分隔

  • 可以使用小括号或不使用小括号

    例:
    	>>>creature='cat','dog','tiger','human'
        >>>creature
        ('cat','dog','tiger','human')
    
2、元组类型操作
  • 元组类型继承了序列类型的全部通用操作
  • 元组因为创建后不能修改,因此无特殊操作 注:与列表函数及方法类似
  • 使用或不使用小括号

四、列表类型及操作

1、定义
  • 列表是一种序列类型,创建后可以随意被修改
  • 使用方括号[]或list()创建,元素间用逗号分隔 注意:方括号[]真正创建一个列表,复制仅传递引用
  • 列表中元素类型可以不同,无长度限制
2、列表类型操作函数及方法
ls[i]=x			#替换列表ls第i元素为x
ls[i:j:k]=lt	#用列表lt替换ls切片后所对应元素子列表
del ls[i]		#删除列表ls中第i元素
del [i:j:k]		#删除列表ls中第i到第j以k为步长的元素
ls+=lt			#更新列表ls,将列表lt元素增加到列表ls中
ls*=n			#更新列表ls,其元素重复n次
len(ls)			#列表长度
ls.append(x)	#在列表ls最后增加一个元素x
ls.clear		#删除列表ls中所有元素
ls.copy()		#生成一个新列表,赋值ls中所有元素
ls.insert(i,x)	#在列表ls的第i位置增加元素x
ls.pop(i)		#将列表第i为位元素取出并删除该元素
ls.remove(x)	#将列表ls中出现的第一个元素x删除
ls.reverse()	#将列表ls中的元素反转
ls.count()		#统计列表ls中出现同一元素出现次数
ls.sort()		#对列表排序
ls.index()		#从列表中找到第一个匹配项索引位置
list(seq)		#强制转换元组seq为列表类型
max()/main()	#返回最大值/最小值
cmp(list1,list2)#python2中比较两个列表得到元素
3、列表浅拷贝(复制)
  • 调用copy方法(列表自带),可以复制一个列表内容,但新列表与原列表指向不同内存空间

    例:
    	x=[100,200,300]
        y=x		#等号是内存地址的赋值。x、y指向同一个内存空间,会相互影响
        z=x.copy()
        x[0]-=1
        print(y) ===> [99,200,300]
        print(z) ===> [100,200,300]
    
  • 使用copy模块

    例:
    	import copy
        a=copy.copy(x)	#等价于 a=x.copy()
    
4、列表深拷贝
例:
	words=['hello','good',[100,200,300],'yes','NO']
    
    words1=words.copy()		#1、浅拷贝,可以认为只拷贝了一层(一维)
    words=[2][0]=1
    print(words1) ==> ['hello','good',[1,200,300],'yes','NO']
    
    word2=copy.deepcopy(words)
    words=[2][0]=1
    print(word2) ==> ['hello','good',[100,200,300],'yes','NO']

五、序列类型运用场景

1、数据表示:元组和列表
  • 元组用于元素不改变的应用场景,更多用于固定搭配场景
  • 列表更加灵活,它是最常用的序列类型
  • 最主要作用:表示一组有序数据,进而操作他们
2、元素遍历
for item in ls:				for item in tp:
    <语句块>				      <语句块>
3、数据保护
  • 如果不希望数据被程序改变,转换成元组类型

    例:
    	>>>ls=['a','b','c']
        >>>lt=tuple(ls)
        >>>lt
        ('a','b','c')
    

六、字典类型及操作

1、字典类型的定义(映射)
  • 映射是一种键(索引)和值(数据)的对应

    ​ 例:内部颜色为蓝色,外部颜色为红色

    ​ 映射关系: (键) ( 值)

    ​ 内部颜色:蓝色

    ​ 外部颜色:红色

  • 序列类型由0···n整数作为数据的默认索引

    ​ 映射类型则由用户为数据定义索引

  • 字典是“映射”的体现:

    • 键值对:键是数据索引的拓展
    • 字典是键值对的集合,键值对之间无序
    • 采用大括号{}或者dict()创建,键值对用冒号表示

​ 例:

​ {<键1>:<值1>,<键2>:<值2>,<键3>:<值3>,········,<键n>:<值n>}

2、字典类型的用法

​ 在字典变量中,通过键获得值

例:
	<字典变量>={<1>:<1>,<2>:<2>,·····,<键n>:<值n>}
    <>=<字典变量>[<>]		<字典变量>[<>]=<>
    #[]用来向字典变量中索引或增加元素
例:
	>>>d={'中国':'北京','美国':'华盛顿'}
    >>>d['中国']
    '北京'
    >>>de={};type(de)	#type(x),返回x的类型
    <class 'dict'>
3、字典处理函数及方法
del d[k]		#删除字典d中键k对应的数据值
k in d			#判断键k是否在字典d中,如果在返回True,否则返回False
d.keys()		#返回字典d中所有的键信息
d.values()		#返回字典d中所有的值信息
d.items()		#返回字典中所有的键值对信息
例:
	>>>d={'中国':'北京','美国':'华盛顿'}
    >>>'中国' in d
    True
    >>>d.values()
    dict_values ['北京','华盛顿']
4、字典类型操作函数和方法
d.get(k,<default>)		#键k存在,则返回相应值,不存在则返回<default>值
d.pop(k,<default>)		#键k存在,则取出相应值,不存在则返回<default>值
d.popitem()				#随机从字典中取出一个键值对,以元组形式返回
len(d)					#返回字典d中元素的个数
d.clear()				#删除所有的键值对
例:
	>>>d={'中国':'北京','美国','华盛顿'}
    >>>d.get('中国','伊斯兰堡')
    '北京'
    >>>d.get('巴基斯坦','伊斯兰堡')
    '伊斯兰堡'
    >>>d.popitem()
    ('中国','北京')
5、元组类型应用场景
  • 映射无处不在,键值对无处不在

  • 例如:统计数据出现的次数,数据是键,次数是值

  • 最主要的作用:表达键值对数据,进而操作他们

    例:
    	for k in d:
            <语句块>
    

文件和数据格式化

一、文件的使用

1、文件定义
  • 文件是数据的抽象和集合
  • 文件是存储在辅助存储器上的数据序列
  • 文件是数据存储的一种形式
  • 文件展现形态:文件文本和二进制文件
2、文件的分类
  • 文本文件—文件是数据的抽象和集合

    • 由单一特定编码组成的文件,如utf-8编码

    • 由于存在编码,也被看成是存储着的长字符串

    • 适用于例如:.txt文件、.py文件等

  • 二进制文件

    • 直接由比特0和1组成,没有统一字符编码
    • 一般存在二进制0和1的组织结构,即文件格式
    • 适用于例如:.png文件、.avi文件等
  • 文本文件与二进制文件区别

    • 文本文件和二进制文件是文件的展示方式
    • 本质上所有文件都是二进制形式存储
    例: f.txt 文件保存:"中国是伟大的国家!"
    	#文本的形式打开文件
    	tf=open("f.txt","rt")
    	print(tf.readline())
    	tf.close()
    	输出:中国是伟大的国家
        
     	#二进制形式打开文件
        bf=open("f.txt","rb")
        print(bf.readline())
        tf.close()
        输出:b'\xd6\xd0\xb9\xfa\xca\xc7\xb8·····
    

二、文件的打开关闭与读取

​ 文件的处理步骤:打开=>操作=>关闭

​ 文件的存储状态----(a=open())---->文件占用状态----(a.close())—>文件储存状态

1、文件的打开

​ <变量名>=open(<文件名>,<打开模式>)

​ <变量名> #文件句柄

​ <文件名> #文件路径和名称(源文件同目录可省路径)

​ <打开模式> #读or写, 文本or二进制

例:文件路径
	"D:/PYE/f.txt"		"./PYE/f.txt"
    "D:\\PYE\\f.txt"	"f.txt"
2、打开模式
'r'		#只读模式,默认值,如果文件不存在,返回FileNotFoundError
'w'		#覆盖写模式,文件不存在则创建,存在则完全覆盖
'x'		#创建写模式,文件不存在则创建,存在返回FileExistsError
'a'		#追加写模式,文件不存在则创建,存在则在文件最后追加内容
'b'		#二进制文件模式
't'		#文本文件,默认值
'+'		#与r/w/x/a一同使用,在原功能基础上增加同时读写功能
例:
	f=open("f.txt")		#文件形式,只读模式、默认值
    f=open("f.txt","rt")		#文件形式,只读模式、同默认值
    f=open("f.txt","a+")		#文件形式,追加模式+读文件
    f=open("f.txt","b")			#二进制形式,只读模式
    f=open("f.txt","wb")		#二进制形式,覆盖写模式
3、文件的关闭

​ <变量名>.close() #<变量名>为文件句柄

	file.seek(0,0)		#重置指针,回到开头
    <文件名>.rpartition('.')	#rartition中'r'表示从右边第一个点往右切分
    例:
    sss.txt.rpartition('.') ==> ('sss','.','txt')
4、文件的读取
<f>.read(size=-1)		#读取全部内容,如果给出参数,读入前size长度
例:	>>>s=f.read(2)
	 中国
<f>.readline(size=-1)	#读入一行内容,如果给出参数,读入该行前size长度
例:
	 >>>s=f.readline()
     ['中国是一个伟大的国家']

三、文件的全文本操作

1、遍历全文本:方法一

#一次读入统一处理

	fname=input("请输入要打开的文件名称:")
    fo=open(fname,"r")
    txt=fo.read()
    f.close()
2、遍历全文本:方法二

#按数量读入逐步处理

	fname=input("请输入要打开的文件名称:")
    fo=open(fname,"r")	
    text=fo.read(2)				#对txt进行处理
    while txt !=" ":		#对txt进行处理(改进)
        txt=fo.read(2)
    fo.close				
3、逐行遍历文件:方法一

#一次读入,分行处理

	fname=input("请输入要打开的文件名称:")
    fo=open(fname,"r")
    for line in fo.reaadlines():
        print(line)
    fo.close()
4、逐行遍历文件:方法二

#分行读入,逐行处理

	fname=input("请输入要打开的文件名称:")
    fo=open(fname,'r')
    for line in fo:
        print(line)
    fo.close()

四、数据文件的写入

1、逐’字’写入
	<f>.write(s)		#向文件写入一个字符串或字节流
2、逐’列’写入
	<f>.writelines(lines)		#将一个元素全为字符串的列表写入文件
 例:
	>>>ls=['中国','法国','美国']
    >>>f.writelines(ls)
    中国法国美国
3、指针操作
	<f>.seek(offset)	#改变当前文件操作指针的位置
    #offset含义如下:
     0-文件开头
     1-当前位置
     2-文件结尾
例:
	>>>f.seek(0)	#回到文件头
注意:初始状态,文件指针一般在文件文件结尾

五、with关键字的使用

1、定义

​ with关键字称为上下文管理器,很多需要手动关闭的连接,比如文件连接、socket连接、数据库连接,都可以使用with关键字自动关闭

2、要求

​ with关键字后面对象,需要实现_ _ enter _ _ 和 _ _ exit _ _魔法方法

3、注意

​ 当进入with代码块时,会调用 _ _ enter _ _ 方法,当with代码块执行完后,会自动调用 _ _ exit _ _方法,完成需求

例:
	with open(文件名,'r') as file:
        file.read()

数据的格式化处理

一、数据组织的维度(形式)

  • 一维数据
    • 由对等关系的有序或无序数据构成,采用线性方式组织
    • 对应列表、数组和集合等概念
  • 二维数据
    • 由多个一维数据构成,是一维数据的组合形式
    • 表格是典型的二维数组,其中,表头是二维数据的一部分
  • 多维数据
    • 由一维或二维数据在新维度上拓展形式
  • 高维数据
    • 仅利用最基本的二元关系(键值对)展示数据间的复杂结构

数据的操作周期:存储<=>表示<=>操作

数据存储(存储格式)<----->数据表示(数据类型)<---->数据操作(操作方式)

二、一维数据

1、一维数据的表示
  • 数据有序:使用列表类型
    • 列表类型可以表达一维有序数据
    • for循环可以遍历数据,进而对每个数据进行处理
  • 数据无序:使用集合类型
    • 集合类型可以表达一维无序数据
    • for循环可以遍历数据,进而对每个数据进行处理
2、一维数据的存储
  • 存储方式一:空格分隔

    • 使用一个或多个空格分隔进行存储,不换行

    • 缺点:数据中不能存在空格

      例: 中国 美国 日本 德国 法国
      
  • 存储方式二:逗号分隔

    • 使用英文半角逗号分隔数据进行储存,不换行
    • 缺点:数据中不能有英文逗号
  • 存储方式三:其他方式

    • 使用其他符号或符号组分隔,建议采用特殊符号
    • 缺点:需要根据数据点定义,通用性较差
3、一维数据的处理
  • 存储(表示)

    • 将存储的数据读入程序
    • 将程序表示的数据写入文件
  • 一维数据的读入处理

    • 从空格分隔的文件读入数据(示例)

      例:    中国 美国 日本 德国 法国
      	txt=open(fname).read()
          ls=txt.split()		#若用特殊符号分隔括号内特殊符号
          f.close()
          >>>ls
          ['中国','美国','日本','德国']
      
  • 一维数据的写入处理

    • 采用空格分隔方式将数据写入文件

      	ls=['中国','美国','日本']
          f=open(fname,'w')
          f.write(''.join(ls))	#若采用特殊符号分隔,例:f.write('$'.jinn(ls))
          f.close()
      

三、二维数组

1、二维数据的表示
  • 使用列表类型

    • 列表类型可以表达二维数据

    • 使用二维列表

      例:	[[3.14,3.16,3.18]
             [3.15,3.17,3.19]]
      
    • 使用两层for循环遍历每一个元素

    • 外层列表元素可对应一行也可对应一列

2、CSV格式与二维数据存储

CSV:comma-separated values

  • 国际通用的一二维数据存储格式,一般.CSV拓展名

  • 每行一个一维数据,采用逗号分隔,无空行

  • Excel和一般编辑软件都可以读入或另存为CSV文件

    例:
    表格数据:  城市·······           CSV存储数据:  城市,环比, 同比
              北京·······                         北京,101.5,120.7
               ·                                 上海 ,101.2,127.3
               ·
    

注意

​ 如果某个元素缺失,逗号仍要保留

​ 二维数据的表示可以作为数据存储,也可以另行存储

​ 逗号为英文半角逗号,逗号与数据之间无额外空格

3、二维数据的存储
  • 按行存或者按列存都可以,具体由程序决定
  • 一般索引习惯:ls(row][column],先行后列
  • 根据一般习惯,外层列表每个元素是一行,按行存
4、二维数据的处理
  • 二维数据的读入处理

    从CSV格式文件中读入处理

    例:
    	fo=open(fname)
        ls=[]
        for line in fo:
            line=line.replace('\n',' ')
            ls.append(line.split(','))
        f.close()
    
  • 二维数据的写入处理

    将数据写入CSV格式的文件

    	ls=[[ ],[ ],[ ]]		#三维列表
    	f.open(fname,'w')
    	for item in ls:
        	f.write(','.join(item)+'\n')
    	f.close()
    
  • 二维数据的逐一处理

    采用二层循环

    	ls=[[1,2],[3,4],[5.6]]		#二维列表
        for row in ls:
            for column in row:
                print(column)
    
补充:

CSV文件读写

写:
	import ssv		#系统内置模块
    file=opennn(文件名,'w',encoding='utf8',newline=' ')	#打开一个文件
    w=csv.writer(file)
    w.writerow([列表数据])		#写入数据,列表可以是多维的
读:
	file=open·····
    r=csv.reader(file)
    for data in r:
        逐行读取····

将数据写入内存:

​ 使用StringIO、BytesIO两个数学模块

​ from io import StringIO/BytesIO

四、json字符串

1、序列化与反序列化
  • 序列化:将数据从内存持久化保存到键盘的过程
  • 反序列化:将数据从硬盘加载到内存的过程
2、json写入注意点
  • write时,只能写入字符串或者二进制,字典、列表、数字等都不能直接写入文件,需要将其转化为字符串
  • 方法:repr/str 例:repr/str(字符串)
  • 也可以使用json模块
  • 转化为二进制,使用pickle模块
3、json中数据持久的方法
  • dumps:将数据转化成为json字符串,不会将数据保存到文件里

  • dump:将数据转换为json字符串的同时写入指定文件

    ​ 例:json.dump(字符串,文件名)

4、json中数据反序列化的方法
  • Loads:将json字符串加载为python里的数据。例:json.Loads(字符串)

  • Load:读取文件,把读取的内容加载为python里的数据

    例:
    	file=open(文件名,'r',encoding='utf8')
        json.Load(file)				
    
5、pickle模块的使用
  • 序列化:dumps、dump,将python数据转化为二进制

  • 反序列化:loods、lood

    例:
    	pickle.dumps(需要转化为字符串的数据)	#方法与json方法类似
    
6、json与pickle的区别
  • json只能保存部分信息,作用是用来在不同平台里传送数据(存储的是基本的数据类型)
  • pickle用以将数据原封不动的转换为二进制,但这个二进制只能在python里识别

面向对象编程

一、类和对象

1、类

类是对一群具有相同特征或行为的事务的一个统称,是抽象的,不能直接使用

  • 特征其实就是一个变量,在类里我们称之为属性
  • 行为其实就是一个函数,在类里我们称之为方法
  • 类其实就是由属性和方法组成的一个抽象概念
2、对象

​ 由类创建出来的一个具体的存在,可以直接使用。由哪一个类创建出来的对象,就拥有在哪一个类中定义的属性和方法

​ **注意:**每次创建对象都会调用_ _init _ _ 方法和 _ _new _ _方法

二、面向对象的基本语法

1、类
定义类:使用Class
格式:class <类名>#类名一般需要遵守驼峰命名法,内一个单词首字母大写
	 class <类名>(object)
例:小明今年18岁,身高1.75,每天早上跑完步,会去吃东西
	小美今年17岁,身高1.65,小美不跑步,小美喜欢吃东西
    
    class Student(object):		#关注这个类有哪些属性和行为
        def __init__(self,name,age,height):		#在__init__方法里,以参数形式定义属性
            self.name=name
            self.age=age
            self.height=height
        #行为定义另一个函数(方法)
        def run(self):
            print('正在跑步')
            
        def eat(self):
            print('正在吃东西')
        #student() 会自动调用__init__方法
        s1=Student('小明','18','1.75')		#使用Student类创建了两个实例对象s1、s2
        s2=	Student('小美','17','1.65')
        #根据业务逻辑,让不同的对象执行不同的行为
        s1.run()  s1.eat()  s2.eat()
2、self语句的使用
例:
	class Student(object):
        def __init__(self,x,y):
            self.name=x
            self.age=y
    s1=Student('张三','18')	#s1.name ==> 张三
执行流程:
		1、调用__new__方法,申请一段内存空间
    	2、调用__init__方法,让self指向申请好的那段内存空间
        3、让s1也指向创建好的这段内存空间
  #可使用"="直接为属性赋值,若这个属性不存在会给对象添加一个新的属性
例:	s1.city='上海'	#动态属性

	__slots__=()	#这个属性直接定义在类里,为一个元组,用来规定对象可以存在的属性
3、魔法方法(魔术方法)
  • 定义

    类里的一些特殊的方法

  • 特点

    • 不需要手动调用,会在合适的时机自动调用
    • 这些方法都使用__开始,使用 _ _结束
    • 方法名都是系统规定好的,在合适的时机自动调用(也可以手动调用)
  • 种类

    __init__	#在创建对象时,会自动调用
    __del__		#当对象被报销时,会自动调用这个方法
    __repr__	#当打印一个对象的时候,会自动调用这个对象的__ str__ 或者 __repr__方法
    __str__		#当两个方法都存在时,__str__优先。打印列表,调用__repr__方法
    __eq__		#运算符相关魔法方法
    
    注意:	#str()将对象转换为字符串,会自动调用__str__方法
    	  #str(p1)/print(p1),转换成为字符串,默认会转化类型+内存地址
    
    例:
    	class Parson(object):
            def __init__(self,name,age):
                self.name=name
                self.age=age
        #p1、p2是不同对象,指向内存不同
        p1=Person('张三','18')
        p2=Person('张三','18')
        print('p1 is p2',p1 is p2)	#False,is运算符,可判断两个对象是否为同一对象
        print('p1==p2',p1==p2)		#False,'=='本质调用__eq__方法,获取这个方法的返回值
        #注意:如果__eq__不重写,默认比较内存地址
    
  • 注意

    例:
    	class Person():
            ·
            ·
        	def __call__(self,**args,**kwargs):
                print('args={},kwargs={}'.format(args,kwargs))
                fn=kwargs['fn']
            return fn(args[0],args[1])
        
        p=Person(····)
        print(reprcp)	#调用内置函数repr会触发对象的__repr__方法
        print(p.__repr__)	#手动调用,魔法方法一般不手动调用
        n=p(1,2,fn=lambda x,y;x+y)	#对象名()<=>调用这个对象的<对象名>.__call__()方法
        print(n) ==>3
    
  • 算术运算符相关魔法方法

__ne__		#not eq,取反
__gt__		#greater than,使用>运算符自动调用
__ge__		#使用>=运算符自动调用
__lt__		#less than p1<p2
__le__		#<=
__add__		##
__sub__		#-
__null__	#*
例:
	def __<运算符>__(self.other):
        ········
    p1<运算>2  	#return self.name<运算符>other.name
    p1<运算>p2 	#return self.name<运算>other.name
重写__eq__方法:
	def __eq__(self,other):
        #if self.name==other.name and self.age==other.age:
        #	return True
        #return False
        return self.name==other.name and self.age==other.age
    p1=Person('张三'18)	==>	print(p1 is p2)	#False
    p2=Person('张三'18)	==> #True,重写,获取返回值
    print(p1 != p2)		#False,本质上调用重写的__eq__方法,或__ne__取反
4、内置属性
	print(dir(p))	#p为一个对象,打印所有属性和方法
    __class__		#属于那个类<class '__main__.person'>
    __dict__		#把对象属性和值换为一个字典
    __dir__			#等价于dir(p)
    __doc__			#用来查看文档注释,格式: 方法(对象名/类名).__doc__
    __module__		#打印 ==> __main__  模块名
    __slots__		#需要自己定义,规定对象所含属性
5、把对象当作字典操作
	__dict__	#将对象转换为字典(属性转换为字典),但不能直接把对象当字典用。<>.__dict__
    __setitem__	#[]语法会调用对象的__setitem__方法
    __getitem__	#[]语法自动调用,使对象可通过字典操作
例:
	class Person(object):
        def __init__(self,name,age,city):
            self.name=name
            self.age=age
            self.city=city
            
(1) p=Person('张三',18,'襄阳')
    print(p.__dict__)	#{'name':'张三','age':·····}
    
    	def __setitem__(self,key,value):
            p.__dict__[key]=value
(2)	p['age']=20		#[]调用__setitem__方法,修改对象age为20

		def __getitem__(self,item):
        	return self.__dict__[item]
(3)	print(p['name'])	#张三
6、类属性和对象属性
  • 对象p1、p2是通过Person类创建的实例对象

    p1=Person('张三'18)	#类person、对象p1、p2分别有单独不同的储存空间
    p2=Person('李四'19)	#name、age是对象属性,是每一个实例对象都会单独保留一份属性,
    					  #每个实例对象互不影响
    注意:对象属性,在__init__方法里,以参数形式定义    
    
  • 类属性(定义在类里、函数之外)

    • 类属性直接在类里定义(不在 __init _ _ 及其他方法里定义)

      class Person(object):
          type='人类':
      
    • 类属性可以通过类和实例对象获取

      print(Person.type) ==> 人类
      print(P1.type) ==> 人类	#p1(实例对象)无type属性,但可从类内存中找到类属性,
      					     #但不可修改类属性
      
    • 类属性只能通过类和实例修改

      p1.type='human'			#仅在p1内存中新建type='human'属性,不修改其属性
      Person.type='monkey'	#修改了类属性
      
7、私有属性(类内部私有)
  • 定义:以两个下划线开始的变量是私有变量

    例:
    	class Person(object):
            def __init__(self,name,age):
                self.name=name
                self.age=age
                self.__money=100		#__开头,私有变量
    注意:可在类里调用私有变量,调用函数获得私量
    	def test(self):		#类里定义函数/方法
            self.__money+=10	#此外私有变量可直接获取
    
  • 获取私有变量

    print(p.__money)	#不能直接获取
    print(p._Person__money)		#使用"对象._类名__私有变量名"获取
    
    定义get和set方法来获取(私有变量在类内容可调用)
    (1)在类内部定义
    	def get_money(self):
            return self.__money
        print(p.get_money())
    (2)修改私有变量,在类内部定义
    	def set_money(self,qian):
            self.__money=qian
        p.set_money		#修改变量
    
  • 私有函数(方法)

    • 定义:以两个下划线开始的函数,类内部私有,不能直接调用
    • 调用:<对象名>._<类名> __<私有函数名>()
8、类方法和静态方法
例:在Person类中(同前面Person方法),定义方法
	def eat(self,food):		#存储在类内存中
        print(self,name+'正在吃'+food)
    p1=Person('张三',18)
    p1.eat('红烧牛肉面')		#实例对象在调用方法时,不需要给形参self传参,
    					   #会自动把实例对象传递给self,直接使用实例对象调用方法
   #1、eat对象方法,可直接使用"<实例对象>.方法名(参数)"调用,使用此方法
   #不需要传递self,会自动将对象名传递给self
   #2、还可用<类名>.(方法名),这种方式,不会自动给self传参,需要手动的指定self
例:
	Person.eat(p1,'西红柿鸡蛋盖饭')	#将p1传给了self

注意:
	如果一个方法里没有用到实例对象的私有属性,可将这个方法定义成static
例:
	@static method
    def demo():		#定义在类内部
        pass
  • 静态方法(即用不到实例对象,也用不到类)

    例:
    	class calculator(object):
            @static method
            def add(a,b):
                return a+b
        calculator.add(1,4)		#或者c=calculator,c.add(1,4)
        #本质上可认为把一些函数方法,打包放到类中,
        #用面向对象的方法来调用(但这些函数,方法不使用对象的任何属性)
    
  • 类方法

    可以使用实例对象和类对象调用

    例:在类Person中
    	@classmethod
        def test(cls):		#如果这个函数只用到了类属性,我们可以定义成类方法
            pass			#cls指类(类对象),类方法会有一个参数cls,会自动传参
    
9、单例设计模式
例:
	class Sigleton(object):
        instance=None	#类属性,也可定义为私有属性
        __is_first=Ture	#s私有属性
        @classmethod
        def __new__(cls,*args,**kwargs):
            if cls.instance is None:
                #申请内存,构建一个对象,并把对象的类型设置为cls
                cls.instance=object.__new__(cls)
        return cls.instance
    def __init__(self,a,b):
        if self.__is_first:
            self.a=a
            self.b=b
            self.__is_frist=False
	s1=Singleton('呵呵','嘿嘿嘿')
    #调用__new__方法,会调用object的__new__方法,object的__new__方法会申请内存,
    #如果重写了__new__方法,需要手动申请内存
    s2=Singleton('哈哈','嘻嘻嘻')
    #s2不会重新申请内存,而是与s1共用一个内存,
    #若__init__方法无if语句,s2中'哈哈','嘻嘻嘻'会覆盖s1中的'呵呵','嘿嘿嘿'
10、面向对象相关方法
  • is

    身份运算符,用以比较是否为同一对象,实质上是获取两个对象内存地址(id(对象1)==id(对象2))进行比较

    type(对象名(实际)) #获取类,type(对象名)==类名,判断该对象是否由类创建

  • isinstance

    内置函数(方法),用来判断一个对象是否由指定的类(或父类)实例化出来的。

    格式:

    ​ isinstance(对象名,类名) #返回值为True/False,isinstance(对象名,类1,类2,······)

  • issubclass

    内置函数(方法),用来判断一个类是否为另一个类的子类

    格式:

    ​ insubclass(类名,可能父类) #返回值True/False,issubclass(类名,(可能父类1,可能父类2,·····))

三、继承

1、继承的基本使用
  • 面向对象编程有三大特征:封装、继承、多态

  • 封装:函数是对语句的封装;类是对函数和变量的封装

  • 继承:类和类之间可以认为手动的建立关系,父类的属性和方法,子类可以使用

  • 多态:是一种技巧,提高代码的灵活度

    例:
    	class Animal(object):	#父类,基类
            def __init__(self,name,age):
                self.name=name
                self.age=age
            
            def sleep(self):
                print(self.name+'正在睡觉')
        
        class Dog(Animal):	#子类,派生类。继承父类的__init__和sleep方法
            def bark(self):
                print(self.name+'正在叫')
                
        d1=Dog()	#Dog()调用__new__方法,再调用__init__方法。Dog中重写__new__方法,
        			#会查找父类。直到找到object
        d1=Dog('大黄',3)	#调用__init__(如new方法一样)
        d1.name ==>大黄
        d1.sleep() ==> 大黄正在睡觉	
        #父类里定义的属性,子类可以直接使用,父类的方法子类实例对象可以直接调用
    
2、多继承
  • 格式:

    ​ class 子类名(父类1名称,父类2名称): #父类个数不限

    ​ pass

  • 注意:

    • 如果多个父类有相同的方法(属性),排在子类括号中靠前的父类得到继承

      例: class 子类名(父类A,父类B)若AB存在相同属性子类将继承父类A中的····

    • 不同父类函数同名的情况下,可用一个类属性查看方法的调用顺序。_ _ mro_ _

      格式: 子类名._ _ mro_ _ 例:print(子类名._ _ mro_ _)

    • 深度大于广度,不同父类函数同名,优先级高的父类的深度深

3、私有属性的继承特点
  • 父类的私有方法,子类没有继承,可通过“对象名._ 父类名 _ _ 类名 _ _私有方法名()“调用;自己类里定义的私有方法 “对象名. _类名 _ _私有方法名()”
  • 私有方法和私有方法类似
4、拓展
  • 新式类和经典类的继承

    #手动的指定student类继承自object
    	class Student(object):
            pass
    #未指定Dog父类,python3默认继承object
    	class Dog():
            ps=pass
        
        新式类:继承自object的类
        经典类:不继承自object的类	(注意:在python2中不手动指定,即默认为经典类)
    

四、多态

1、子类重写父类的方法

#继承特点:如果一个类A继承自类B,由类A创建出来的实例对象都能直接使用类B里定义的方法

  • 子类的实现和父类的实现完全不一样。子类可以选择重写父类

  • 子类在父类的基础上又有更多的实现

    例:
    	class Person(object):
            def __init__(self,name,age):
                self.name=name
                self.age=age
            
            def Sleep(self):
                print(self.name+'正在睡觉')
         
        class Student(person):
            def __init__(self,name,age,school):
                Person.__init__(self,name,age)	#第一种方法
         #子类在父类实现的基础上,又添加了自己新的功能,调用父类的两种方法:
         #		1、父类名.方法名(self,参数,列表)
         #		2、使用surper直接调用父类方法,推荐使用第二种方法
        		surper(Student,self).__init__(name,age)	#第二种方法
            	self.school=school
            
            def Sleep(self):
                print(self.name+'正在课间休息时间睡觉')
                    
    
2、多态的使用
  • 概念:基于继承,通过子类重写父类的方法,达到不同的子类对象调用相同的父类方法,得到不同的结果,提高代码的灵活度。

    例:
    	class Dog(object):
            def work(self):
                print('狗正在工作')
        
        class PoliceDog(Dog):
            def work(self):
                print('警犬正在攻击敌人')
        
        class BlidDog(Dog):
            def work(self):
                print('缉毒犬正在搜毒')
        
        class Person(object):
            def __init__(self,name):
                self.name=name
                self.dog=None
        
        def work_with_dog(self):
            if self.dog is not None and isinstance(self,dog,Dog):
                self.dog.wrk
                
        p=Person('张三')
        
     1、pd=policeDog()
        p.dog=pd
        p.work_with_dog()
        
      2、bd=BlindDog()
    	p.dog=bd
        p.work_with_dog()
        
      3、dd=DruDog()
    	p.dog=dd
        p.work_with_dog()
    

网络编程

一、端口

1、定义

​ 像一个房子的门,是进出这间房子的必经之路,如果一个程序需要收发网络数据,那么就需要有这样的端口。(在linux系统中,端口可以有65535即2的16次方个)

2、端口号

用以标记端口,端口号只有整数,范围是0到65535,端口号按照一定的规定进行分配

  • 知名端口号

    知名端口号是众所周知的端口号,范围从0到1023.可以理解为,一些常见的号码是估计的,好比电话号码110、10086····,一般情况下,如果一个程序需要使用知名端口号需要有root权限

  • 动态端口号

    范围:从1024到65535

    之所以称为动态端口,是因为他一般不固定分配某种服务,而是动态分配动态分配是指当一个系统或程序需要网络通信时它向主机申请一个端口,主机从可用端口中分配一个供它使用,当程序关闭,同时也释放了所占用的端口号

  • 端口号作用

    同一电脑用pid区分不同程序(或进程)

    ​ ip + 协议 + 端口 ==> 标记网络进程

二、socket

1、概念

​ socket(套接字)是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多数都是基于socket来完成通信的。例如我们每天浏览网页、QQ聊天以及法email等等

2、创建socket

​ 在python中使用socket模块的函数socket就可以完成:

	import socket
    socket.socket(Adderss Family,Type)

说明:

  • 函数socket.socket创建一个socket,该函数带有两个参数:

    • Adderss Family:可以选择AF_INET(用于internet进程间通信)或者AF_UNIX(用于同一台机器进程间通信)实际工作中常用AF_INET
    • Type:套接字类型,可以是SOCK_STREAM(流式套接字,主要用于TCP协议)或者SOCK_DGRAM(数据报套接字,主要用于UDP协议
  • 创建一个tcp.socket(tcp套接字)

    	import socket		#创建tcp的套接字
        s=socket.socket(socket.AF_INET,socket.STREAM)
        ·············· #使用套接字的功能,省略
        s.close		#不用的时候关闭套接字
    
  • 创建一个udp.socket(udp套接字)

    	import socket
        #创建udp套接字
        s=socket.socket(socket.AF_INET,socket.DGRAM)
        ·········· 省略功能
        s.close
    

三、udp协议

注意:调试代码使用工具:网络调试助手(NetAssist)

1、概念

​ udp是User Datagram Protocol的简称,中文名称是用户数据报协议,在通信开始之前不需要建立相关的链接,只要发送数据即可,类似于生活中“写信”

2、udp发送数据

​ socket可以在不同的电脑间通信,还可以在同一电脑的不同程序之间通信

例:
	import socket
    s=socket.socket(socket.AF_INET,socket.SCK_DGRAM)
    #1、创建socket,并链接
    #AF_INET:表示这个socket是用来进行网络链接的
    #SOCK_DGRAM:表示这是一个udp链接
    
    s.sendto('你好'.encode('utf8'),('192.168.31.199',9090))	
    #2、发送数据,s.sendto(data,address)
    #data:要发送的数据,他是二进制的数据
    #address:发送给谁,参数是一个元组,元组内含2个参数,第0个表示目标哦IP地址,第一个表示程序端口号
    
    #3、关闭socket
    s.close
    
 注意:23之间接收数据:
	data,address=s.recvfrem(1024)
    print(·······)
3、udp接收数据

注意:通信时最好绑定端口号

例:
	import socket
    #创建基于udp的网络socket链接
    s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    #绑定端口号和IP地址
    s.bind('192.168.31.199',9090)
    #recvfrom 接收数据
    data,addr=s.recvfrom(1024)
    #接收到的数据是一个元组,元组里有两个元素:data,address。
    #第0个元素是接收到的数据,第1个元素是发送方的ip和端口号
    print('从地址:{}端口号{}接收到了消息,内容是:{}'.format(addr[0],addr[1],data))
    #data需做utf8编码,防止传输过程中文乱码,上一行写不下省略:data.decode('utf8')
    #关闭
    s.close()

四、TCP协议

1、udp和tcp协议

​ UDP:没有严格的客户端和服务器的区别

​ TCP:有客户端和服务器,面向连接的协议

2、TCP客户端(发送)
例:
	import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #基于TCP协议的socket连接,在发送数据前,需先与服务器建立连接
    s.connect(('192.168.31.199',9090))	#调用connect方法连接到服务器
    s.send('hello'.encode('utf8'))
    s.close()
3、TCP服务端(接收、处理)
例:
	import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.bind(('192.168.31.199',9090))
    #把socket变成一个被动监听的socket
    s.listen(128)	#注意:128位队列数,即超过服务器极限数目的最多排队数
    #s.accept接收数据,一个元组,内含两个元素,第0个客户端socket连接,第一个是客户端ip和端口号
    client_socket,client_addr=s.accept()	#拆包
    data,addr=client_socket.recv(1024)	#tcp用recv获取数据,udp用recvfrom
    print('客户端{}端口号{}内容{}'.format(addr[0],addr[1],data.decode(utf8)))

五、HTTP服务(程序搭建)

1、概述
  • http协议:Hyper Text Transfer Protocol 超文本传输协议

  • HTML:超文本标记语言

  • 架构

    • C/S 架构:client—server 例:手机版淘宝
    • B/S架构:browser—server 例:浏览器访问淘宝网
  • http服务器都是基于tcp的socket链接

  • B/S架构中。http请求头:request headers,http响应头:response headers

    返回内容前,需要先设置http响应头

    例:
    	client_socket.set('HTTP/1.1 200 OK\n'.encode('utf8'))
        client_socket.send('content-type:text/html\n'.encode('utf8'))
    
  • http请求头

    • GET 请求方式 GET/PUT/POST/DELETE
    • /<文件名> “/" 请求路径
    • HTTP/1.1 HTTP版本号
    • HOST:<IP地址> 请求服务器地址
    • User-Agent: UA 用户代理,最开始设计目的为辨别用户浏览器类型
  • IP地址的绑定

    127.0.0.1 和 0.0.0.0 都表示本机,0.0.0.0表示所有可用的地址

    例:
    	server_socket.bind(('0.0.0.0',<端口号>))
        server_socket.listen(128)	#监听队列数
    
  • 状态码

    • 2xx:请求响应成功
    • 3xx:重定向
    • 4xx:客户端错误
    • 5xx:服务器错误
2、http服务程序实例

​ 根据不同请求返回不同内容

	#创建好socket链接并绑定端口后
    #根据不同请求作出不同响应
    while True:
        client_socket,client_addr=serve_socket.accept()
        data=client_socket.recv(1024).decode('utf8')
        path='/'
        if data:	#浏览器发送过来的数据有可能是空的
            path=data.splitlines()[0].split(' ')[1]
            print('请求路径是{}'format(path))
            response_body='返回内容'
            if path=='</请求路径>':
                response_body='返回内容'
                ·······		#各种请求对应不同响应(返回内容)
                ·······
     响应:
    	'conten-type:text/html;charset=utf8\n'	#用utf8打开
        404页面未找到:'HTTP/1.1 404 page not found'
        成功访问:'HTTP/1.1 200 OK\n'
3、面向对象封装
	import socket
    class myserver(object):
        def __init__(self,ip,port):
            self.socket.socket(socket.AF_INET·····)
            self.bind(ip,port)
            self.socket.listen(128)
        def run_server(self):	#服务器代码主体
            ········
            
        server=myserver('0.0.0.0',9090)
        server.run_server()
4、WSG1服务器

​ 类似于模块、框架

实例:
	import json
    from wsgiref.simple_server import make_server
    def load_file(file_name,**kwargs):
        try:
            with open('pages(文件名)'+file_name,'r',encoding='utf8') as file:
                cotent=file.read()
                if kwargs:	#一个字典,需要填入'html'里的内容
                    content(content.format(**kwargs))	#拆包,填入对应的位置
                return content
         except  FileNotFoundError:
            print('文件未找到')
    def index():
        ····
    def show():
        return load_file(xxx.txt)
    def show_info():
        return load_file('info.html',username='zhangsan',age=19,gedder='male')
    url={
        '/':inndex,
        '/show':show,
        ·····
    }
    def demo_app(environ,start_response):	
        #demo_app需两个参数
        #第0个参数,表示环境(电脑环境:请求路径相关环境)
        #第一个参数,是一个函数,用来返回响应头
        #这个函数需要一个返回值,返回值是一个列表
        #列表里只有一个元素,是一个二进制,表示返回给浏览器的数据
        path=environ['PATH_INFO']	
        #environ是一个字典,保存了很多数据,其中重要的一个是PATH_INFO能获得到用户的访问路径
        statas_code='200 ok'
        method=url.get(path)
        if method:
            response=method()
        else:
            statas_code='404 not found'
            response='页面走丢了'
        start_response(statas_code,[('Contenn-Type','text/html;charset=utf8')])
        return [response.encode('utf8')]
    
    if __name__=='__main__':
        httpd=make_server(' ',8090,demo_app)
        sa=httpd.socket.getsocketname()
        print('serving HTTP on',sa[0],'port',sa[1],'····')
        httpd.serve_forever()	#服务器一直运行
        #httpd.headle_request()	#只处理一次请求
        
        #一般放在处理请求前
        import webbrowser
        webbrowser.open('http://locahost:8000/xyz?abc')
        #相当于打开电脑浏览器并输入'http://······'       

多任务

一、线程

1、创建线程
	import threading
    t1=threadingn.Thread(target=<函数/方法>)	#线程1
    t2=threadingn.Thread(target=<函数/方法>)	#线程2
    #注意:target需要一个函数,用来指定需执行的任务
    #启动线程
    t1.start()		#两个线程并非同时运行,而是交替运行
    t2.start()		#python多线程特性,多个线程交替运行,称为伪多线程
    
补充:
	threading.carrent_thread().name		#方法,得到线程名字
    t1=threading.Tread(target=<函数/方法>,name=<线程名>)
2、多线程与线程锁
  • 多线程

    多个线程可以同时操作一个全局变量,即多线程共享全局变量

  • 同步

    当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。同步就是协同步调,按预定的先后顺序进行运行。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。

    • 互斥锁

      互斥锁为资源引入一个状态:锁定/非锁定

      某个线程需要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改。直到该线程释放资源,将资源的状态变成“非锁定”,其他线程才能两次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性

      threading模块中定义了Lock类,可以方便的处理锁定

      	#创建一把“锁”
          lock=threading.Lock()
          #加载同步锁
          lock.acquire()
          #释放“锁”
          lock.release()
      
3、线程间的通信
  • 概念

    线程之间有时需要通信,操作系统提供了很多机制来实现线程间的通信,其中我们使用最多的就是队列Queue

  • Queue的原理

    Queue是一个先进先出(first in first out)的队列,主进程中创建一个Queue对象,并作为参数传入子进程,两者之间通过put()放入数据,通过get()取出数据,执行了get函数之后队列中的数据会被同时删除,可以使用multiprocessing 模块的Queue实现多线程之间的数据传递

    	#创建一个队列q
    	q=queue.Queue()
    	#往队列q中添加数据(元素)
    	q.put()
    	#从队列中取出数据(元素)
    	q.get()
    
    补充:
    	stack 栈结构
        先进先出,后进后出
    

二、进程

1、概述
  • 程序:例如 xxx.py是一个程序,是一个静态的

  • 进程

    一个程序运行起来后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位

    注意:不仅可以通过线程完成多任务,进程也是可以的

  • 进程的状态

    工作中,任务数往往大于cpu的核数,即一定有一些任务正在执行,而另一些任务在等待cpu进行执行,因此导致了有了不同的状态

    • 就绪态:运行的条件都自己满足,正等待cpu进行执行
    • 执行态:cpu正在执行其他功能
    • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态。
2、创建进程
  • multiprocessing 模块是跨平台版本的多进程模块,提供了一个process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情

  • 示例:创建子进程,只需要传入一个执行函数和函数的参数创建一个process实例,用start()方法启动

    	import multiprocessing
    	·······
        if __name__ == '__main__':	#注:在windows系统中,须当执行整个.py文件时,才不会报错
            p1=multiprocessing.process(target=<方法/函数(不带括号)>)
            p2=multiprocessing.process(target=<方法/函数(前面已定义)>)
        启动:
        	p1.start()
            p2.start()
        注意:
        #如果函数/方法有参数,使用"args"
        target=<函数>,args=<参数>
        #进程pid:
        import os
        os.getpid()		#放于方法/函数下(代码行下)
    
  • 方法说明

    	process(target[,name[,args[,kwargs]]])
        #target:如果传递了函数的引用,可以任务这个子进程就执行这里的代码
        #args:给target指定的函数传递的参数,以元组的方式传递
        #kwargs:给target指定的函数传递命名参数
        #name:给进程设定一个名字,可以不设定
        process创建实例对象的方法:
        start()		#启动子进程实例(创建子进程)
        is_alive()	#判断子进程是否存活
        join(timeout)	#是否等待子进程结束,或等待多少秒
        teminate()	#不论任务是否完成,立即终止子进程
    注意:常用属性
    	name:进程别名,默认process-N,N从1递增
        pid:进程号(进程pid)	
    
3、进程与线程区别
  • 功能区别

    • 进程:能够完成多任务。比如在一台电脑上同时运行多个QQ

    • 线程:能够完成多任务。比如一个QQ中多个聊天窗口

  • 定义不同

    • 进程:系统进行资源分配和调度的一个独立单位
    • 线程:线程的一个实体,是cpu调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一个点运行中必不可少的资源(如程序计数器,一组寄存器和栈0),但它可与同属一个进程的其他线程共享进程所拥有的全部资源
  • 区别总结

    • 线程执行开销小,但不利于资源的管理和保护,而进程正相反
    • 一个程序至少有一个进程,一个进程至少有一个线程
    • 线程的划分此尺度小于进程(资源比进程少),使得多线程程序的并发性高
    • 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的运行效率
    • 线程不能独立执行,必须依存在进程中
    • 可以将进程理解为工厂中的一条流水线,而其中的线程就是这个流水线上的工人
    • 多线程可以共享全局变量,多进程不能共享全局变量(都可调用,但无相互关系)
4、进程间通信

注:进程间通信与线程一样,使用队列Queue,但这个队列是multiprocessing中的模块,即multiprocessing.Queue。队列中先进先出,用法与前面线程间通信Queue一致

5、队列的使用
  • 种类

    • 线程间通信:queue.Queue()
    • 进程间通信:multiprocessing.Queue()
  • 用法

    示例:以multiprocess.Queue为例

    	#创建队列时,可以指定最大长度,默认是0,表示无限制。超过限制造成堵塞,进程等待
    	q=multiprocessing.Queue(5)
    	#查看判断进程队列Queue是否满了(达到限制),返回布朗值,True表示满了
        print(q.full())
        #block:put参数,表示阻塞
        q.put('how',block=True)		#若队列满了就等待
        #timeout:put参数,超时,等待xx时间后,程序报错,单位秒
        #若不使用block、timeout则会一直等待,不报错,一般两者配合使用
        q.put('how',block=True,timeout=1)		#等待1秒后报错
        #不阻塞,满了不等待,直接报错
        q.put_nowait('how') 	#等价于:q.put('how',block=False)
    
    注意:
    	get()与put()一样,都会阻塞,都可配置timeout
    
6、进程池
  • 概述

    当进程(所需)不多时,可直接用multiprocessing中process动态生成多个进程。但当需要进程非常多时,可用multiproocessing 中pool方法

    初始化pool时,可以指定一个最大进程数,当有新的请求提交到pool时,若池未满,那就创建一个新的进程来执行该请求。反之,请求会等待,直至池中有进程结束,才会用之前进程执行新的任务

  • 实例

	from multiprocessing import pool
    import os,time,random
    
    def worker(msg):
        t_start=time.time()
        print("%s开始执行,进程号为%d"%(msg,os.getpid()))
        time.sleep(random.random()*2)	#random 随机生成0~1之间浮点数
        t_stop=time.time()
        print(msg,"执行完毕,耗时%0.2f"%(t_stop-t_start))
	
    po=pool(3)	#定义一个进程池,最大进程数3
    for i in range(0,10):
        #pool().apply_async(需要调用的目标,(传递给目标的参数元组,))
        #每次循环将会用空闲出来的子进程去调用目标
        po.apply_async(worker,(i,))
    
    print('-----start------')
    po.close()	#关闭进程池,关闭后po不再接收新的请求
    po.join()	#等待po中所有子进程执行完毕,必须放在close语句之后
    print("------end-------")
7、join方法的使用

​ 进程和线程中都有join方法,join方法的作用是让主进程(或线程)等待子进程(或线程)

	import time
    
    x=10
    def test(a,b):
        time.sleep(1)
        global x
        x=a+b
        
	t=threading.Thread(target=test,args=(1,1))
    t.start()
    #t.join(),使用join方法后,主线程将等待子线程运行完成,打印结果为2
    print(x)	#结果为10,主线程快于子线程,主线程先打印结果,子线程还在计算

python爬虫

一、requests库

1、requests库的7个主要方法
	requests.request()	#构造一个请求,支持以下各种方法的基础方法
    requests.get()		#获取HTML网页的主要方法,对应于HTTP的GET方法
    requests.head()		#获取HTML网页头信息的方法,对应于HTTP的HEAD
    requests.post()		#向HTML网页提交POST请求方法,对应于HTTP的POST
    requests.Put()		#向HTML网页提交PUT请求的方法,对应于HTTP中PUT
    requests.patch()		#向HTML网页提交PATCH请求的方法,对应于HTTP中PATCH
    requests.delete()	#向HTML页面提交删除请求,对应于HTTP的DELETE
2、requests库的get()方法
  • 含义

    	r=requests.get(url)
        #构造一个向服务器请求资源的request对象,url为请求路径
        #返回一个包含服务器资源的response对象,并将对象赋给变量r
    
  • 格式构成

    	requests.get(url,params=None,**kwargs)
        #url:拟获取页面url链接
        #params:url中的额外参数,字典或字节流格式,可选
        #**kwargs:12个控制访问的参数
    
3、requests库的2个重要对象
  • response对象(包含服务器返回的所有信息,也包括请求的request的信息)
  • request对象
4、response对象
  • response对象的属性

    	r.status_code		#HTTP请求的返回状态,200表示连接成功,404表失效
        r.text				#HTTP响应内容的字符串形式,即url对应的页面内容
        r.encodeing			#从HTTP header中猜测的响应内容编码方式
        r.content			#HTTP响应内容的二进制形式
        r.apparent_encoding	#从内容中分析出响应内容编码方式(备选编码方式)
    
  • 理解response的编码

    	r.encoding:如果header中不存在charset,则认为编码为ISO-8859-1
        			r.text根据r.encoding显示网页内容
        r.apparent_encoding:根据网页内容分析出的编码方式,可以看做r.encoding备选
    
5、requests库异常
  • 异常种类

    	requests.connectionError	#网络连接错误异常,如果DNS查询失败,拒绝连接等
        requests.HTTPError			#HTTP错误异常
        requests.URLRequired		#URL缺失异常
        requests.ToomanyRedirects	#超过最大重定向次数,产生重定向异常
        requests.ConnecTimeout		#连接远程服务器超时异常
        requests.Timeout			#请求url超时,产生超时异常
    
  • 异常处理(爬虫通框架)

    	r.raise_for_status()	#在方法内部判断 r.status_code 是否等于200,不需要增加额外的if
        						#语句,该语句便于利用try-except 进行异常处理
    

    框架

    	import requests
        def gethtmltext(url):
            try:
                r=requests.get()
                r.raise_for_status()	#若状态码不是200,引发HTTPError报错
                r.encoding=r.apparent_encoding
                return r.text
            except:
                return "异常"
    
6、HTTP协议
  • 定义

    HTTP,Hypertext Transfer Protocol 超文本传输协议。HTTP是一个基于“请求与响应”模式的、无状态的应用层协议。HTTP协议采用URL作为定位网络资源的标识,URL格式如下:http:// host [:port] [patch]

  • 名词解释

    • host:合法的Internet主机域名或IP地址
    • port:端口号,缺省端口为80
    • path:请求资源的路径
    • HTTP URL:URL是通过HTTP协议存取资源的internet路径,一个url对应一个数据资源
  • HTTP协议对资源的操作

    • GET:请求获取url位置的资源
    • HEAD:请求向URL位置资源的响应消息报告,即获得该资源的头部信息
    • POST:请求向URL位置的资源后附加新的数据
    • PUT:请求向URL位置存储一个资源,覆盖原URL位置的资源
    • PATCH:请求局部更新URL位置的资源,即改变该处资源的部分内容
    • DELETE:请求删除URL位置存储的资源
  • PATCH和PUT的区别

    假设URL位置有一组数据UserInfo,包括UserTD、UserName等20个字段

    需求:用户修改UserName、其他不变

    • 采用PATCH,仅向URL提交UserName 的局部更新请求
    • 采用PUT,必须将所有20个字段一并提交到URL,未提交字段被删除。PATCH最主要好处:节省网络带宽
7、request()方法
  • requests.request(method,url,**kwargs)

    • method:请求方式,对应get/put/post等7种
    • url:拟获取页面的url链接
    • **kwargs:控制访问参数,共13个
  • **kwargs访问控制参数(13个)

    • params:字典或字节序列,作为参数增加到url中

      >>>kv={'key1':'value1','key2':'value2'}
      >>>r=requests.request('GET','http://python123.io/ws',params=kv)
      >>>print(r.url)
      http://python123.io/ws?key1=value1&key2=value2
      
    • data:字典、字节序列或文件对象,作为request对象的内容

      >>>kv={'key1':'value1','key2':'value2'}
      >>>body='主体内容'
      >>>r=requests.request('GET','http://python123.io/ws',params=kv,data=body)
      
    • json:JSON格式的数据,作为request的内容(与data类似)

    • headers:字典,HTTP定制头,request请求头(比如UA、host等信息)

      >>>hd={'User-Agent':'chrome/10'}
      >>>r=requests.request('POST','http://python123.io/ws',headers=hd)
      
    • Cookies:字典或cookieJar,request中的cookie

    • auth:元组,支持HTTP认证功能

    • files:字典类型,传输文件

      >>>fs={'file':open('data.xls','rb')}
      >>>r=requests.request('POST','http://python123.io/ws',files=fs)
      
    • timeout:设定超时时间,秒为单位

      >>>r=requests.request('POST','http://python123.io/ws',timeout=3)
      
    • proxies:字典类型,设定访问代理服务器,可以增加登录认证

      >>>pxs={'http':'http://user:pass@10.10.10.1:1234','https':'https://10.10.10.1:44321'}
      >>>r=requests.request('GET','http://python123.io/ws',proxies=pxs)
      
    • allow_redirects: True/False,默认为True,重定向开关

    • stream: True/False,默认为True,获取内容立即下载开关

    • Verify: True/False,默认为True,认证SSL证书开关

    • cert:本地SSL证书路径

8、get()方法
	格式:requests.get(url,params=None,**kwargs)
    #url:拟获取页面url链接
    #params:url中的额外参数,字典或字节流格式,可选
    #**kwargs:12个控制访问参数。与request方法一致,除params。
    #requests库其他6个方法由request封装而来,故他们参数与request一致
9、其他5个方法

requests库其他6个方法由request封装而来,故他们参数与request一致

	requests.head(url,**kwargs)	#**kwargs 12个可选
    requests.post(url,data=None,json=None,**kwargs)
    requests.put(url,data=None,**kwargs)
    requests.patch(url,data=None,**kwargs)
    requests.delete(url,data=None,**kwargs)
10、爬取网页的通用框架
	try:
        r=requests.get(url,timeout=30)
        r.raise_for_status()
        r.encodeing=r.apparent_encoding
        return r.text
    except:
        return '产生异常'

二、robots协议

1、网络爬虫的限制
  • 来源审查:判断User-Agent进行限制,检查来访HTTP协议头的User-Agent域,只响应浏览器或友好爬虫的访问

  • 发布公告:robots协议,告知所有爬虫,网站的爬取策略,要求爬虫遵守(非强制性)

2、robots协议

Robots Exclusion Standard,网络爬虫排除标准

  • 作用:网站告知爬虫那些页面可以抓取,那些不行
  • 形式:在网站根目录下的 robots.txt 文件,例如京东robots协议 https://www.jd.com/robots.txt
  • 原则:类人行为可不参考robots协议

三、Beautiful soup 库

​ 别名:beautiful soup 4,bs4

1、对bs4库的理解
	<html>
        <body>
            <p class="title">
                ·······				<====> HTML 标签树
            </p>
        </body>
	</html>
	Beautiful Soup 库是解析、遍历、维护“标签树”的功能库
	<p>····</p>:标签Tag
	<p class="title">·····</p> #p:标签名称name,成对出现。class:属性Attributes,0个或多个
2、bs4库的引用

​ 主要是用bs4库中的BeautifulSoup类

	from bs4 import BeautifulSoup
    import bs4
3、BeautifulSoup类

​ HTML <———> 标签树 <———> BeautifulSoup

​ BeautifulSoup 对应一个HTML/XML 文档的全部内容

4、bs4库解析器
例:soup=BeautifulSoup('<html>data</html>','html.parser')
	
    bs4的HTML解析器(需安装bs4库):BeautifulSoup(mk,'html.parser')
    lxml的HTML解析器(需安装lxml库):BeautifulSoup(mk,'lxml')
    lxml的xml解析器(需安装lxml库):BeautifulSoup(mk,'lxml')
    html5lib的解析器(需安装html5lib库):BeautifulSoup(mk,'html5lib')
5、BeautifulSoup类基本元素
	Tag:标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾
    Name:标签的名字,<>····</>的名字是'p',格式:<tag>.name
    Attributes:标签的属性,字典形式组织,格式:<tag>.attrs
    Navigablestring:标签内非属性字符串,<>···</>中字符串,格式:<tag>.string
    comment:标签内非属性字符串的注释部分,一种特殊的comment类型

元素详解:

	Tag
    #任何存在html语法中的标签都可以用soup.<tag>访问获得。当HTML文档存在多个相同<tag>对应内容时,
    #soup.<tag>返回第一个
    Tag的name(名字):每个<tag>都有自己的名字,通过<tag>.name获取字符串类型
    Tag的attrs(属性):一个<tag>标签可以有多个属性,字典类型。
    Tag的Navigablestring:Navigablestring可以跨越多个层次。
    Tag的comment:comment是一种特殊类型
6、基于bs4库html内容遍历

注意:以下示例的soup为BeautifulSoup类解析的网页内容

  • HTML基本格式
<html>
    <head>
        <title>This is a python demo page</title>
    </head>
    <body>
        <p class="title">
        </p>
        <p class="course">python is ··········
           <a herf=>is demo········
           </a>
            <a>is demo·····
            </a>
        </p>
    </body>
</html>				//<>···</>构成了所属关系
  • 标签树
		|			<html>			   <————|b到html为上行遍历
    	|	<head>							|	<body>
        |  								<p>	|	<-------->	<p>
        |   <title>						<b>	|		  	<a>	<--> <a>
  html到title为下行遍历							p到p,a到a是平行遍历
  • 标签树的下行遍历

    • .contents #子节点的列表,将所有儿子节点存入列表
    • .children #子节点的迭代类型,与.contents类似,用于循环遍历儿子节点
    • descendants #子孙节点的迭代类型,包含所有子孙节点,用于循环遍历儿子节点
    例:
    	for child in soup.body.children:
            print(child)					#遍历儿子节点
        
        for child in soup.body.descendants:
            print(child)					#遍历子孙节点
    
  • 标签树的上行遍历

    • .parent #节点的父亲标签

    • .parents #节点先辈标签的迭代类型,用于循环遍历先辈节点

      ​ #注意:遍历先辈节点,包括soup本身,所以要区别判断

  • 标签树的平行遍历

    • .next_sibling #返回按照HTML文本顺序的下一个平行节点标签

    • .previous_sibling #返回按照HTML文本顺序的上一个平行节点标签

    • .next_siblings #迭代类型,返回按照HTML文本顺序的后续所有平行节点标签

    • .previous_siblings #迭代类型,返回按照HTML文本顺序的前续所有平行节点标签

      注意:平行遍历发生在同一父亲节点下的个节点之间

      例:
      	for sibling in soup.a.next_strings:
              print(sibling)					#遍历后续节点
          
          for sibling in soup.a.previous_siblings:
              print(sibling)					#遍历前续节点
      
7、基于bs4库HTML格式输出
  • bs4库的prettify()方法

    • prettify()为HTML文本<>及其内容增加’\n’
    • prettify()可用于标签,方法:.prettify()
  • bs4库的编码

    bs4库将任何HTML输入都变成utf-8编码;python 3.x 默认支持编码是utf-8,解析无障碍

四、信息标记与提取方法

1、HTML的信息标记
  • HTML通过预定义的<>····</>标签形式组织不同类型的信息

  • 信息标记的三种形式

    • XML(extensible Markup language) #可拓展性好,但繁琐

      例:
      	<img src="chain.jpg" size="10"/>
      			完元素的缩写形式
      	<! --This is a comment,very useful-->
      			注释书写形式
      	格式:
      	<name>·····</name>
      			<name/>
      			<!--   -->
      
    • JSON(JavaScript Object Notation) #有类型的键值对,适合程序处理js,较xml简洁

      	有类型的键值对  key:value
      	示例:"name":"北京大学"     #name:类型,键key。北京大学:值value
      
      	例:
      	"name":["北京大学""延安自然学院"]	#多值用[,]组织
      	"name":{
              "newName":"北京大学",
              "oldName":"延安自然学院"				#键值对嵌套用{,}
          }
      
      	键与值得三种形式:
      	"key":"value"
      	"key":["value1","value2"]
      	"key":["subkey":"subvalue"]
      
    • YAML(YAML Aint Markup Language) #信息无类型,文本信息比例高,可读性好

      	1、无类型键值对 key:value
      	name:北京理工大学		#name:键key,仅字符串。北京大学:值value
      	
      	2、缩进表示所属关系
      	name:
      		newName:北京理工大学
      		oldName:延安自然科学院
      		
      	3、-表达并列关系
      	name:
      	-北京理工大学
      	-延安自然科学院
      	
      	4、|表达整块数据,#表示注释
      	text:| #学校介绍
      	······················
      	······················
      
2、信息提取的一般方法
  • 方法一:完整解析信息的标记形式,再提取关键信息。(xml、json、yaml)需要标记解析器,例如:bs4库的标签树遍历

  • 方法二:无视标记形式,直接搜索关键信息。(搜索)对信息文本查找函数即可

  • 融合方法:结合形式解析与搜索方法,提取关键信息。(xml、json、yaml、搜索)需要标记解析器及文本查找函数

3、html内容查找方式

​ 基于bs4库

  • find_all()方法

    	<>.find_all(name,attrs,recursive,string,**kwargs)
    	#返回一个列表类型,存储查找结果,<>表示经beautifulsoup对象解析后的内容,下面以前文soup表示
    
  • name:按标签名称检索字符串

    >>>fof tag in soup.find_all(True):		#True表示所有标签
        	print(tag.name)					#输出所有标签名
    
    >>>import re
    >>>for tag in soup.find_all(re.compile('b')):
        	print(tag.name)					#输出所有b标签
    
  • attrs:对标签属性的检索字符串,可标注属性检索

    >>>soup.find_all(id='link')	#括号内仅一个参数直接写属性,为多个参数时为attrs(id='link')
    
    >>>import re
    >>>soup.find_all(id=re.compile('link'))
    
  • recursive:是否对子孙节点全部检索,默认True

    >>>soup.find_all('a',recursive=False)
    
  • string:<>······</>中字符串区域的检索字符串

  • 注意:

    注意:
    	<tag>(····)		#等价于<tag>.find_all(···)
        soup(···)		#等价于 soup.find_all(···)
    
4、拓展方法
	<>.find()			#搜索且只返回一个结果,用find_all()参数
    <>.find_parents()	#在先辈节点中搜索,返回列表类型,同find()参数
    <>.find_parent()	#在先辈节点中返回一个结果,同find()参数
    <>.find_next_siblings()	#在后续平行节点中搜搜,返回列表类型,同find_all()参数
    <>.find_next_sibling()	#在后续平行节点中返回一个结果,同find()参数
    <>.find_previous_sibling()	#在前序平行节点中搜索,返回列表类型,同find_all()参数
    <>.find_previous_sibling()	#在前方平行节点中返回一个结果,同find()参数

五、Re(正则表达式)库

1、正则表达式的概念

​ re:regular expression ,别名:regex、RE

  • 正则表达式是用来简洁表达一组字符串的表达式

    例:
    	'pn'
        'pyn'				正则表达式:
        'pytn'			<====>  p(y/yt/yth/ytho)?n
        'pythn'
        'python'
        
        'py' 'pyy' 'pyyy' 'pyyyy' ······'pyyy····(无穷个)' 
           正则表达式:py+
         
        'py'开头,后续存在不多于10个字符,后续字符不能是'p''y'(例:pyabc)
        	正则表达式: py[^py]{0,10}
    
  • 正则表达式是一种通用的字符串表达框架,可以用来判断字符串中的特征归属

2、正则表达式的使用
  • 正则表达式在文本处理中十分常用:

    • 表达文本类型的特征(病毒、入侵等)
    • 同时查找或替换一组字符串
    • 匹配字符串的全部或部分
  • 编译:将符合正则表达式语法的字符串转换成正则表达式特征

    例:
    	正则表达式:p(y/yt/yth/ytho)?n
         regex='p(y/yt/yth/ytho)?n' ===编译==> p=re.compile(regex)  #特征p
    
3、正则表达式的语法

​ 正则表达式由字符和操作符构成

  • 正则表达式常用操作符

    	.		#表示任何单个字符
    	[]		#字符集,对单个字符给出取值范围。	[abc]表示a、b、c,[a~z]表示a到z单个字符
    	[^]		#非字符集,对单个字符给出排除范围。	[^abc]表示非a或b或c的单个字符
    	*		#前一个字符0次或无限次拓展。	abc*表示ab、abc、abcc、abccc等
    	+		#前一个字符1次或无限次拓展。	abc+表示abc、abcc、abccc等
    	?		#前一个字符0次或1次拓展。	abc?表示ab、abc
    	/		#左右表达式任意一个。	abc/def表示abc、def
    	{m}		#拓展前一个字符m次。	ab{2}c表示abbc
    	{m,n}	#拓展前一个字符m至n次(含n)。 ab{1,2}c表示abc、abbc
    	^		#匹配字符串开头。	^abc表示abc且在一个字符串开头
    	$		#匹配字符串结尾。	abc$表示abc且在一个字符串结尾
    	()		#分组标记,内部只能使用|操作符。	(abc)表示abc,(abc|def)表示abc、def
    	\d		#数字、等价于[0~9]	\D非数字的等价于[^0~9]
    	\w		#单词字符、等价于[A~Za~z0~9] (匹配非标点符号)	\W与\w相反
    	\s		#非打印字符(空格、换行、制表符)	\S打印字符
    	注意:
        匹配标点符号需加\。例:匹配点\.
        []表示可选项范围,结果为单个字符。|表示或者
    
  • 示例

    	^[A~Za~z]+$				#由26个字母组成的字符串
    	^[A~Za~z0~9]+$			#由26个字母和数字组成的字符串
    	^_?\d+$					#整数形成的字符串
    	^[0~9]*[1~9][0~9]*$		#正整数形式的字符串
    	[1~9]\d{5}				#中国境内邮政编码,6位
    	[\u4eoo-\ugfa5]			#匹配中文字符
    	\d{3}-\d{8}|\d{4}-\d{7}	#国内电话号码:010-68913536
        
        匹配IP地址正则表达式(IP地址分段,每段0-255):
        \d+.\d+.\d+.\d+ 或 \d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}
        精确算法:
        0~99:[1~9]?\d
        100~199:1\d{2}
        200~249:2[0-4]\d
        250~255:25[0-5]
        即:(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]).){3}([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])
    
4、re库(字符串匹配)
  • 调用:import re

  • 正则表达式的表示类型

    • raw string类型(原生字符串类型)<=> 注意:不包含对转义符再次转义的字符串。

      ​ re库用raw string类型表示正则表达式,表示为:r’text’

      ​ 例如:r’[1-9]\d{5}’

    • re库也可以采用string类型表示正则表达式,但更繁琐。

      ​ 例如:‘[1-9]\d{5}’

      ​ 建议:当正则表达式包含转义字符时,使用raw string

  • re库的功能函数

    re.search()		#在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
    re.match()		#从一个字符串的开始位置起匹配正则表达式,返回match对象,无结果返回None
    re.findall()	#搜索字符串,以列表类型返回全部能匹配的字串。
    re.split()		#将一个字符串以正则表达式匹配结果进行分隔,返回列表类型
    re.finditer()	#搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象
    re.sub()		#在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
    
  • re功能函数详解

    • re.search(pattern,string,flags=0)

      	re.search(pattern,string,flags=0)
          #pattern:正则表达式的字符串或原生字符串
          #string:待匹配字符串
          #flags:正则表达式使用时的控制标记
          注意:常用标识
          re.I	re.IGNORECASE	#忽略正则表达式的大写,[A-Z]能够匹配小写字符
          re.M	re.MUL TLL IME	#正则表达式中的^操作符能够将给定字符串的每行当作匹配的开始
          re.S	re.DTALL	  #正则表达式中的.操作符能够匹配所有字符,默认匹配除换行外所有字符
      
    • re.match(pattern,string,flags=0)

    • re.findall(pattern,string,flags=0) #以上两个函数参数同re.search()

    • re.split(pattern,string,maxsplit=0,flags=0)

      	re.split(pattern,string,maxsplit=0,flags=0)
          #pattern,string,flags参数同上
          #maxsplit:最大分割数,剩余部分作为最后一个元素输出
      	例:
          >>>re.split(r'[1-9]\d{5}','BLT10081 TSU10084')
          ['BLT','TSU','']
         	>>>re.split(r'[1-9]\d{5}','BLT10081 TSU10084',maxsplit=1)
          ['BLT','TSU10084']
      
    • re.finditer(pattern,string,flags=0))

    • re.sub(pattern,repl,string,count=0,flags=0)

      re.sub(pattern,repl,string,count=0,flags=0)
          #reple:替换匹配字符串的字符串,正则替换
          #count:匹配的最大替换次数
          #其他参数同上
          
          注意:
          1、repl可以是函数,例:
          def test(x):
              y=int(x.group(0))
              Y*=2
              return str(y)
          2、sub内部调用text方法时,会把匹配的数据以re.match格式传参
          print.sub(r'\d+',test,待匹配字符 )
      
    • re.compile(pattern,flags=0)

      	regxe=re.compile(pattern,flags=0)	#将正则表达式的字符串形式编译成正则表达式对象
          例:
          >>>rst=re.search(r'[1-9]\d{5}','BLT 10081')	#函数式用法,一次性操作
          >>>rst=pat.search('BLT 10081')	#面向对象用法:编译后的名次操作
      
5、re库的match对象
  • 定义:match对象是一次匹配结果,包含匹配的很多信息

    >>>match=re.search(r'[1-9]\d{5}','BLT 10081')
    
  • match对象的属性

    	.string		#待匹配文本
        .re			#匹配时使用的patter对象(正则表达式)
        .pos		#正则表达式搜索文本的开始位置
        .endpos		#正则表达式搜索文本的结束位置
        .group(0)	#获得匹配后的字符串,括号内参数为分组序数。分组相关解释放在末尾
        .start()	#匹配字符串在原始字符串的开始位置
        .end()		#匹配字符串在原始字符串的结束位置
        .span()		#返回(.start(),.end())
       
    	补充:
        分组:在正则表达式中,加括号分组
        例:(?p<分组名>正则表达式) #给分组取名,{分组名:匹配值}
        groupdict(分组名)/groupdict()	#获得分组组成的字典,{分组名:匹配值}
        
        示例:
        >>>import re
        >>>m=re.search(r'[1-9]\d{5}','BLT10081 TSU10084')
        >>>m.string/m.endpos·······
    
6、re的贪婪和最小匹配
  • 贪婪匹配

    同时匹配长短不同的多项,re库默认采用贪婪匹配,即输出匹配最长的子串

  • 最小匹配

    最小匹配操作符:

    	*?		#前一个字符0次或无限次拓展,最小匹配
        +?		#前一个字符1次或无限次拓展,最小匹配
        ??		#前一个字符0次或1次拓展,最小拓展
        {m,n}?	#拓展前一个字符m至n次(含n),最小匹配
        
        注意:只要长度输出可能的,都可以通过在操作符后增加?变成最小匹配
    

六、scrapy爬虫框架

1、scrapy介绍
  • 一个快速功能强大的网络爬虫框架(网站级)

  • 爬虫框架是实现爬虫功能的一个软件结构和功能组件集合,是一个半成品,能够帮助用户实现专业网络爬虫

  • scrapy爬虫框架结构(“5+2”结构)

    数据流的三个路径:

    • 第一条路径
      • 1、Engine从spider处获得爬取请求(request)
      • 2、Engine将爬取请求转发给scheduler处获得下一个要爬取的请求
    • 度二条路径
      • 3、Engine从scheduler处获得下一个要爬取的请求
      • 4、Engine将爬取请求通过中间件发送给Downloader
      • 5、爬取网页后,Doownloader形成响应(response)通过中间件发给Engine
      • 6、Engine将收到响应通过中间件发给spider处理
    • 第三条路径
      • 7、spider处理响应后产生爬取项(Scraped Item)和新的爬取请求(requests)给EEngine
      • 8、Engine将爬取项发给Iter pipeline(框架出口)
      • 9、Engine将爬取请求发给Scheduler
  • 爬虫框架结构解析

    • Engine(不需要用户修改)
      • 控制所有模块之间的数据流
      • 根据条件触发事件(不需要用户修改)
    • Downloader(不需要用户修改)
      • 根据请求下载网页
    • Scheduler(不需要用户修改)
      • 对所有爬取请求进行调度管理
    • Downloader MIDDLEWARE(用户可以编写配置代码)
      • 目的:实施Engine、scheduler和Downloader之间进行用户可配置的控制
      • 功能:修改、丢弃、新增请求或响应
    • spider(需要用户编写配置代码)
      • 解析Download返回的响应(response)
      • 产生爬取项(scrapy item)
      • 产生额外的爬取请求(request)
    • Item Pipelines
      • 以流水线路方式处理spider产生的爬取项
      • 由一组操作顺序组成,类似流水线,每个操作是一个Item pipeline类型
      • 可能操作包括:清理、检验和查重爬取项中的HTML数据,将数据存储到数据库,需要用户编写而配置代码
    • Spider Middleware(用户可以编写配置代码)
      • 目的:对请求和爬取项的再处理
      • 功能:修改、丢弃、新增请求或爬取项
2、scrapy爬虫常用命令

scraoy命令行(scrapy -h)

  • scrapy专业爬取框架,提供操作的scrapy命令行。windows下,启动cmd控制台

  • 格式:scrapy <command(scrapy命令)> [options] [args]

  • 常用scrapy命令

    	startproject	#创建一个新工程	 scrapy startproject <name> [dir]
        genspider		#创建一个爬虫		  scrapy genspider [options] <name> <domatin>
        settings		#获得爬虫的配置信息  scrapy settings [options] 
        crawl			#运行一个爬虫		  scrapy crawl <spider>
        list			#列出工程中的所有爬虫	scrapy list
        shell			#启动url调试命令行	  scrapy shell [url]
    
3、scrapy爬虫的第一个实例
例:1、演示html页面地址:http://python123.io/ws/demo.html
		文件名称:demo.html
    
    产生步骤1:应用scrapy爬虫框架主要是编写配置代码
    步骤1:建立一个scrapy爬虫工程
    	选取一个目录(例D:\python\),然后执行如下命令:
        (命令行下)D:\python>scrapy startproject python123demo
    生成的工程目录
结构:   python123demo/			#外层目录
        	scrapy.cfg			  #部署scrapy爬虫的配置文件	
            python123demo/		  #scrapy框架的用户自定义python代码
            	_init_py		  #初始化脚本
                items.py		  #Items代码模块(继承类)
                middlewares.py	  #MiddleWares 代码模块(继承类)
                pipelines.py	  #pipelines代码模块(继承类)
                setting.py		  #scrapy爬虫的配置文件
                spiders/		  #spiders代码模块目录(继承类)
                _pycache_		  #缓存目录,无需修改
                
内层目录:	spiders/			   #spiders代码模块目录(继承类)
		    	_init_.py		  #初始化文件,无需修改
    			_pycache_/		  #缓存目录,无需修改
        注意:用户自定义的spiders代码增加在此处
        
	产生步骤2:
    步骤2:在工程中产生一个scrapy爬虫
    进入工程目录(D:\python\python123demo),然后执行如下命令:
    D:\python\python123demo>scrapy genspider demo python123.io
    #该命令作用:仅用于生成demo.py爬虫文件,该文件可以手工生成
    1、生成一个名称为demo的spiser
    2、在spider目录下增加代码文件demo.py
    3、注意:爬虫文件中
    		def parse(self,response)
        #parse()用于处理响应,解析内容形成字典,发现新的url爬取请求
    
    产生步骤3:
    步骤3:配置产生的spider爬虫
    1、初始化url地址
    2、获取页面的解析方式
    
    产生步骤4:
    步骤4:运行爬虫,获取网页
    在命令行下,执行如下命令:
    D:\python\python123demo>scrapy crawl demo
    #爬虫被执行,捕获页面存储在demo.html
4、yield关键字的应用
  • 概述:

    ​ 包含yield语句的函数是一个生成器,生成器每次处理一个值(yield语句)。函数被冻结,被唤醒后再产生一个值,生成器是一个不断产生值的函数,每调用一次在yield位置产生一个值,直到函数执行结束。

  • 优势

    生成器更节省空间,响应更加迅速,使用更加灵活。

    例:demo.py
    	import scrapy
        class Demospider(scrapy.spider):
            name='demo'
            def atart_request(self):
                urls=['http:·····']
                for url in urls:
                    yield scrapy.Request(url=url,callback=self.parse)
    
5、crapy爬虫的基本使用
  • 步骤:

    • 创建工程和spider模板
    • 编写spider
    • 编写Item pipeline
    • 优化配置策略
  • scrapy爬虫数据类型(Request、Response、Item三大类)

    • Request类

      格式: class Scrapy.http.Request()
          #Request对象表示一个http请求,由spider生成,由Downloader执行
      属性方法:
      	.url		#Request对应的请求url地址
          .method		#对应的请求方法,'GET''POST'等
          .header		#字典类型风格的请求头
          .body		#请求内容主体,字符串类型
          .meta		#用户添加的拓展信息,在scrapy内部模块间传递信息使用
          .copy		#复制该请求
      
    • Rsponse类

      格式: class Scrapy.http.Request()
          #Response对象表示一个http响应,由Downloader上生成,由spider处理
      属性和方法:
      	.url		#Response对应的url地址
          .status		#http状态码,默认是200
          .headers	#Response对应的头部信息
          .body		#Response对应的内容信息,字符串类型
          .flags		#一组标记
          .request	#产生Response类型对应的Request对象
          .copy()		#复制该响应
      
    • Item类

      格式: class Scrapy.item.Item()
          #Item对象表示一个从HTML页面提取的信息内容。由spider生成,由Item pipeline处理
          #Item类似于字典类型,可以按照字典类型操作   
      
  • scrapy爬虫提取信息的方法

    scrapy爬虫支持多种html信息提取方法:

    Beautiful Soup、lxml、re、xpath selector、css selector

  • css selector的基本使用

    <HTML>.css('a::attr(herf)').extract()
    

七、爬虫工具

1、curl

​ windows、linux系统自带工具,命令行启动。

基本使用:

	curl -A		#指定请求表示,设置user_agent.格式:curl -A "chrome" 网址url
	-X			#用指定方法请求。格式:curl -X POST url
	-I			#只返回请求头信息。格式: curl -I url
	-d			#以post方法请求url,并发送相应参数。格式:curl -d <要传参数> url
	-O			#下载文件并以远程文件名保存。格式:curl -O url
	-o			#下载文件且自定义文件名。格式:curl -o <自定义文件名> url
	-L			#跟随重定向请求。格式: curl -IL url
	-H			#设置头信息。格式: curl -H <头信息>
	-k			#允许发起不安全的ssl请求(有的网站未认证)
	-b			#设置cookie。格式: curl -b <自定义cookie> url
	-S			#不显示其他无关信息
	-v			#显示连接过程中所有信息
	
	注意:
	-d可以传文件,可传多个参数:
				-d a=1	-d b=2	-d c=3
				-d*a=1&b=2&c=3
				-d @文件名
				
	例:curl -o image.webp -H 'accept:image/webp' url
	#image.webp:定义文件名
	#accept:image/webp   定义文件格式
	
	示例:linux系统中
		curl url |grep -E "\dt"
		#-E:正则表达式
		#-v:取反
2、wget

​ linux系统自带工具

基本使用:

	-O		#以指定文件名保存下载文件。格式:wget -O <自定义文件名> url
    --limit	#以指定速度下载目标文件。格式:wget --limit,例:--limit-rate=200k 以200k/s速度下载
    -c		#断点续传
    -b		#后台下载
    --mirror #镜像某目标网站
    -p		#下载页面所有相关资源
    -r		#递归下载所有网页中所有的链接
    -log	#日志(显示下载进度)
    -u		#指定user-agent
    	例:
        	镜像下载整个网站保存到本地
            wget -c --mirror -u "mozilla" -p --convert-links url
3、其他工具
  • HTTPie

    linux系统中使用的爬虫工具,使用:http -参数

  • postman

    接口类爬虫工具

  • charles、fiddler

    抓包工具

4、其他爬虫库
  • urllib等

八、xpath语法

​ 基本语法,详情:https://www.w3school.com.cn/xpath/index.asp

1、选取节点

​ XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。

  • 下面列出了最有用的路径表达式:
表达式描述
nodename选取此节点的所有子节点。
/从根节点选取。
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
.选取当前节点。
选取当前节点的父节点。
@选取属性。
  • 实例

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式结果
bookstore选取 bookstore 元素的所有子节点。
/bookstore选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book选取属于 bookstore 的子元素的所有 book 元素。
//book选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang选取名为 lang 的所有属性。
2、谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。

谓语被嵌在方括号中。

  • 实例

​ 在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式结果
/bookstore/book[1]选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()]选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1]选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()❤️]选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang]选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=‘eng’]选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00]选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。
3、选取未知节点
  • XPath 通配符可用来选取未知的 XML 元素。
通配符描述
*匹配任何元素节点。
@*匹配任何属性节点。
node()匹配任何类型的节点。
  • 实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
/bookstore/*选取 bookstore 元素的所有子元素。
//*选取文档中的所有元素。
//title[@*]选取所有带有属性的 title 元素。
4、选取若干路径
  • 通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

  • 实例

​ 在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
//book/title | //book/price选取 book 元素的所有 title 和 price 元素。
//title | //price选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。
5、XPath 轴
  • 轴可定义相对于当前节点的节点集。
轴名称结果
ancestor选取当前节点的所有先辈(父、祖父等)。
ancestor-or-self选取当前节点的所有先辈(父、祖父等)以及当前节点本身。
attribute选取当前节点的所有属性。
child选取当前节点的所有子元素。
descendant选取当前节点的所有后代元素(子、孙等)。
descendant-or-self选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
following选取文档中当前节点的结束标签之后的所有节点。
namespace选取当前节点的所有命名空间节点。
parent选取当前节点的父节点。
preceding选取文档中当前节点的开始标签之前的所有节点。
preceding-sibling选取当前节点之前的所有同级节点。
self选取当前节点。
6、位置路径表达式
  • 位置路径可以是绝对的,也可以是相对的。

​ 绝对路径起始于正斜杠( / ),而相对路径不会这样。在两种情况中,位置路径均包括一个或多个步,每个步均被 斜杠分割:

  • 绝对位置路径:
/step/step/...
  • 相对位置路径:
step/step/...
  • 每个步均根据当前节点集之中的节点来进行计算。
7、步(step)
  • 相关概念

    • 轴(axis)

      定义所选节点与当前节点之间的树关系

    • 节点测试(node-test)

      识别某个轴内部的节点

    • 零个或者更多谓语(predicate)

​ 更深入地提炼所选的节点集

  • 步的语法:
轴名称::节点测试[谓语]
  • 实例
例子结果
child::book选取所有属于当前节点的子元素的 book 节点。
attribute::lang选取当前节点的 lang 属性。
child:😗选取当前节点的所有子元素。
attribute:😗选取当前节点的所有属性。
child::text()选取当前节点的所有文本子节点。
child::node()选取当前节点的所有子节点。
descendant::book选取当前节点的所有 book 后代。
ancestor::book选择当前节点的所有 book 先辈。
ancestor-or-self::book选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点)
child:😗/child::price选取当前节点的所有 price 孙节点。

第三方库

一、第三方库介绍

1、数据分析
  • Numpy:表达N维数组的最基础库
    • python接口使用,c语言实现、计算速度优异
    • python数据分析及科学计算的基础库,支持pandas等
  • pandas:python数据分析高层次应用库
    • 提供了简单易用的数据结构和数据分析工具,基于Numpy开发
    • 理解数据库类型与索引的关系,操作索引即操作数据,python最主要数据分析功能库
    • series=索引+一维数据。DataFrame=行列索引+二维数据
  • scipy:数学、科学和工程计算功能库
    • 提供了一批数学算法及工程数据运算功能,python最主要科学计算功能库,基于Numpy开发
    • 类似于Matlab,用于如傅里叶变换、信号处理等应用
2、数据可视化
  • Matplotlib:高质量的二维数据可视化功能库

    • 提供超过100种数据可视化展示效果,调用matplotib.python字库,可用各种可视化效果
  • seaborn:统计类数据可视化功能库

    • 主要用于展示数据间分布、分类和线性关系等内容,提供高层次的统计类数据可视化展示效果。
  • Mayavi:三维数据可视化功能库(3D效果)

3、文本处理
  • PYPDF2:用来处理pdf文件的工具集
  • NlTK:自然语言文本处理第三方库
  • python-docx:创建或更新Microsoft word文件的第三方库
4、机器学习
  • scikit—learn:机器学习方法工具集
    • 统一化机器学习方法功能接口,提供聚集、分类、回归、强化学习等计算功能。最优秀最基本的机器学习第三方库,与数据处理相关。
  • TensorFlow:AlphaGo背后的机器学习计算框架
    • 谷歌公司开源机器学习框架,将数据流图作为基础,图节点代表运算,边代表张量
  • MXNet:基于神经网络的深度学习计算框架
5、网络爬虫
  • requests:最友好的网络爬虫功能库
    • 提供类http协议的网络爬虫功能、支持连接池、ssl、cookie、http(s)代理
  • scrapy:优秀的网络爬虫框架,python数据分析高层次应用
    • 提供构建爬虫系统框架功能、功能半成品,支持批量和定时网页抓取、数据处理流程。
  • pyspider:强大的web页面爬取系统
    • 提供完整的网页爬取系统构建功能,支持数据库后端、消息队列、优先级、分布式架构等
6、web信息提取
  • Beautiful Soup:HTML和XML的解析库
  • re:正则表达式解析和处理功能库
  • python—Goose:提取文章类型web页里功能库
7、web网站开发
  • Django:最流行的web应用框架
    • 提供构建web基本框架、MTV模式:模型(model)、模板(template)、视图(views)
  • pyramid:规模适中的web应用框架
  • Flask:web应用开发微框架(简单、规模小、快速)
8、网站应用开发
  • weRoBot:微信公众号开发框架
    • 提供解析微信服务器消息及反馈消息功能
  • api:百度AI开放平台接口
  • MYQR:二维码生成第三方库
9、图形化用户界面
  • pyQt5:Qt开发框架的python接口
    • 提供创建Qt5程序python api接口,Qt跨平台桌面应用开发系统,完备GUI
  • wxpython:跨平台GUI开发框架
  • pyGobiect:使用GTK+GUI的功能库
10、游戏开发
  • pyGame:简单游戏开发功能库
  • panda 3D:开源、跨平台的3D渲染和游戏开发库
  • cocos2d:构建2D游戏开和图形化界面交互式应用的框架
11、虚拟现实
  • VR Zero:在树莓派上开发VR应用的python库
  • pyovr:oculus Rift的python开发接口
  • vi2ard:基于python的通用VR开发引擎
12、图形艺术
  • Quads:迭代的艺术(对图像进行四分迭代,形成像素风)
  • ascii_art:Ascll艺术库
  • turtle:海龟绘图体系

二、python第三方库安装

1、python社区

​ https://pypi.org

  • pypi:python package index
  • psf维护的展示全球python计算生态的主站
  • 学会检索并利用pypi,找到合适的第三方库开发程序
2、python第三方库安装
  • 使用pip安装工具(命令行工具)

    	pip install <第三方库名>		#安装指定第三方库
        pip install -u <第三方库名> 	#更新已安装第三方库
        pip uninstall <第三方库名>	 #卸载指定第三方库
        pip download <第三方库名>	#下载但不安装指定第三方库
        pip show <第三方库名>		#列出某个指定第三方库的详细信息
        pip search <关键词>		 #根据关键词在名称和介绍中搜索第三方库
        pip list				   #列出当前系统已安装得到第三方库,等价于:pip free2e
    
  • 集成安装

    ​ 结合特定的python开发工具的批量安装

    ​ Anaconda,支持近800个第三方库,包含多个主流工具,适合数据计算领域开发

  • 文件安装

    某些第三方库pip下载后,需要编译后在安装

    如果操作系统没有编译环境,则只能下载不能安装

    解决方案:搜索第三方库名,下载对应版本文件,使用pip install <文件名>安装

三、OS库

1、概述
  • 提供通用的、基本的操作系统交互功能
  • os库是python标注库,包含几百个函数
  • 常用于路径操作、进程管理、环境参数等几类

​ 路径操作:os.path子库,处理文件路径及信息

​ 进程管理:启动系统中其他程序

​ 环境参数:获得系统软硬件信息等环境参数

2、os库操作
  • 路径操作

    • os.path子库以path为入口,用于操作和处理文本路径

      import os.path 或 import os.path as op

    • 操作函数

      	os.path.abspath(path)		#返回path在当前系统中的绝对路径
          例:>>>os.path.abspath('file.txt')
          	'C:\\users\python\file.txt'
          
          os.path..normpath(path)		#归一化path的表现形式,统一用\\分隔路径
          例:>>>os.path.normpath('D://PYE//file.txt')
          	'D:\\PYE\\file.txt'
              
          os.path.relpath(path)		#返回当前程序与文件的相对路径(relative path)
          例:>>>os.path.relpath('C://PYE//file.txt')
          	'····\\····\\····\\····\\PYE\\file.txt'
              
          os.path.dimame(path)		#返回path中的目录名称
          例:>>>os.path.dirname('D://PYE//file.txt')
          	'D:PYE'
              
          os.path.basename(path)		#返回path中最后的文件名称
          例:>>>os.path.basename('D://PYE//file.txt')
          	'filee.txt'
              
          os.path.join(path,*paths)	#组合path与paths,返回一个路径字符串
          例:>>>os.path.join('D:/','PYE/fiel.txt')
          	'D:/PYEE/file.txt'
              
          os.path.exists(path)		#判断path对应文件目录是否存在,返回True或False
          os.path.isfile(path)		#判断path对应是否已存在的文件,返回True或False
          os.oath.isdir(path)			#判断path所对应的是否为已存在的目录,返回True或False
          os.path.getmtime(path)		#返回path对应文件或目录最近一次修改时间
          os.path.getatime(path)		#返回path对应文件或目录上一次访问时间
          os.path.getctime(path)		#返回path对应文件或目录的创建时间
          os.path.getsize(path)		#返回path对应文件的大小,以字节为单位
      
  • 进程操作

    	os.system(command)		#执行程序或命令command,在windows中返回值为cmd的调用返回信息
        例:
        import os
        os.system('C:\\windows\\system32\\calc.exe')
        >>>
        0
    
  • 环境参数

    	os.chdir(path)		#修改当前程序操作的路径
        os.getcwd()			#返回程序的当前路径
        os.getlogin()		#获得当前系统登录用户名称
        os.cpu_count()		#获得当前系统的cpu数量
        os.urandom()		#获得n个字节长度的随机字符串,通常用于加密运算
    

四、pyinstaller库

1、概述

​ 将.py源代码转换成无需源代码的可执行环境

​ .py文件 ——> pyinstaller ——> windows(exe文件)

2、pyinstaller库的安装

​ cmd命令行下:pip install pyinstaller

3、pyinstaller库的使用
	命令行下: pyinstaller -F <文件名.py>  #将py文件转化为exe文件
	-h				#查看帮助
	--clear			#清理打包过程中的临时文件
	-D,--onedir		#默认值,生成dist文件夹
    -F,--onefile	#在dist文件夹中只生成独立的打包文件
    -i <图标文件名.ico>	#指定打包程序使用的图标(ico)文件
    
    例:pyinstaller -i <文件名>.ico -F <文件名>.py

五、random库的使用

1、概念
  • random库是使用随机数的python标准库
  • 伪随机数:采用梅森旋转算法生成的(伪)随机数序列中的元素
  • random库主要用于生成随机数
2、基本随机数函数
  • 随机数种子

    ​ 随机数种子 ——> 梅森旋转算法——> 随机序列

  • 基本随机数函数

    	seed(a=None)		#初始化给定的随机数种子,默认为系统时间
        random()			#生成一个[0.0,1.0]之间的随机小数
        
        例:
        >>>random.seed(10)	#产生种子10对应序列
        >>>random.random()	#0.5714025946899135
    
3、拓展随机函数
	random(a,b)			#生成一个[a,b]之间的整数
    randrange(m,n[,k])	#生成[m,n]之间以k为步长的随机整数
    getrandbits(k)		#生成一个k比特长的随机整数
    uniform(a,b)		#生成[a,b]之间的随机小数
    choice(seq)			#从序列seq中随机选择一个元素
    shuffle(seq)		#将序列seq中元素随机排列,返回打乱后的序列
    
    例:
    random.random(10,100)	==>64
    random,randrange(10,100,10)	===>80
    random。getrandbits(16)	===>37885
    random.uniform(10,100)	===>13.096321648808136
    random.choice([1,2,3,4,5])	===>2
    s=[1,2,3,4,5];random.shuffle(s);print(s) ==> [3,5,1,4,2]

六、time库

​ calendar 日历库

1、时间获取
	time()			#获取当前时间戳,即计算机内部时间值,浮点数(time.time())
    ctime()			#获取当前时间并以易读的方式表示,返回字符串
    gmtime()		#获取当前时间,表示为计算机可处理的时间格式
    localtime()		#类似于gmtime(),但它将秒数转换为本地时间>>>time.time()	==> 1658653428.881543
    >>>time.ctime()	==> Sun Jul 24 17:04:51 2022
    >>>time.gmtime() ==>
    time.struct_time(tm_year=2022, tm_mon=7, tm_mday=24, tm_hour=9, tm_min=5, 		       tm_sec=51, tm_wday=6, tm_yday=205, tm_isdst=0)
    >>>time.localtime() ==>如果加参数结果与gmtime()不同
    time.struct_time(tm_year=2022, tm_mon=7, tm_mday=24, tm_hour=17, tm_min=7,          	tm_sec=52, tm_wday=6, tm_yday=205, tm_isdst=0)
2、时间格式化
  • 格式化函数

    	time.ASCtime()		#格式化为可读格式
        strftime(tpl,ts)	#tpl是格式化模板字符串,用来定义输出效果。ts是计算机内部时间类型变量
        strptime(str,tpl)	#str是字符串形式的时间值,tpl是格式化模板字符串,用来定义输入效果
        
        例:
        >>>t=time.gmtime()
        >>>time.strftime('%Y-%m-%d %H:%M:%S',t)
        	'2022-07-24 09:15:48'
            
        >>>timestr='2022-07-24 09:15:48'
        >>>time.strptime(timestr,'%Y-%m-%d %H:%M:%S')
        time.struct_time(tm_year=2022, tm_mon=7, tm_mday=24, tm_hour=9, tm_min=15,  	tm_sec=48, tm_wday=6, tm_yday=205, tm_isdst=-1)
    
  • 格式化控制符

%Y年份0000~9999 例:1900
%m月份01~12 例:10
%B月份名称January~December 例:April
%b月份名称缩写Jan~Dec 例:Apr
%d日期01~31 例:25
%A星期Monday~sunday 例:wednesday
%a星期缩写Mon~Sun 例:Wed
%H小时(24h制)00~23 例:12
%I小时(12h制)01~12 例:7
%P上/下午AM,PM 例:PM
%M00~59 例:36
%S00~59 例:26
3、程序计时
	perf_counter()	#返回一个cpu级别的精确时间计数值,单位为秒。
    				#由于这个计数值起点不确定,连续调用差值才有意义
    sleep(s)		#s拟休眠时间,单位是秒,可以是浮点数
    
    例:
    >>>start=time.perf_counter()	==> 194087.2030045
    >>>end=time.perf_counter()		==> 194108.093826
    >>>end-start					==>20.89082149998285
    
    >>>def wait():
           time.sleep(3.3)
    >>>wait()	#程序将等待3.3秒后再退出

七、turtle库

1、turtle窗体大小及处理
	turtle.setup(width,height,startx,starty) #画布的宽度、高度,窗体位置坐标(以屏幕左上参考)
    注意:setup()不是必须的
2、绘图命令
  • 画笔运动命令

    	forward()		#简写fd(),向当前方向前进
        backward()		#bk(),后退
        right()			#rt(),右转多少度
        left()			#lt(),左转多少度
        goto(x,y)		#画笔移动到坐标x,y位置
        penup()			#up(),提起画笔
        pendown()		#down(),放下画笔
        circle()		#画圆,半径为正,圆心在画笔左侧,反之右侧
        speed()			#画笔速度[0,10]整数
    
  • 画笔控制命令

    	pensize()				#画笔宽度
        pencolor()				#画笔颜色
        fillcolor()				#填充颜色
        color(color1,color2)	#pencolor=color1,fillcolor=color2
        filling()				#返回当前是否填充
        begin_fill()			#开始填充
        end_fill()				#填充完成
        hideturtle()			#隐藏画笔
        showturtle()			#显示画笔
        bgcolor()				#背景颜色
        bgpic('1.gif')			#背景图片
        clear()					#清空窗口,turtle位置不变
        resec()					#清空窗口,turtle起始状态
        undo()					#撤销一个turtle动作
        isvisible()				#返回当前turtle是否可见
        stamp()					#复制当前图形
        write()					#写文本
        dot()					#以参数为直径画点
        setpos(x,y)				#设置笔坐标
        position()				#笔当前坐标
    

八、jieba库

1、概述
  • 中文文本需要通过分词获得单个的词语
  • jieba是优秀的中文分词第三方库,需要额外安装
  • jieba库提供三种分词模式,最简单只需要掌握一个函数
2、jieba分词原理
  • jieba分词利用一个中文词库,确定中文字符之间的关联概率
  • 中文字符间概率大的组成词组,形成分词结果
  • 除了分词外,用户还可以添加自定义的词组
3、jieba库使用说明
  • jieba库分词的三种模式

    • 精确模式:把文本精确切分开,不存在冗余单词
    • 全模式:把文本所有可能的词语都扫描出来,有冗余
    • 搜索引擎模式:在精确模式的基础上,对长词再次切分
  • jieba库常用函数

    	jieba.lcut(s)					#精确模式,返回一个列表类型的分词结果
        jieba.lcut(s,cut_all=True)		#全模式,返回一个列表类型的分词结果,存在冗余
        jieba.lcut_for_search(s)		#搜索引擎模式,返回一个列表类型的分词结果,存在冗余
        jieba.add_word(w)				#向分词词典增加新词w
        
        例:
        >>>jieba.lcut('中国是一个伟大的国家')
        ['中国','是','一个','伟大','的','国家']
        >>>jieba.lcut('中国是一个伟大的国家',cut_all=True)
        ['中国','国是','一个','伟大','的','国家']
        >>>jieba.lcut_for_search('中华人民共和国是伟大的')
        ['中华','华人','人民','共和','共和国','中华人民共和国','是','伟大','的']
    

九、wordcloud库

1、概述

​ 词云以词语为基本单位,更加直观和艺术地展示文本

2、wordcloud库基本使用
  • wordcloud库把词云当作一个wordcloud对象
  • wordcloud.wordcloud()代表一个文本对应的词云
  • 可以根据文本中的词语出现的频率等参数绘制词云
  • 词云的绘制形状、尺寸和颜色都可以设定
3、wordcloud库常规方法
  • WordCloud对象

    	w=wordcloud.WordCloud()		#以WordCloud对象为基础,配置参数,加载文本,输出文件
        w.generate(txt)				#向wrdcloud对象W中加载文本txt
        w.to_file(filename)			#将词云输出为图像文件,png或jpg格式
        
        例:
        import wordcloud
        c=wordcloud.WordCloud()					#步骤1:配置对象参数
        c.generate('wordcloud by python')		#步骤2:加载词云文本
        c.to_file(pywordcloud.png)				#步骤3:输出词云文件	
    
  • 配置对象参数

	width			#指定词云对象生成图片的宽度,默认400像素
    height			#指定词云对象生成图片的高度,默认200像素
    min_font_size	#指定词云中字体的最小字号,默认4号
    max_font_size	#指定词云中字体的最大字号,根据高度自动调节
    font_step		#指定词云中字体字号的步进间隔,默认为1
    font_path		#指定字体文件的路径,默认为None
    max_words		#指定词云显示的的最大单词数量,默认200
    step_words		#指定词云的排除词列表,即不显示的单词列表
    background_color#指定词云的背景颜色,默认为黑色
    mask			#指定词云形状,默认为长方形,需要引用imread()函数
    
    注意:mask的使用
    >>>from scipy.misc import imread
    >>>mk=imread('pic.png')		#图片形状即为词云形状
    >>>w=wordcloud.WordCloud(mask=mk)
    例:
    w=wordcloud.WordCloud(width=600,min_font_size=7,backgroud_color='white')
4、wordcloud应用demo
	demo 1
    import wordcloud
    txt='life is short,you need python'
    w=wordcloud.WordCloud(background_color='white')
    w.generate(txt)
    w.to_file('pywordcloud.png')
    
    demo2
    import jieba
    import wordcloud
    txt="程序··········"
    w=wordcloud.WordCloud(width=1000,font_path="msyh.ttc",hight=700)
    w.generate(" ".join(jieba.lcut(w.generate(txt))))	#用空格分隔词
     w.to_file('pywordcloud.png')
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python笔记.md 是一个用于记录Python编程相关内容的markdown文档。 在Python学习过程中,学习者通常会遇到各种问题和疑惑,需要有一个地方来记录学习笔记和重要概念,以方便日后复习和查阅。Python笔记.md 就是一个很好的选择。 Python笔记.md 可以按照自己的需要来组织内容,比如可以分为不同的章节或主题,并使用markdown语法来格式化文档,使其更加清晰易读。 在Python笔记.md中,可以记录Python的基础语法、常用数据结构、函数、类、模块等内容。此外,还可以记录一些常见的错误和解决方法,以便日后遇到类似问题时能够快速找到解决方案。 Python笔记.md 还可以用来记录自己的思考和理解。在学习过程中,我们常常会思考某个概念或代码背后的原理,这时候可以将自己的思考记录在笔记中,以便后续复习和回顾。 使用Python笔记.md 还可以方便与他人分享学习心得。可以在文档中加入注释或标题,使得文档更加易读和友好。同时,也可以将Python笔记.md 推送到版本控制系统中,与他人共享和共同编辑。 总之,Python笔记.md 是一个非常有用的工具,可以帮助学习者系统地记录、整理和复习Python编程相关的知识和经验。无论是初学者还是有经验的开发者,都可以从中受益,并提高自己的编程技能。 ### 回答2: Python笔记.md是一个使用Markdown语法编写的Python笔记文档。Markdown语法是一种轻量级的标记语言,可以快速地编辑和排版文档。 在Python笔记.md中,可以记录Python程序设计的相关知识、概念和技巧。通过使用Markdown语法,可以方便地插入代码块、链接、图片以及其他强调和排版格式,使得笔记更加直观和易读。 Python笔记.md可以按照不同的章节和主题组织内容,方便快速查找和阅读。在每个章节中,可以记录不同的Python编程概念,如数据类型、控制结构、函数、类等。可以通过示例代码和解释说明来详细解释这些概念的用法和特点。 在笔记中,还可以记录一些Python的常见问题和解决方案,例如常见错误、调试技巧等。这些内容可以帮助初学者更好地理解和掌握Python语言。 此外,Python笔记.md还可以连接到其他的Python资源,如官方文档、教程、在线代码编辑器等。这样可以提供更多的学习和参考资料。 总之,Python笔记.md是一个有条理、易读和方便编辑的Python学习笔记文档,可以帮助人们更好地学习和理解Python编程语言。 ### 回答3: Python笔记md是一种用来记录Python编程语言相关内容的文本文件格式。它使用Markdown语法来快速、简洁地编写和格式化笔记Python笔记md的优点是: 1. 简单易懂:Markdown语法简洁明了,使用起来非常简单,即便没有编程背景的人也能快速上手。 2. 跨平台兼容:无论是在Windows、Mac还是Linux系统中,Python笔记md都可以轻松使用。 3. 可读性强:Python笔记md的文本格式使得代码和说明可以同时显示,方便读者理解和学习。 4. 方便分享和发布:Python笔记md可以导出为HTML或PDF格式,方便分享给其他人或者发布到网络上。 5. 与开发工具兼容:大多数集成开发环境(IDE)和文本编辑器都支持Markdown语法,可以实时预览和编辑笔记。 使用Python笔记md可以帮助程序员记录代码和相关的解释和说明,方便复习和查看。它还可以用于编写技术博客、文档和教育材料等。而且由于其文本格式的特点,Python笔记md也非常适合使用版本控制系统进行版本管理。 总而言之,Python笔记md是一种简单、灵活且易于分享的笔记格式,可以有效提高编程学习和开发的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值