Python学习笔记文档

在计算机命令中中括号代表括号里的内容是可选项 可以有也可以没有

PEP8:Python代码风格指南

PEP8提供了Python代码的编写约定,旨在提高代码的可读性,并使其在各种Python代码中编写风格保持一致

1.缩进使用4个空格,空格是首选的缩进方式但是其实还是用制表符缩进比较多,python3不允许混合使用制表符合空格来缩进

2.每一行最大长度限制在79个字符以内

3.顶层函数,类的定义,前后使用两个空行隔开

4.import的导入

1.建议一个模块导入占一行

例如 :import os

import sys

不建议如下导包

import os,sys

但如下情况可以这样导入

from 包名 import 模块1,模块2

2.导入包一般位于文件的顶部,在模块注释,文档字符串之后,全局变量常量之前(但Python允许任何一次模块使用前进行导入模块, 只不过能一开始就导入就建议一开始导入罢了),导入顺序按照以下顺序分组

标准库导入

相关第三方导入

本地应用/库导入

在每一组导入之间加入空行

5.Python中定义字符串使用双引号单引号都是相同的,尽量保持使用同一方式定义字符串,当一个字符串包含单引号和双引号时,在最外 层使用不同的符号来避免使用反斜杠转义(也就是嵌套字符串的时候需要避免),从而提高可读性

6.表达式和语句中的空格:

1.避免在小括号,方括号 花括号后跟空格

2.避免在逗号 分号 冒号之前添加空格

3.冒号在切片中就像二元运算符,两边要有相同数量的空格,如果某个切片参数省略,空格也省略

4.避免为了和另一个赋值语句对齐,而在赋值运算符附加多个空格

5.避免在表达式尾部添加空格,因为尾部空格通常会看不见,容易产生混乱

6.总是在二元运算符两边加一个空格 例如 = 两边 等等

7.避免将小的代码块和if/for/while放在同一行,要避免代码行太长

8.永远不要单独使用字符'i'即小写的L,’o‘小写的O或者‘一些格式大写的L 来作为单字符变量名,在有些字体例,这些字符无法与数组0和 1区分。

9.类名一般使用首字母大写的约定 即大驼峰命名法

10.函数名应该小写,如果相提高可读性,可以使用下划线进行分隔

11.如果函数的参数名和已有的关键词冲突,通过在最后加单一下滑线比缩写或随意拼写更好,因此class_比clss要更好(最好用同义词来避 免这种冲突其实)

Python中“ ” 和‘ ’ 无差别 单引号和双引号不区分 但是需要成对使用

Python中缩进是最重要的

编辑代码结尾不用加;

中文编码声明注释:在文件的开头加上中文声明注释,用以指定源码文件的编码格式 #coding:gbk/utf-8 等等 在python2中需要注释说明一下 python中可以省略

####

####

alt+n 复制上一条语句

alt+p 退回到上一条语句

python中的注释方法

1.单行注释

python中用#进行单行注释 为了规范 #后尽量加一个空格

2.多行注释

用一对‘’‘ 三引号开头是多行注释

python中标识符命名规则

表示符由数字 字母 下划线混合组成 ,但是不能以数字开头且标识符命名区分大小写

注意:在定义标识符时,不要使用关键字作为标识符名,也不要使用程序定义的功能函数的名字,会改变原来函数的功能导致程序出错

驼峰命名法

python一般是类名采用大驼峰命名 其他会采用下划线模式命名

大驼峰命名规则:每一个单子的首字母都采用大写字母,例如FirstName

小驼峰命名规则:第一个单词以小写字母开始,第二个之后的单词第一个字母都用大写 例:myName

还有一种命名是用下划线来连接所有单词,例:send_but

python中的变量

python中 不用专门定义变量 不用 int a之类的 直接 声明一下 a=10这样就可以了

变量名可以为中文但是不建议这样定义

python中的数据类型

不可变类型:

基础数据类型:

数字类型: int long float complex(复数)

布尔类型: True False

字符串类型:String

元组:Tuple

可变类型:

列表类型:list

集合类型:set

字典:dictionary

自定义类型:

类:class

通过type函数来查看变量的数据类型

print(type(变量名/数据))

转义字符

\ 转义符号 不可以放在末尾

\n:换行 n:newline 换行

\r:回车 r:return 回车

\t:水平制表符 一个tab的距离哦

\b:退格 b:backspace

输出原始字符串方法:在代码前面加r /R可以用来输出原始字符串

例:print(r“D:”)

#####

input输入函数

temp(变量名)=input(“ 提示输入信息”) 显示括号内内容并接收用户输入内容

input接收到的内容都会以字符串形式返回 而字符串类型的数据是无法直接进行数据计算的哦

print函数

这个函数默认 会在代码结尾加一个隐藏的\n 可以通过 print("内容 ",end=‘ ’)来改变默认的结束符号

在python中 true和false是可以直接自行转换成整数类型进行计算的但是不是很推荐使用计算

True = 1

false = 0

与java不同 不同数据类型进行链接输出的时候 需要进行主动的数据类型转换后才可以输出

print(type(a))查看该数据类型 type(变量)

字符数据类型的转换语法

a=int(a) 将str类型转换成int类型时,字符串必须为数字串(必须为整数不能为小数)

a=str(a)

a=float(a)

a=complex(a) 转换为复数

char(整数) 会转换为整数对应的字符

ord() 会将字符转为对应的ascii码的值

由于浮点数存储不精准的问题,浮点数进行计算的时候可能会导致最终计算结果不准确的问题 因此需要导入模块decimal

from decimal import Decimal

print(Decimal('1.1')+Decimal('2.2')) 中间的单引号尽量不要省略

随机数的引用

import random 引入random模块

random.randint(x,y) x:最小值 y:最大值

Python运算符

*两个数相乘或者返回一个被重复若干次的字符串

/ python中 / 代表的是正常除法 例:11/2=5.5

//(地板除(整除)的结果是取比目标结果最小的整数)例:-9//4=-3

abs(x) 求x的绝对值

* 幂运算 例子:23 二的三次方*

% 取余 余数=被除数-除数*商

pow(2,-3) 也是二的三次方

pow(2,3,5)是23%5**

关于+= -= *= /= 的一些需要注意的点

a+=20 --- > a=a+20 参数赋值 等效展开但并不展开

如 a=b+5 实际进行的运算 是 a=(b+5)a

赋值运算符

a=b=c=d=10 链式赋值

a+=20 --- > a=a+20 参数赋值

a,b,c=20,30,40 ---> a=20 b=30 c=40 解析包赋值

应用:

将x值传给y将y值传给x的快捷写法

x,y=y,x

比较运算符

== 比较的是两个对象的值(value)

is 比较的是两个对象的标识(ID) 看变量内容的标识方式 print(id(变量))

布尔(逻辑)运算符

and 是且的意思

or是或的意思

not 是非 取反的意思

in 某内容是否在某中 x=‘hello’ print(‘o’ in x)

not in 某内容是否不在某中

Python中任何对象都能直接进行真值测试(测试该对象的布尔类型值为true或者false),用于if或者while语句的条件判断,也可以作为布尔逻辑运算的操作数

以上都是短路逻辑

短路逻辑的核心思想:从左往右,只有当第一个操作数的值无法确定逻辑运算结果时,才对第二个操作数进行求值

运算符的优先级

加减乘除高于比较 比较高于not>and>or

continue 跳出本次循环 回到循环体开头进行继续判断

8位(bit)>1(字节)bye>

列出python中的保留字的编码

import keyword
print(keyword.kwlist)

print(type(a))查看该数据类型

二进制 :0b/0B

八进制:0O/0o

十六进制:0X/0x

分支结构 if else elif

if-else的用法格式

if 条件:

满足条件将执行的代码

else:

不满足条件时执行的代码

if -elif-else的用法格式 else if 在python 中 用 elif 来表达

if 条件1:

满足条件1执行的代码

elif 条件2:

满足条件2执行的代码

elif 条件n:

........

else:

所有条件都不满足时将执行的代码

在python中 判断条件中可以用 80<=x<=90 这种写法

条件表达式的语法 :( 条件判断为true的输出语句 if 条件 else 条件判断为false的输出语句 )

if语句的三目运算语法

值1 if 条件 else 值2

如果条件成立 执行值1 如果不成立执行值2

pass语句:什么都不做,只是一个占位符,用到需要写语句的地方使用 防止报错

循环结构

循环的四要素:

1循环变量的初始值

2.循环条件

3.循环体代码

4.循环变量迭代代码

while循环

while 条件表达式:

循环体代码

改变循环变量的迭代语句

for in循环

in表达从(字符串、序列等)中依次取值,又称遍历

for in 遍历的对象必须是可迭代对象

for循环搭配内置函数range()

range(stop)默认从0开始,默认步长为1 到stop的值结束 不包括这个值

range(start,stop)从start的值开始 ,默认步长是1到stop的值结束 不包括这个值

range(start,stop,step)从start的值开始 ,步长step的值 ,到stop的值结束 不包括这个值

in 某内容是否在某中 x=‘hello’ print(‘o’ in x)

not in 某内容是否不在某中

for-in的语法结构

for 自定义的变量in 可迭代对象:

循环体

例:

for i in range(10):

print(i)

如果在循环体中不需要使用到自定义变量,可将自定义变量写为 _

流程控制语句 break :结束最近的一层循环,不管还剩多少次循环

流程控制语句 continue :结束本轮次循环

关于python中else的搭配和作用

1.与if进行搭配时 if条件表达式不成立时执行else中的语句
2.与 while 以及 for进行搭配时候,循环结束过程中没有碰到break时执行else中的语句就是说都是在执行完循环正常结束后执行else后面的代码

组包和拆包

组包:将多个值同时赋给一个变量时,解释器会进行自动组包操作

a=1,2,3,4,5 其实就是创建了一个 a元组 里面有 这些数据

拆包:将一个容器值(元组),里面的多个数据同时赋值多个变量,解释器会进行拆包操作

a1,a2,a3,a4,a5= a 实际进行操作是 把a元组中的数据分别赋值给 前面的变量

这个a无论是元组数据还是列表数据 或者是字符串数据,字典都是可以的 不过字典赋值出来的只是key值

注意:拆包要注意被赋值变量的个数和元组中的值个数相同

a,b=1,2

实际操作就是 将1赋值给a 将2赋值给b

列表

列表是一种可变类型

列表的创建

创建列表的第一种方式

列表名=[元素1,元素2......]

lst1=【‘hello’,‘world’】

创建列表的第二种方式

列表名=list([元素1,元素2......])

lst2=list(【‘hello’,‘world’】)

列表索引 从左往右是 0 1 2 3 ...... 从右往左也可以是 -1 -2 -3 ......

例: 索引 : 0 1 2 3 4

数据: ‘a’ ‘b’ 'c' 'd' 'e'

索引: -5 -4 -3 -2 -1

获取数组中某个数据的索引: print (lst1.index(‘a’))如果列表中有相同元素,只返回相同元素中第一个元素的索引
也可从指定的索引范围内进行查找

例 :print(lst1.index('a',1,4)) 从索引为1开始查 到索引为4的但不包括4

获取列表中的单个元素的方式

正向索引从0--N-1 例 lst1【2】

逆向索引 从-N---1 例 lst1【-N】

若指定索引不存在,抛出indexError
获取列表元素的多个元素

语法格式:列表名【start:stop:step】区间还是左开右闭

列表元素的遍历方法

列表可迭代对象,所以可以用for..in进行遍历

方法一: for 变量名 in 列表名:

print(变量名)

方法二:循环配合下标的方式

for i in range(len(s)):

print(s[i])

i=0

while i <len(i):

print(s[i])

i=i+1

列表中嵌套元组/列表的遍历

isinstance() 判断参数一是否是参数二的类型

通过isinstance判断遍历的元素是否是一个元组 如果是就继续遍历 输入不是就是直接输出

例:

t=[1,2,[4,5],(2.1,5)]

for v in t:

if isinstance(v,tuple) or isinstance(v,list):

for v2 in v:

print(v2)

else:

print(v)

列表原素的修改

列表名【索引】=元素内容 一次修改一个值

列表名【start:stop】=【内容1,内容2,........】 一次修改多个值

通过循环遍历实现字符串的逆序

例:

s='abcde'

s1=' '

i=len(s)-1

while i>=0:

s1=s1+s[i]

i=i-1

print(s1)

通过sort和reverse列表元素的排序操作
方法一:调用列表对象的sort方法,升序排序 是在原列表上进行排序 (不会改变ID值!!!)

例 列表名.sort()

通过指定关键字的参数,将列表中的元素进行降序排列

例: 列表名.sort(reverse=True(必须大写这个))

reverse不进行指定时默认为false

用sort按照自己的要求进行排序

默认sort方法是不能对字典等内容进行比较排序的 需要设置key 结合lambda进行设置

例 按照字典中id的升序进行排序

list1=[{'id':1,'name':'tom'},{'id':3,'name':'rose'},{'id':2,'name':'jack'}]

list1.sort(key=lambda dic:dic['id'])

如果是降序就是list1.sort(key=lambda dic:dic['id'],reverse=True)

关于reverse()逆序的用法

该方法时直接将原列表中的顺序进行逆转

例:

s=[2,5,1,3]

s.reverse()

那么s将会变成[3,1,5,2]

方法二:使用内置函数sorted()对列表进行排序,将产生一个新的列表对象 当然会改变ID值
升序 例:

新列表名=sorted(列表名)

降序 例:

新列表名=sorted(列表名,reverser=True(必须大写))

列表元素的增加操作

并不会改变列表的id 只是在原内存空间内添加数据

列表名.append(内容)在列表的末尾添加一个元素 无论多少个数据都会作为一个元素添加到原列表的末尾

列表名.extend(内容)在列表的末尾至少添加一个元素 会把每一个数据都作为一个元素添加到原数据的末尾

列表名.insert(索引值,内容)在列表的任意索引位置添加一个元素,原位置和其后的元素依次向后移动, 无论多少个数据都会作为一个元素添加 在插入数据时没有下标越界问题,如果指定下标超过正常范围,相当于追加

切片 原列表名【索引值:】=添加的列表名 添加的列表中的元素将从原列表的索引处开始覆盖 未覆盖位置不变

列表的查找操作

统计列表中某个元素的个数 :列表名.count(元素内容)

查询某元素是否在列表中: 元素内容 in 列表名 not in 同理

列表元素的删除操作 并不会改变列表的id 只是在原内存空间内添加数据(除了特殊情况)

列表名.remove(元素内容) 一次删除一个元素 重复元素只删除第一个 元素不存在则抛出ValueError

列表名.pop(索引值) 删除一个指定索引位置上的元素 指定元素不存在则抛出 IndexError 不指定索引则删除列表中最后一个元素

del 列表名[索引值] /del (列表名[索引值]) 删除一个指定索引位置上的元素

del 列表名/del(列表名) 删除列表

切片删除 新列表名=原列表名【start:stop】 将会复制原列表中从索引start到索引stop(不包括stop)到一个新的列表中 但是会产生一个新列表当然新列表的ID也是新的

不产生新的序列对象 而是删除原列表中的内容的方法

lst1【start:stop】=【】

列表名.clear()清空列表

注意!!!:不要在循环遍历时进行删除元素 因为每遍历删除一个元素 后一个元素索引就会变成原前一个元素的索引 那么在删除下一个索引的时候删除的其实是原来的下下一个元素

生成列表的公式

列表名=【 存入列表元素表达式(用变量进行组合) for 变量名 in range(start,stop)】

列表推导式

所谓的列表推导式,就是指的轻量级循环创建雷彪

格式1:

列表变量=[变量表达式 for 变量inrange(10)]

表达式中需要使用后面那个变量

例:

list1=[ i for i in range[100]]

能够实现 创建一个里面有从0-99元素的列表

格式2:

在循环中使用if

例: list1=[x for x in range(30,100) if x%2==0]

能够实现 只有当 x%2为0时 才会将x的值放入列表

格式3:

在其中使用两个for循环

例: list1=[(x,y) for x in range(1,3) for y in range(2,6)]

能够实现 创建一个列表中 包含一个个包含 x和y值的元组

三个for循环也一样

元组

python内置的数据结构之一,是一个不可变序列 元组名(元素1,元素2,元素3)

不可变序列与可变序列

不可变序列:字符串、元组 整数 没有增、删、改的操作

可变序列:列表、字典 可以对序列执行增、删、改操作,对象地址(ID)不会发生改变

为什么要将元组设计为不可变序列呢?

这样可以在多任务环境下,同时操作对象时不需要加锁 因此,在程序用尽量使用不可变序列

注意事项:元组中储存的对象的引用

如果元组中元素本身是不可变对象,则不能再在该元素中引用其他对象,但如果元组中元素本身的引用不允许改变但是引用的数据可以改变。

元组的创建方式

方法一:元组名=(元素1,元素2,元素3 )小括号是可以省略的 但当只有一个元素的元组时必须需要小括号并加逗号 例 元组名=(元素,)来声明这是一个元组数据
方法二 使用内置函数tuple(): 元组名=tuple((元素1,元素2,元素3))

空元组的创建方式 方法一:元组名=() / 方法二:元组名=tuple()

关于两种方法的不同

方法一这种不会进行遍历处理 比如 a=('abcd',) 他会把‘abcd’作为一个元素放入元组

而方法二 a=tuple('abcd') 他会将字符串中每一个字符作为一个元素放入元组中

元组中可以包含元组

元组的遍历

元组时可迭代对象,所以可以用for..in进行遍历

方法一: for 变量名 in 元组名

print(变量名)

方法二:循环配合下标的方式

for i in range(len(s)):

print(s[i])

i=0

while i <len(i):

print(s[i])

i=i+1

元组中嵌套元组的遍历

isinstance() 判断参数一是否是参数二的类型

通过isinstance判断遍历的元素是否是一个元组 如果是就继续遍历 输入不是就是直接输出

例:

t=(1,2,(4,5),(2.1,5))

for v in t:

if isinstance(v,tuple):

for v2 in v:

print(v2)

else:

print(v)

元组中常用的方法

.count(元素内容) 统计元组中某元素的个数

.index(元素内容,start,stop) 查找某元素的下标索引值

###

字典

Python内置的数据结构之一,与列表一样是一个可变序列 以键值对的方式存储数据,字典是一个无序的序列

字典名={键名:值,键名:值,键名:值} 例:scores={‘张三’:100,‘李四’:98,‘王五’:59}

字典的实现原理:与查字典类似,查字典是先根据部首或者拼音查找相应的页码 ,python中的字典是根据

key(键名)来查找value(值)的位置 而key的排序是根据通过hash函数计算后的值进行排列的 key必须是一个不可变的序列

字典的创建

字典也是一种可变序列

最常用的方式:使用花括号

字典名={键名:值,键名:值,键名:值} 例:scores={‘张三’:100,‘李四’:98,‘王五’:59}

key一般情况下使用字符串类型的数据进行充当,理论上所有不可变类型数据都可以作为key,也就是只要可hash的对象都可以作为key

使用内置函数dict()

字典名=dict(键名(字符串也不用加‘引号)=值,键名(字符串也不用加‘引号)=值)

空字典的创建

字典名={}

字典的常用操作

字典的特点

key不允许重复 若重复会出现值覆盖情况 后赋给的值会替代之前的值value(值)可以重复

字典当中的元素是无序的3.5之后变成有序的了 字典中key必须是不可变的对象

字典可以根据需要动态的进行伸缩

字典会浪费较大的内存,是一种使用空间换时间的数据结构

#####

key是否在字典内的判断 利用 in 和not in 进行判断

语法 :print(键名 in 字典名) print(键名 not in 字典名)

字典中元素的访问方法

字典也是通过下标方式来访问元素,但是字典中没有索引,也就是没有下标编号,字典中通过中括号中写key的方式来直接访问key所对应的值

方法一

字典名【键名】 如果字典中不存在指定的key会抛出keyError异常

例: s={‘a’:’星期一‘,’b‘:’星期二‘}

print(s['a'])

方法二

get()方法 例: 字典名.get(键名,默认value(可以不写))如果字典中不存在指定的key,并不会抛出keyError而是返回None,可以通过参数设置默认value(值),以便指定的key不存在时返回

字典元素的修改

字典名【键名】=修改后的值 注意这个是修改某个键名对应的值

字典元素的删除

del 字典名【键名】 (这个是删除指定的键值对,一删删一对)

字典名.popitem() 删除字典中最后一个键值对

字典名.pop(key) 可以通过指定key来删除任意位置键值对

字典名.clear() 清空字典内的所有元素(键值对)

字典元素的添加

字典名【键名】=值 新增元素(键值对)

获取字典视图的三个方法

变量名=字典名.keys()获取字典中所有的key

变量名=字典名.values()获取字典中所有的value

变量名=字典名.items()获取字典中所有的键值对

将视图转换成列表 list(变量名)

字典元素的遍历

方法一

遍历字典元素的key

for 变量 in 字典名:

print(变量)

遍历字典中的value(值)

for 变量 in 字典名:

print(字典名【变量】/字典名.get(变量))

方式二

通过keys()方法 来遍历

for 变量 in 字典名.keys():

print (变量)

for 变量 in 字典名.keys():

print(字典名【变量】/字典名.get(变量))

方式三

通过values()方法

for 变量 in 字典名():

print(变量)

该方法直接就可以遍历字典中的所有值 但是这个方法无法取到key哦

方式四

通过 items()方法来获取

print(字典名.items())

该方法会将字典中每一对键值放在元组中以一个元素形式放在列表中显示

例 [('a':'星期一'),('b':’星期二‘)]

那么就可以通过 for in 遍历里面的元组 然后通过元组索引来获取里面的key和value

for 变量 in 字典名.items():

print(变量[0],变量[1])

也可用通过解包的方式进行遍历出 key和value

for 变量1,变量2 in 字典名.items():

print({变量1},{变量2})

生成字典的公式

内置函数zip()

用于将可以迭代的对象作为参数,将对象对应的元素打包成一个元组,然后返回这些由元组组成的列表

字典名={作为key的变量:作为value的变量 for 作为key的变量,作为value的变量 in zip(列表1,列表2) }

这样就能将两个列表分别作为key 和value生成在一个字典中了

集合

集合是python语音提供的内置数据结构之一

集合与列表、字典一样都是属于可变类型的序列 可以说集合是没有value的字典

集合的创建

集合中的元素不能重复 且无序的

方法一 :集合名={元素1,元素2,元素3}

方法二:使用内置函数set()

创建一个空集合 : 集合名 = set() 注意这里如果只是通过 集合名={} 创建出来的是一个空字典

集合元素的判断操作

同样使用 in 或者not in 即可

集合元素的新增操作

集合名.add(元素) 一次添加一个元素

集合名.update(一个或多个元素) 一次添加一个或多个元素

集合元素的删除操作

集合名.remove(元素) 一次删除一个指定的元素,如果指定元素不存在则抛出KeyError

集合名.discard(元素) 一次删除一个指定元素,如果指定元素不存在不会抛出异常

集合名.pop(不可写入参数) 一次只删除一个任意元素 元素不可指定

集合名.clear() 清空集合内元素

集合之间的关系

两个集合是否相等

可以用运算符==或者!=进行判断 注意:由于集合本身就是无序的所有判断是否相等的标准是里面元素是否一样

一个集合是否是另一个集合的子集

可以调用方法issubset进行判断 集合1.issubset(集合2)

一个集合是否是另一个集合的超子集(真子集)

可以调用issuperset来判断 集合1.issuperset(集合2)

判断两个集合之间是否含有交集

可以调用isdisjoint(没有交集)方法来判断 例:集合1.isdisjoint(集合2) 如果结果为true 说明没有交集 如果为false则说明两者存在交集

集合之间的数据操作

交集

两种方式调用 方法一: 集合名1.intersection(集合名2) 方法二:集合名1&集合名2

并集

两种方法调用 方法一:集合名1.union(集合名2) 方法二:集合名1|集合名2

差集

两种方法调用 方法一:集合名.difference(集合名) 方法二:集合名1-集合名2 值得是前者比后者多出的部分

对称差集

两种方法调用 方法一:集合名1.symmetric_difference(集合名2) 方法二:集合名1^集合名2

集合生成式

集合名={ 需要存储进去的变量组成的公式 for 变量 in range(start ,stop) }

set-list-tuple三者类型间的转换

字符串转list

变量名=list(字符串)

列表转集合 set

set(列表名) 可以达到去重的效果 而且集合是无序哦

字符串转集合 set

set(字符串名)

元组,列表,字典,集合中需要知道的公共方法

len(item) 计算容器中元素个数

del(item) 删除变量

max(item) 返回容器中元素最大值

min(item) 返回容器中元素最小值

字符串

字符串在python中是一种基本数据类型,是一个不可变的字符序列

python中字符串一般用 ‘ ’ 或者“ ” 引起来表示定义

用‘’ 和""引住的字符串只能在一行中进行输出 在其后写一个\表示没有写完

如果希望得到一个跨越多行的字符串,需要用‘’‘或者 三重引号进行引住(‘’‘ ’‘’)或(“”“ ”“”)

字符串的下标

字符串的下标范围也是从0开始的

获取字符串中某一个字符的方法 字符串名字[字符的下标] 或者 ‘abcdef’[字符的下标]

len函数

len(字符串) 可以得到字符串的长度

关于字符串的遍历

1.使用系统提供的 for 变量名 in 字符串

例: for v in s:

print(v)

该系统函数的实现原理:

def bl(s):

定义一个用来返回转换后的变量

num=0

for c in s:

//利用 拿到了字符,通过ord函数和字符0的ASCII码进行转换,得到差值

n=ord(c)-ord('0')

//进行数据的累加

num=num*10+n

return num

m=bl(‘123’)

print(m)

print(m*10)

  1. for i in range(len(字符串)):
  2. i=0
    while i<len(字符串):
    print(s[i])
    i+=1
字符串的驻留机制

仅保存一份相同且不可变字符串的方法,不同的值被存放在字符串的驻留池中,python的驻留机制对相同的字符串只保留一份拷贝,后续创建相同字符串时,不会开辟新空间,而是把该字符的地址赋给新创建的变量 即相同的字符串的话ID是一样的

驻留机制的优缺点

优点:当需要值相同的字符串时,可以直接从字符串池里拿来使用,避免频繁的创建和销毁,提升效率节约内存,因此拼接字符串和修改字符串是比较影响性能的

在需要进行字符串拼接时建议使用str类型的join方法,而非+,因为join()方法是先计算出所有字符的长度,然后进行拷贝,只new一次对象,效率要比+效率高

会触发驻留机制的几种情况(交互模式)

字符串的长度为0或者1时

符合标识符的字符串

字符串只在编译时进行驻留,而非运行时

【-5,256】之间的整数数字

可以通过引入sys中的intern方法强制2个字符串指向同一个对象

import sys

被赋值的变量a=sys.intern(b)

值得注意的是PyCharm这个软件对于字符串进项了优化处理,默认进行了强制驻留

字符串的常用操作

字符串的查询操作方法

字符串变量名.index(需要查找的内容) 查找子串substr第一次出现的位置,如果子串不存在,则抛出 ValueError

字符串变量名.rindex(需要查找的内容) 查找子串substr最后一次出现的位置,如果子串不存在,则抛 ValueError

字符串变量名.find(需要查找的内容,start,stop) 查找子串substr第一次出现的位置,如果子串不存在,则返回-1

字符串变量名.rfind(需要查找的内容) 查找子串substr最后一次出现的位置,如果子串不存在,则返回-1

建议是使用find进行查找,因为如果没有找到只是会返回-1而不会报错

字符串中的统计count

字符串变量名.count(需要统计的内容,start,stop)

字符串分隔操作的方法

字符串变量名.split(分隔符,分隔次数)从字符串的左边开始劈分,默认的劈分字符是以空格作为分隔的标志位置,返回值是一个列表 如果整个字符串中没有空格除 则会将整个字符串变成列表中的一个元素 可以通过参数sep指定劈分字符串的劈分符 例 :字符串变量名.split(sep=‘|’) 就是以‘|’ 作为劈分处的标志 同时也可以通过参数maxsplit来指定劈分字符串的最大劈分次数,在经过最大劈分次数后剩余的子串会单独作为一部分

注意!!如果在分割字符串时,需要使用任何空白进行分隔,那么只需在参数中什么都不行即可 例:如果是想将字符串中\t或者\n这些作为分隔符 直接 字符串名.split() 中间不用写任何参数 系统会默认将字符串中 \t和\n等 作为分隔符全部分隔

字符串变量名.rsplit()表示从字符串的右边开始进行劈分 其用法和split相同,而且值得注意的是它只是从右开始劈分 劈分好后 仍然是从左往右将劈分好后的子串们 从左往右存储到列表中 所有得出来的列表中元素的顺序和从左劈分无差别

按行进行分隔

字符串名.splitlines() 其实就相当于 字符串名.split('\n')

判断字符串操作的方法
partition分隔

该方法是按照分隔条件将字符串分割为三部分(分隔条件前,分隔条件,分隔条件后)

例 s=‘abcdefg’

print(s.partition(c))

得到的结果是 (‘ab’,'c','defg')

rpartition 也是 从右开始分隔

字符串合并操作的方法

某字符.join(元组/序列/字符串) 将列表或元组中的字符串合并成一个字符串 join前的这个字符会分别添加到原元素之间组成新的字符串

例 s='abcde'

print('-'.join(s))

结果就是 a-b-c-d-e

注意如果join的那个字符串如果只有一个字符元素是不能成功的 链接后还是原来的字符串

字符串替换操作的方法

字符串变量名.replace(要被替换的子串,用来替换的字符串,替换的次数(不写默认全部替换)) 第一个参数是指定被替换的子串,第二个参数指定替换子串的字符串,该方法返回替换后得到的字符串,替换前的字符串不发生变化,调用该方法可以通过第三个参数来指定最大替换次数

字符串的比较操作方法

比较使用的运算符:> >= < <= !=

比较的规则:首先比较两个字符串中的第一个字符,如果相等则继续比较下一个字符,依次比较下去,直到两个字符串中的字符不相等时,其比较的结果就是两个字符串比较的结果,两个字符串中的所有后续字符将不再被比较

比较原理:两个字符比较的时候,比较的其实是其ordinal value(原始值),调用内置函数ord可以得到指定字符串的ordinal value。与内置函数ord相对应的是内置函数chr,调用内置函数chr是来指定ordinal value可以得到其对应的字符

== 与 is 的区别 == 比较的是value 而is 比较的是内存地址(ID)

字符串的切片操作方法

由于字符串是不可变类型所以其不具备增 删 改等操作,切片操作将产生新的对象

新字符串名=原字符串名【start:stop:step】

如果是全部切 新字符串名=原字符串[:] 如果是不给结束下标就是从开始到结束 如果是不给开始下标 就切到给的结束下标

步长可以为负值,表示从右往左切 start为大下标,stop为小下标了,但是下标还是从左往右排不会改变 例 print(s[10:2:-1])

实例就是字符串的逆序 语法:print(s[::-1])

对字符串中内容进行判断的操作

.startswith()判断是否以指定字符串开头

.endswith()判断是否以指定字符串结束

注:在编码中中文也是属于字母

.isidentifier() 判断指定的字符串是不是合法的标识符

.isspace()判断指定字符串是否全部由空白字符组成(回车,换行,水平制表符)

.isalpha() 判断指定字符串是否全部由字母组成

.isdecimal()判断指定字符串是否全部由十进制的数字组成(整数型)

.isnumeric()判断指定字符串是否全部由数字组成(可以识别出中文数字和罗马数字)

.isalnum()判断指定字符串是否全部由字母和数字组成

字符串的大小写转换操作方法
**字符串变量名.upper()**把字符串中所有字符都转成大写字母

**字符串变量名.lower()**把字符串中所有的字符都转成小写字母

字符串变量名.swapcase()把字符串中原来的大写改成小写而原来的小写改成大写

字符串变量名.capitalize()把第一个字符改成大写,其余的字符全部改为小写

字符串变量名.title()把每一个单词的第一个字符转换为大写,把每一个单词的剩余字符转换为小写

字符串内容对齐操作方法

字符串变量名.center() 居中对齐,第一个参数用于指定宽度,第二个参数指定填充符,第二个参数是可选的,默认是空格,如果设置的宽度小于实际宽度则会返回原字符串

字符串变量名.ljust()左对齐,第一个参数用于指定宽度,第二个参数指定填充符,第二个参数是可选的,默认是空格,如果设置的宽度小于实际宽度则会返回原字符串

字符串变量名.rjust()右对齐,第一个参数用于指定宽度,第二个参数指定填充符,第二个参数是可选的,默认是空格,如果设置的宽度小于实际宽度则会返回原字符串

字符串变量名.zfill()右对齐,左边用数字0进行填充,该方法只接收一个参数,用于指定字符串的宽度,如果指定的宽度小于等于字符串的长度,则返回原字符串

字符串去除空白操作方法

.strip()去除两端空白

.lstrip()去除左端空白

.rstrip()去除右端空白

注意!! 在去除空白是必须保证字符串的左端或者右端或者两端是空白 有一个字符都不可以哦 如:“- aaaaa -”两端是有字符的哦

字符串的格式化操作方法

%作占位符

%c 字符

%s 字符串

%d 有符号十进制整数

%3d 输出时,数据将会占3个字符宽度 若超出宽度 原样打印哦

%03d输出时,数据占3个字符宽度,当数字不足3个字符时前缀用0来补

%-3d 输出时,数据占3个字符宽度,左对齐

%u 无符号十进制整数

%f 浮点数

%.3f 小数点后保留位数为3位

%o 八进制整数

%x 十六进制整数(小写0x)

%0X十六进制整数(大写0X)

%e 科学计数法(小写e)

%E科学计数发法(大写E)

%g %f和%e的简写

¥G %f和%E的简写

用法

‘我的名字叫做%s,今年%d岁了’%(名字变量名,年龄变量名)

name='张三'
age=20
print('我叫%s,今年%d岁'%(name,age))

%作占位符时还可以进行别的作用

1.print('%10d' % 99) 输出的结果会占有十个整数位置 而99会补上原本10个中的最后两个 可以以此来设置宽度

2.print(‘%.3f’%3.14159)输出将会保留三位小数,以此来设置精度 .3表示小数点后三位小数

3.print(‘%10.3f’%3.14159) 在设置宽度为10的情况下保留三位小数

{}作占位符

用法

1.调用.format(格式)方法

‘我的名字叫做{0},今年{1}岁了,我真的叫{0}’.format(名字变量名,年龄变量名)

name='张三'
age=20
print('我叫{0},今年{1}岁,我真的叫{0}'.format(name,age))

print('{0:.3}'.format(3.14159)) 输出结果为3.14 因为这里的.3表示的是一共保留三位数

print('{0:.3f}'.format(3.14159))输出的为3.142 这里的.3f表示的是保留三位小数的意思

print('{0:10.3f}'.format(3.14159)) 在设置宽度为10的情况下保留三位小数

2.使用f-string的方法

print(f‘我叫{名字变量名},今年{年龄变量名}岁’)

字符串的编码和解码
s='海内存知己'
print(s.encode(encoding='GBK'))  #将字符串‘海内存知己’编码为GBK格式,此格式一个中文占两个字符
print(s.encode(encoding='utf-8'))#将字符串‘海内存知己’编码为utf-8格式,此格式一个中文占三个字符
d=s.encode(encoding='GBK')
print(d.decode(encoding='GBK')) #解码
d=s.encode(encoding='utf-8')
print(d.decode(encoding='utf-8'))#解码

函数

什么是函数:函数就是执行特定任何以完成特定功能的一段代码

为什么需要函数

复制代码

隐藏实现细节

提高可维护性

提高可读性便于调试

函数的创建和调用

创建函数的语法

def 函数名(需要的参数):

 函数体

【return *

实例: def hsname(a,b) : a和b称为形式参数,简称形参,形参的位置是在函数的定义处

c=a+b

return c

函数中可以用PASS来占位函数体来防止程序报错

在创建函数是有时暂时不知道函数体要写什么 如果空着程序会报错,这时可以用PASS来进行占位来解决

调用函数语法

函数名(实际参数)

接收的变量名=函数名(实际参数)

print(接收的变量名)

实例:

方法一

jg=hsname(10,20) 10和20称为实际参数,简称为实参,实参的位置是函数的调用处

print(jg) 这里采用位置实参进行传参的,根据形参对应的位置进行实参传递

方法二

jg=hsname(b=20,a=10) 这个会 将20传给b,10传给a 不管位置,是关键字实参,根据形参的名称进行实参传递

print(jg)

函数文档注释

文档注释:DocString

写在程序文件开头的位置时,是程序文件的DocString,对于整个程序进行说明

写在函数开头位置时,是对函数功能的说明,是函数的DocString

例:

def show():

'''

这是show函数的说明文档

'''

print("hello world")

查看函数说明文档的语法

help(函数名)

还有一种方法可以快速查看函数说明文档 选中函数名 按住ctrl然后点一下函数名

在函数的调用过程中,进行参数的传递满足以下规则

1.如果是不可变对象,在函数体的修改不会影响实际参数的值

2.如果是可变对象,在函数体的修改会影响到实际参数的值

函数的返回值

return

1.如果函数没有返回值【函数执行完毕后,不需要给调用处提供数据】那么return可以省略不写

2.函数的返回值,如果是1个,直接返回类型

3.函数的返回值,如果是多个,可以直接在return后面直接写出多个值,返回结果将会为元组的格式 通过组包和拆包操作

例:

def max_min()

x=1

y=2

return x,y

res=max_min()

解释器在执行代码时,发现return后面如果有多个值,那么就会将这多个值,直接组包称为一个元组然后将这个元组返回

函数的参数定义
默认参数

例:

def show(a=0,b=0):

print(a,b)

show()

在这个函数中 a的那个0和b的那个0 都是默认参数 show中未给实参值 那么 函数就会使用默认参数 如果只给了一个实参值那么会传给第一个实参第二个仍然使用默认值 在有默认值参数的右侧不能再出现没有默认值的参数 例 def show(a,b=0,c) 就是不可以的

#####

1.函数定义默认值参数:函数定义时,给形参设置默认值,只有与默认值不符的时候才需要传递实参

注意:定义可变的位置参数一个函数只可以定义一个

例: def fun(a,b=10):

print(a,b)

fun(100) 此时值传递一个参数把100传给a,b则采用默认值

fun(20,30) 此时传递两个参数,将20传给a,30则将替代b的默认值10

位置参数

按照位置进行依次进行传参

2.个数可变的位置参数:定义函数时,可能无法事先确定传递的位置实参的个数时,可以使用可变的位置参数

用法:使用*定义个数可变的位置形参 结果是一个元组

def fun(*形参名):

关键字参数

只需要在函数调用时候 写上 形参名=数据就可以了 这样就能把数据传到对应的形参位置了

3.个数可变的关键字形参:定义函数时,无法事先确定传递的关键字实参个数时,使用可变的关键字形参

注意:定义可变的关键字参数一个函数只可以定义一个

用法:使用定义个数可变的关键字形参 结果为一个字典**

def fun1(形参名):**

print(形参)

调用: fun1(a=10,b=20 )

在函数的定义过程中,可以既有一个个数可变的关键字形参又有一个个数可变的位子形参,但是要求个数可变的位置形参放在个数可变的关键字形参之前

混合参数

如果很多个值都是不定长参数,那么这种情况下可以省略参数放到 参数后面 但如果有*参数的话必须放在最后

例:

def fun(a,b,c,d,参数名,f=1,g=2,*参数名):

函数可变参数的二次传递

def show(a,*args):

print(a)

show2(args)

def show2 (a,b,c,d)

print(a,b,c,d)

show(1,2,3,4)

这时程序会报错 因为 2,3,4作为一个元组数据存放到b中 导致 c,d没有数据传入导致报错

解决方案:把 show2(args) 这时使用args 将会主动将元组进行解包,将元祖中的元素依次赋给四个变量

函数的返回值

return 返回一个函数的结果

格式:

return 数据(此数据可以是函数中的某个变量也可以是一个固定的数据)

返回后的数据可以用一个变量接收,也可以直接用print打印出来

一个函数汇总可以存在多个return,但是只能有一个语句是有效的,在执行顺序上,第一个遇到的return有效

一个函数无论在哪遇到return,那么这个函数都会直接结束执行,回到调用处

return 后面可以没有数据 只是退出函数回到调用处

函数也可以没有return,函数默认返回None

变量的作用域:程序代码能够访问该变量的区域

局部变量:在函数内定义并使用的变量,只在函数内部有效,局部变量使用global声明,这个变量就会成全局变量

注意这个global声明需要在该变量第一次出现之前进行声明才可以

全局变量:函数体外定义的变量,可作用于函数内外 ,多个函数可以利用全局变量共享数据,因为全局变量在使用时,特性是共享,所以可以利用全局变量实现函数间的通信

变量查找规则:LEGB

Local->EnClosed->Global->Buildins

本地 (局部属于本地) 闭包 全局 内建

如果在函数定义中已有语句先是用来全局变量,且该全局变量还是不可变类型数据那么想要再修改全局变量需要使用 global进行再次声明一下该变量才能使用

不可变类型:

数字 int

字符串 str

浮点数 float

布尔类型 bool

元组 tuple

特点:这些数据是不可以直接进行修改的,如果在修改或赋值时,都会开辟一个新的空间

可变类型:

列表 list

字典 dict

集合 set

特点:这些数据类型,是可以直接在原对象上进行修改数据,修啊改完后,并不影响原对象地址

python中引用的概念

引用的就是数据在内存空间中储存是的地址编号 通过 id()函数leukemia得到数据在内存中的地址

在python中 a=1 a是直接引用了1 而并没开辟新的空间 会发现 1的地址和a的地址是一模一样的 如果再将a赋值给b a=b 会发现三者的地址居然还是一样 仍然没有开辟新的空间 这一点和其他编程语言很是不同

数字类型和字符串类型数据的缓存区的概念

为了在程序汇中使用数据时,效率更高,python解释器会在程序加载后产生一个缓存区,缓存区中放的是常用的数据

数字:-5~255

字符串:长度小于20的字符串

函数的嵌套调用

嵌套调用是指,在一个被调用的函数体内又调用了另一个函数

递归函数

如果一个函数的函数体内调用了该函数本身,这个函数就被称为递归函数

递归的组成分:递归调用与递归终止条件

递归的调用过程:每递归调用一次函数,都会在栈内存分配一个栈帧,每执行完一次函数都会释放相应的空间

递归的优缺点:优点:思路和代码简单 缺点:占用内存多,效率低下

例:阶乘

def factorial(n):

if n==1:

return 1

return n*factorial(n-1)

编程思想

面向过程:事物比较简单,可以用线性的思维进行解决

面向对象:事物比较复杂,使用简单的线性思维无法解决

匿名函数(也称匿名表达式) lambda

格式:

lambda 形参1,形参2,形参3:

单行表达式/函数调用

匿名函数与普通函数的区别“

1.没有函数名

2.参数列表外没有括号

3.函数体中只能实现简单的表达式计算或函数调用

4.函数体中不能使用return if while for - in 匿名函数是默认返回结果的不需要加return 加了反而报错

5.函数体中可以使用if实现三目运算符 但是不可以使用if的正常格式

例: 变量=lambda m,n :m if m>n else n

使用场景

需要给一个变量来引用

变量名=lambda.......:单行表达式/函数调用

调用

变量名()

一般情况下因为lambda的局限性,使他不能实现复杂功能,只能实现一些简单功能,一般在使用时都是用在实现一个简单的,一次性的场景

首先我们需要明确一个概念 万物皆对象,对象就会有内存地址,就会有一个引用,通过这个引用就可以找到该对象并使用他

那么 我们就可以将一个函数的引用赋值给另一个变量

例:

def show():

print('1111')

show()

那么既可以将 show()函数赋给一个变量

需要注意的是赋值时不要加() 不然就会变成将函数返回值赋值给变量

变量名=show 即可

高阶函数

map函数

map()会根据提供的函数对指定序列做映射

第一个参数function 以参数序列中的每一个元素调用function函数没返回包含每次function函数返回值的新序列表

例:

list1=[1,2,3]

def f(x):

return x*x

result=map(f,list1) 其操作就是将列表list1中的每一个元素作为f中的参数进行调用f函数 然后将结果返回到一个新列表中

注意的是 map中函数参数不要加括号

map函数与lambda函数结合使用

变量=map(lambda n: n*n,list1)

reduce函数

此函函数需要引入functools模块

需要在开头 进行 import functools 引用

调用需要 functools.reduce(参数一,参数二)

reduce()函数会对参数序列中元素进行累计

1.用传给reduce中的函数function(有两个参数) 先对集合中的第1,2个元素进行操作

2.得到结果再与第三个数据用function函数运算,最后得到一个结果

例:

import functools

list1=[1,2,3,4,5]

def f(x,y):

return x+y

result = functools.reduce(f,list1)

该函数会将 list1中的前两个元素先传入 函数f中进行操作 然后 再将结果和第三元素传入函数f 然后以此类推 list中的不一定就得是数字也可以是字符 或者字符串什么的

关于reduce和lambda函数的结合使用

例: result=functools.reduce(lambda x,y:x+y,list1)

filter函数

filter()函数用于过滤序列,过滤掉不符合条件的元素,返回一个filter对象如果要转换为列表,可以使用list()来转换.

该接收的两个参数,第一个为函数,第二个为序列序列的每一个元素作为参数传递给函数进行判定,然后返回True或者False,最后将返回True的元素返回到新的列表中.

例:过滤列表中的偶数

list1=[1,2,3,4,5,6]

def f(x):

return x%2==0

result=filter(f,list1)

print(list(result))

filter与lambda函数的结合使用

list2=filter(lambda s:s%2==0,list1)

类与对象

:是多个类似事物组成的群体的桐城,能够快速帮助我们理解和判断事物的性质

不同的数据类型属于不同的类,可以使用内置函数查看数据的类型

对象

100,99,520都是int类之下包含的相似的不同个例,这个个例的专业术语称为实例或者对象

在Python中一切皆对象

类的组成:类名 属性 方法

类属性

实例方法

静态方法

类方法

类的创建

创建语法

方法一 经典定义形式

class 类名:

方法列表

方法二:新式定义法

class 类名(object):

方法一 例:

class Student: //这个Student为类的名称,类名由一个或者多个单词组成,每个单词的首字母需要大写 大驼峰命名法

def info(self): // 定义类的方法 可以理解为类中的函数

print('hello')

_init_方法(重点掌握)

这是一个(魔法方法)也称初始化方法,命名规则是前后各有两个下划线

该方法是是python中预设好具有特定功能法方法,一般这种方法,不需要手动调用,会自动调用执行

该方法会在创建对象的时候自动调用,调用时这个方法会对对象进行初始化

class Student:

tp=‘ 学生’ //直接写在类里面的变量,称为类属性

def_init _(self,name,age) 定义初始化方法 self不可省略表示对自身使用 name age是形参

self.name=name self.属性=值 通过_init_方法实例属性还是动态绑定**只不过是初始化时就对类里面的都自动绑定了

self.age=age

def eat(self实例方法中必须写一个参数建议写self) 在类之外定义称之为函数,在类之内称之为方法

print(‘学生在吃饭’)

_str_方法(重点掌握)

注意 都是双下划线

当这个类使用print进行打印对象时会自动调用 当该类的实例对象进行print时总是会打印该方法return的内容

可以理解为其是对print函数的重定义 默认没有实现__ str__方法时,那么会打印<模块名.类名 object at 0X......>

当使用str()做类型转换时,就会自动调用该方法

格式化对象

该方法会返回一个字符串值


默认没有实现__ str__方法时,那么会打印<模块名.类名 object at 0X......>

如果先按照自己的格式显示,需要在类中实现该方法

例:

def __str__(self):

print('hello',self.某属性) //该函数必须有一个返回值,并且该返回值必须是一个字符串

//如果需要将对象的信息按照一定的格式进行格式化,那么可以在这里进行格式修饰,修饰完后,可以将这个格式化字符串返回,让str()方法在执行时得到该对象转换的字符串类型

s=self.name.ljust(5)+str(self.age).ljust(3) //.ljust(3)表示左对齐三个宽度

return s

__del__方法(了解)

该方法是用来销毁对象时,回收释放资源使用的方法

该方法也是自动调用

当使用del对象时,会调用方法

def __del__(self):

print('已销毁对象 回收了资源')

调用方法:

例 : del self.name //手动调用del销毁了 name

@staticmethod 定义为静态方法

def fangfa(静态方法中括号中不能写参数)

print('我是静态方法')

@classmethod 定义为类方法

def fangf(cls类方法中必须写cls这个参数)

pass

对象的创建:又称类的实例化

语法:对象名=类名(参数) 实例对象时,会在内存中分配一块内存空间,这个空间就是对象的位置

例: stu=Student(‘张三’,20)

对象调用 方法 属性

stu.eat() 调用Student中的eat方法

print(stu.name) stu.name 调用该实例的实例属性name值

类的复合

在设计一个类时,类中包含其他类的对象,称为类的复合

动态绑定属性和方法

Python是动态语言,在创建对象以后,可以动态的绑定属性和方法

动态绑定属性:可以通过 实例对象名.属性名=‘ 内容 ’ 来给该实例添加动态属性,动态绑定属性式,给那个对象绑定了那个对象就有该属性,但其他对象是没有该属性的,如果在方法中引用了该属性,需要self.属性进行调用,且其他那么没有动态绑定该属性且用了有该属性的方法就会报错

动态绑定方法:可以通过在类之外定义一个函数,然后通过 实例名.方法名=函数名来给该实例添加动态方法

公有属性与私有属性

公有属性

在python抽象类时,默认定义的变量时公有的,公有变量可以通过对象在任何位置进行访问

定义属性时,没有任何修饰的都是公有的

在类中公有属性一般不建议使用,其会破坏程序的封装性,在类的外部可以通过对象直接访问,对于数据涞说相对不安全

私有属性

因为公有属性的不安全性,可以将属性设置为私有,私有属性只能在类的内部进行使用,而在类的外部是不可以使用的

在定义时,如果在属性或方法前加两个下划线前缀,那么这个属性或方法就被认为是私有的

例如之前的那个 __init__中 self.name 就可以写成 self.__name 来进行属性私有化

私有属性定义好后,可以保证数据的访问安全,但是还有需求取进行属性访问时,可以通过共有接口方法进行间接访问

一般对私有属性会提供一种称为存储器方法的公有方法 需要写在类中

set/get方法 这个set和get只是一般我们会这么起名 但是你也可以自己起别的名

set_属性名() 特点:有参无返回值

例:def set_balance(self,new_balance)

self.__balance=new_blance

get_属性名() 特点:无参有返回值

例: def get_name(self):

return self.__name

set/get方法对私有属性操作时的好处:

1.提供精确的访问控制权限

2.隐藏实现细节,让代码更加安全

3.可以提供更加安全的数据精度控制

私有方法

将不想公开的实现数据和逻辑通过私有方法进行封装,可以隐藏关键代码

在定义时,如果在属性或方法前加两个下划线前缀,那么这个属性或方法就被认为是私有的

例:def__add(self,name)

self.name=' '

如果想要使用私有方法功能时,可以在类的内部进行使用,或者在类的外部通过类中提供的公有方法来间接使用

封装

面向对象的三大特征

封装:提高程序的安全性

将数据(属性)和行为(方法)包装到类对象中,在方法内部对属性进行操作,在类对象的外部调用方法,这样,无需关心方法内部的具体实现细节,从而隔离了复杂度

在Python中没有专门修饰符用于属性的私有,如果改属性不希望在类对象外部被访问,前面使用两个‘ _ ’.

class Student:

def_init_(self,name,age)

self.name=name

self__age=age 那么年龄就不可以在类的外部进行调用,但是是可以在类的内部进行调用

def show(self):

print(self.age)

可以通过print(dir(实例名))查看所有属性 然后在类的外部通过 实例名._类名__属性名进行访问

####

object类

object类是所有类的父类,因此所有类都有object类的属性和方法

内置函数dir()可以查看指定对象所有的属性

object有一个_str__()方法,用于返回一个对于对象的描述(对象的内存地址),对应于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对 _str _()进行重写 这样print(对象名) 返回的就不是对象的描述 而是你重写的了

继承:提高代码的复用性

如果一个类没有继承任何类,则默认继承object python支持多继承 定义子类时,必须在其构造函数中调用父类的构造函数

继承后,子类会继承父类中的属性和方法,可以直接使用 但是子类不能继承直接使用父类的私有属性和私有方法只能通过继承

得到的父类的公有方法中间接的调用那个私有的方法

出现继承后

当子类对象去调用方法时,会先在子类中去查找,方法如果有那么就执行如果没有找到就去父类中去查找,再找不到就再

到上一级类中去查找,直到找到object类,如果还没有那么就报错

被继承的类:父类,超类,基类

衍生出的类:子类,派生类

语法格式:

class 子类名(父类名1,父类名2.......):

pass

class Persson(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def info(self):
        print(self.name,self.age)
class Student(Persson):
    def __init__(self,name,age,score): //因为子类在提供了init方法后那么使用子类实例对象时就会调用子类自己的init方法,因
        Persson.__int__(self,name,age)//此如果想要父类中的属性也得到需要主动执行父类的init方法 即 父类名.__init__()
        super().__init__(name,age)    //    如果需要父类中的原本内容 通过super().xxx()调用父类中被重写的方法
        self.score=score
stu=Student('张三',20,30)
stu.info()

方法重写

在子类继承父类时,子类会拥有父类中的方法,但是子类并不需要或不满父类方法中提供的功能,这是就需要进行对父类方法重写

重写之后调用该方法时,会调用重写后子类中的方法

如果子类对于父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写

子类重写后的方法可以通过super().xxx()调用父类中被重写的方法

在子类中进行重写

语法:def 父类方法名():

新编写的内容

如果需要父类中的原本内容 通过super().方法名()调用父类中被重写的方法 当然也可以父类名.方法名进行引用两者都是可以的

多层继承

继承关系有多层,祖孙三代

class A(object):

def a(self):

print('a')

class B(A):

def b(self):

print('b')

class C(B):

def b(self):

print('c中b方法')

def c(self):

print('c')

class D(C):

def d(self):

print('d')

d=D()

d是可以调用ABCD中的所有方法 由于C类中重新了b方法 d而的是直接继承的c类所有调用的将是c中重写的b方法

多层继承时的初始化问题,其实可以参照继承中的初始化方法

多继承

例;

class Father(object):

def f(self):

print('f,fun')

class Mother(object):

def m(self):

print('m,fun')

class Son(Father,Mother....):

def play(self):

print('s,play')

s=Son()

s.play()

s.f()

s.m()

子类是可以直接继承自己的所有父母类们 当调用方法时会先从自身方法找 如果找不到就会找继承中()里离自己最近的那个类

这里面就是Father 如果还找不到就往后找就是Mother类 ,这种方法参考的是MRO机制(方法解析顺序)

再找不到就是object类了

关于MRO的顺序确定原理(理解即可)
多继承的初始化(难点)

例;

class Person:

def__init__ (self,aaa):

self.aaa=aaa

class Father(object):

def__init__ (self,aaa,name):

Person.__init__(self,aaa) //优化后改为super(Father,self).__init__ (aaa)

self.name=name

class Mother(object):

def__init__ (self,aaa,age):

Person.__init__(self,aaa) //优化后改为super(Mother,self).__init__ (aaa)

self.age=age

print('m,fun')

class Son(Father,Mother....):

def__init__ (self,gender):

Father.__init__ (self,aaa,name):

Mother.__init__ (self,aaa,age): //优化后改为 super(Son,self).__init__ (aaa,name,age):

self.gender=gender

print('s,play')

s=Son()

s.play()

s.f()

这种方式的继承写法会导致这个共同的父类Person会被初始化多次,是这继承时是否父类名调用初始化方法的时候所引起的

要解决这个问题就需要使用super(子类名,self)__init__ (父类的属性)来解决 而一般会使用super()简化的方式进行使用

简化方式其实就是super(子类名,self)改为了super() 省略了小括号里的内容因为本身就是默认的嘛

关于初始化传参的问题

由于父类与子类单独调用方法时,有时需要给的参数不一样因此会把参数改为*arges这种来表示不定参

类的继承书写顺序会影响mro顺序(方法解析顺序)其实就是说 当调用方法时会先从自身方法找 如果找不到就会找类写继承中()

里离自己最近的那个类这里面就是Father 如果还找不到就往后找就是Mother类

多继承调用指定父类中方法

由于多层继承多继承时,方法查找顺序也参考MRO,因此如果子类在调用某个方法时不想要调用MRO机制中所会调用的继承方法时

需要指定调用父类中的方法

方法一:super().方法名() 就可以调用比MRO机制会调用的那个类的更上一层的类的对应方法名的方法了 所以还是有一定限制

方法二:想要调用类名.方法名(参数)即可 这个是经常会用的方式 这个方式记得需要把要给的参数给上


多态:提高程序的可拓展性,和可维护性

简单来说,多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍可以通过这个变量调用方法,在运行过程中,根据变量所引用对象的类型,动态决定调用那个对象中的方法

####

类属性

类中方法外的变量称为类属性,被该类的所有对象所共享

类方法

使用@classmethod修饰的方法,使用类名直接访问的方法

定义格式

class 类名(object):

@classmethod //是一个修饰器,如果有他,当执行性该方法时,解释器会自动将该类对象传递到参数cls中去

def 方法名(cls,......): //这个cls类似于其他方法中的那个self

pass

调用格式

类对象.类方法名

注意:在类方法中,不能使用self,但是可以使用cls,该参数用来表示当前类对象,这个参数也是自动传递的

应用场景

一般在设计类时,如果有类方法,该类一般不会去实例对象,而是直接使用类对象来操作(比如数学函数类)

一般用来定义工具类使用

例:Math.max(1,2)

静态方法

使用@staticmethod修饰的方法,使用类名直接访问的方法

格式:

class 类名(object):

@staticmethod

def 方法名(参数列表...): //注意这里括号里可以什么都不写 包括self 但如果有需要的参数还是要写的

pass

调用方式:

同类方法,类对象.静态方法名()

设计目的:

静态方法实际就是放到类当中的一堆普通函数

作用:

静态方法同类方法相似,也是在不需要实例对象产生的条件下,可以使用静态方法来实现,一般这两种方法

都是用在工具类的实现上。通过类将一系列的功能方法进行整合到一起

总结:实例方法 类方法 静态方法的特点

实例方法:必须通过实例对象进行调用执行(第一个参数是当前调用该方法的实例)

类方法:当不需要阐释实例对象时,可以使用类方法来实现相应的代码功能,类方法可以直接使用类对象来调用,

类方法的第一个参数是cls,用来接收当前类对象,通过这个参数可以在各个类方法中共享数据

静态方法:作用同类方法相似,但是静态方法不接收任何默认参数(实例对象或类对象),静态方法其实就是将一些

相关联的普通方法使用类进行相关整合

类方法和静态方法在使用时,大多用来实现工具类


类属性的调用方法:print(实例名.类属性名)

类方法的调用方法:类名.方法名()

静态方法的调用方法:类名.方法名()

静态语言与动态语言的区别

1.静态语言实现多态的三个必要条件

继承 方法重写 父类引用指向子类对象

2.动态语言的多态崇尚“鸭子类型”当看到一只鸟走起来像鸭子,游泳起来像鸭子,走动起来也像鸭子,那么这只鸟就可以被称为鸭子,在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,而是只关心对象的行为。

特殊的方法和特殊的属性汇总

特殊的属性 __dict_:获得类对象或者实例对象所绑定的所有属性和方法的字典

_class_:会获得对象所属的类

_bases_:获得该类的所有父类 并以元组输出

_base_:获得该类所有父类中()写的第一个并输出

_mro_:查看类的继承的层次结构

_subclasses_:查看该类的所有的子类

特殊方法 __len_()通过重写该方法让内置函数len()的参数可以是自定义类型

_add_() 通过重写该方法可以使自定义对象具有“ + ”功能

_new_()用于创建对象

_init_() 对创建对象进行初始化

类的浅拷贝与深拷贝

浅拷贝:Python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此源对象与拷贝对象会引用同一个子对象

语法: import copy

实例化对象2=copy.copy(实例化对象1)

可以发现 实例化对象2的id与实例化1的id是不同的,但是他们的属性的id却是一致的。

深拷贝:使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同

语法:import copy

实例化对象2=copy.deepcopy(实例化对象1)

可以发现 实例化对象2不仅自己的id与实例化1的id不同连同他们的属性的id也是不一样的 都开辟了新的空间

模块

模块:英文名字Modules 在Python中一个拓展名为.py的文件就是一个模块

在python中,万物皆对象,模块也不例外,通过模块可以有效的组织代码结构,让代码结构更加清晰

函数与模块之间的关系:一个模块中可以包含N个函数

使用模块的好处:

方便其他程序和脚本的导入并使用

避免函数名和变量名冲突

提高代码的可维护性

提高代码的可重用性

模块的导入

1.在一个.py文件中,使用另外一个.py文件时,需要将这个文件导入

2.在一个模块中,使用另外一个模块时,需要将这个模块导入

格式:

方法一:在文件开头 import 模块名 这个是导入该模块中的全部内容 该方式实际是引入了模块的地址 通过地址来使用模块成员

方法二:在文件开头 from 模块名 import 成员名(不要加括号的) 特例: from 模块名 import (这个表示的也是引入全部模块中的内容 是通配符,但是如果采用这个方式就理所当然不能进行起别名**了)

两种导入方式的区别:

1.第一种方式实际是引入模块的地址,通过这个地址来使用模块中的成员 这种方式是没有任何限制的

2.方法二这种方式导入时,调用模块中的成员时不需要再用 模块名.成员名 的形式来进行调用了,可以在当前文件中直接使用成员名,

相当于将导入的复制了一份代码到本地文件 但是有些模块中的成员时不能被单独导入的(有权限限制)比如一些私有属性

但通过__all__['私有属性名']这个魔法属性可以改变from-import的导入规则从而可以导入私有属性 但一般不建议使用

3.form-import导入方式的命名冲突问题 因为你导入的内容可能会和你自己定义的变量名或别的一样导致报错

别名的使用

由于一些模块的名字太过冗长,所以会用别名来代替进行引用

语法:

方法一:import 模块名 as 别名 //将模块名引入并给它起了个别名

from 模块名 import 成员名 as 成员别名 //这个是只引入模块中的某个成员时用的方法

调用模块中的函数或方法

格式:模块名.函数名()/方法名() 如果是使用 import-成员名 的方式导入的 可以直接调用

模块的分类

1.系统模块

2.自定义模块

3.第三方模块

模块的查找顺序

1.当前程序所在目录中找模块

2.当前程序所在工程的根目录中找

3.PYTHONPATH中进行找

4.系统目录

5.site_packages 第三方模块安装的目录找

模块的导入顺序

自定义模块

创建模块

新建一个.py文件,名称尽量不要与Python自带的标准模块名称相同

导入模块

导入模块时,需要创建该模块的对象,也就会执行该模块文件中的所有代码

语法:import 模块名称 【as 别名 就是在该文件中可以使用这个别名进行调用出来,一般针对名字较长的进行起别名】

import 模块名称 这个是导入该模块中的全部内容

      这里调用里面的东西需要 被调用的模块名称.类/函数/...

from 模块名称 import 函数/变量/类 这个是只导入该模块的 部分内容(函数/变量/类)

而这里直接 函数名就可以 不用模块名.出来

在导入自己写的模块时候需要在pycharm的该.py文件所在的文件夹右键选择Mark Directory as 中的Sources Root

新版本pycharm不需要此操作

模块中__name__使用 以主程序的形式运行

当使用__name__属性在获取当前文件名时,会有两种效果,如果当前模块文件时主动执行的,那么__name__得到的结果是

字符串__main__表示这是程序的入口,而如果当前模块文件是被动执行时(也就是被作为模块导入执行)那么得到的结果就

是当前模块文件的文件名

通过以上特性可以在模块编写时开头进行一个判断:

if __name__==‘__main__’://当满足这个条件时会执行一部分测试代码 这样就能避免在导入模块使用使用时会执行一些不想执行的

测试代码

在每个模块的定义中都包括一个记录模块名称的变量_name_,程序可以检查该变量,以确定他们在哪个模块中执行,如果一个模块不是被导入到其他程序中执行,那么它可能在解释器的顶级模块中执行。顶级模块的_name_的变量值为_main_

例:

def add(a,b):
    return a+b
if __name__ == '__main__':
    print(add(50, 60))  #只有在点击运行本模块时,该语句才会进行执行

Python中的包

包是一个分层次的目录结构,它将一组功能相近的模块组织在一个目录下

作用:规范代码,避免模块名称冲突

包与目录的区别:

在python3.6之前 包含__init__.py文件的目录称为包 目录中通常不包含_init_.py文件

在python3.6之后,不管有没有__init__.py,都认为是一个包

包的创建方式:

选择文件夹 右键 选择New中的 Python Package进行创建包

导入包中模块的方式:

方式一:

import 包名.模块名 那么调用时就是 包名.模块名.成员名

【as 别名 同样的可以给它起一个别名】 import 包名.模块名 as 别名 调用时就是 别名.成员名

方式二:

当然也可以使用 from--import方式进行导入

from 包名 import 模块名 调用: 模块名.成员名

from 包名.模块名 import 成员名 当然也有特例 用通配符*来代指所有成员名

通过__init__.py设置导入模块时的权限

通过__init__.py中进行设置

当在文件中导入包而没有细节到导入包里那个模块时但是又想导入时导入包里的多个模块时就需要借助__init__.py进行设置

__init__.py将会告诉解释器 当导入这个包的时候将会导入那些模块

格式:

* 在__init__.py文件中 from .import 模块名* 在文件中写了那些模块 那么在导入该包时就会自动导入这些模块

也可以使用__all__来指定可以包中可以导入的模块

这种方法不通用 这种方式只能在引用方式采用from 包名 import * 引入全部模块时

通过 __all__['模块名(不要写后缀)','模块名'......]

Python中常用的内置模块

sys:与Python解释器及其环境操作相关的标准库

time:提供与时间相关的各种函数的标准库

os:提供了方位操作系统服务功能的标准库

calender:提供与日期相关的各种函数的标准库

urllib:用于读取来自网上(服务器)的数据标准库

json:用于使用JSON序列化和反序列化对象

re:用于在字符串中执行正则表达式匹配和替换

math:提供标准算数运算函数的标准库

decimal:用于进行精确控制运算精度,有效数位和四舍五入操作的十进制运算

loggin:提供了灵活的记录事件、错误、警告和调试信息等日志信息的功能

第三方模块的安装以及使用

第三方模块的安装

语法:首先cmd打开系统命令行 然后 pip install 模块名

引入第三方模块

方法和正常引入模块语法一样 import 模块名

编码格式

Python的解释器使用的是Unicode(内存) .py文件在磁盘上使用的是UTF-8储存(外存)

异常

异常:程序在执行过程中发生了不可预知的问题,导致程序崩溃

常见异常:

NameError:变量未定义但调用了

TypeError:类型错误 比如int类型的数据和其他数据类型进行操作

ValueError:值错误

AttribitueError:属性错误

SyntaxError:语法错误

IndexError:下标错误

异常的传递

当代码中出现了跟多层级的调用时,在其中发生了异常,如果没有处理这个异常,这个异常就会自动向上抛出

当上层也没有处理时,就会继续向上抛出,直到抛到解释器,而解释器默认处理异常的方式就是中断程序将异

常信息显示到控制台上 这就导致只是一个地方的错误但是确报异常了好几次因此处理同一个问题的异常可以在

不同位置进行处理 但一般我们都是在最底层的根源位置进行处理

异常处理

格式:

try:

except:

else:

finally:

一般会采用 这try except两个就够了 例

s='hello'

try:

print(s.index('y')) //此处用来写有可能会发生异常的代码

except ValueErro(有可能会报的异常) :

print('查找的子串不存在')

通过上方代码可以发现 使用try来执行可能会异常的代码,如果发生异常那么异常信息将会被except捕捉到,

可以在except中进行异常处理 如果代码没有异常,那么程序将会正常执行,不会再执行except中的代码

关于except中异常如果不确定会是什么 可以写Exception 来表示通用异常

自定义异常

格式:

class 异常名Error(Exception):

def __init__(self,msg=' '):

self.__msg=msg

def__str__(self):

return self.__msg

class CustomError(Exception):

pass

自定义异常的抛出

raise 异常对象

raise 类名

raise 类名()

多个异常处理

将多个异常通过圆括号包裹成一个元组

注意:虽然可以捕捉多个异常,但是同一时刻只能有一个异常发生

语法:

try:

可能出现异常的代码

except (异常错误类型1,异常错误类型2...): 如果想要捕捉到太多的话 还是写Exception吧

当异常发生时处理异常的代码

异常同样可以起别名

例:

try:

可能出现异常的代码

except (异常错误类型1,异常错误类型2...) as 别名:

print(e)

当异常发生时处理异常的代码

异常处理中else和finally的使用

else:

在异常处理时,当没有异常发生的情况后需要执行的代码放到else语句块中

finally:

无论异常是否发生,都会执行这个语句块中的内容 一般用在资源需要关闭的场景上

例:文件关闭 网络连接关闭 数据库的关闭

文件

程序操作文件的过程

打开->打开的模式(读写)

操作文件(读取)

关闭->close()

文件的读写原理

文件的读写俗称“IO”操作 (input output)

读写语法规则: file(被创建的文件对象)=open(创建文件对象的函数)(filename(要创建或打开的文件名称) 【,mode(打开模式默认为只读取),encoding(默认文本文件中字符的编写格式为gbk)】)

常用的文件打开模式

按照文件中数据的组织形式,文件分为以下两大类

文本文件:储存的是普通“字符”文本,默认为unicode字符集,可以使用记事本程序打开

程序文件

.txt

.rtf

二进制文件:把数据内容用字节进行储存,无法用记事本打开,必须使用专门的软件打开,例:MP3文件视频文件图片文件等

打开模式

r :以只读模式打开文件,文件的指针会放在文件的开头

w:以只写模式打开文件,如果文件不存在则创建,如果文件存在则覆盖原有内容,文件指针在文件的

a:以追加模式打开文件,如果文件不存在则创建,文件指针在文件开头,如果文件存在,则在文件末尾追加内容,文件指针在原文件末尾

b:以二进制方式打开文件,不能单独使用,需要与其他模式一起使用,rb或wb

+:以读写方式打开文件,不能单独使用,需要与其他模式一起使用,r+ w+ a+ 这三种都具有读写功能 但是有具备自身独有的特性

语法 : file=open(“文件名”,‘模式(如w)‘)

file.write(’内容‘)

file.close()

文件对象的常用方法

都需要先打开文件哦

文件的读取

.read([size]): 从文件中读取size个字节或字符的内容返回,若省略[size]则读取到文件末尾,即一次读取文件的所有内容 (但是这种全部读取的方式只适合文件比较小的情况,如果文件比较大建议还是设置读取的字节一般指定4096)同一个程序中 下一次读取会继续上一次读到的位置开始读

例: 变量名= file.read()

.readline():从文本文件中读取一行内容

.readlines():把文本文件中每一行都作为独立字符串对象,并将这些对象放入列表返回

.write(str):将字符串str内容写入文件 在打开文件时需要具有写入模式才行

.writelines(s_list):将字符串s_list写入文本文件,不添加换行符 在打开文件时需要具有写入模式才行

.seek(offset[,whence]):把文件指针移动到新的位置,offset表示相对于whence的位置: offset:为正往结束方向移动,为负往开始方向移动 whence不同的值代表不同的含义:0 代表从文件的开头开始计算(默认值) 1代表从当前位置开始计算 2: 代表从文件末尾开始计算

tell():返回文件指针的当前位置

flush():把缓冲区的内容写入文件,但不关闭文件 (向文件中写入数据的时候,python并不会立刻写入,而是会写到缓冲区,等待清空的时候写入文件,而flush可以将缓存区内容写入文件,并清空缓存区,从而达到不用close文件就能写入文件)

close():把缓冲区的内容写入文件,同时关闭文件,释放文件对象相关资源

上下文管理器:当一个类能够实现(enter和exit)功能时候称这个类遵循(上下文管理)协议,可以称这个类的实例对象为上下文管理器

with语句(上下文管理器)

with语句可以自动管理上下文资源,不管什么原因跳出with块,都能确保文件正确的关闭,以此来达到释放资源的目的

with open() as 【别名】 这个open就是一个自带的一个上下文管理器

目录操作

os模块是python内置的与操作系统功能和文件系统相关的模块,该模块中的语句的执行结果通常与操作系统相关,在不同的操作系统上运行,得到的结果可能不一样

os模块与os.path模块用于对目录或文件进行操作

OS模块

path指定是环境路径 在写路径时记得对\进行转义 防止出错

os.rename('a.txt','b.txt') 重命名操作

os.system(’notepad.exe‘) 就可以打开操作系统的记事本

os.startfile(’应用程序所在目录及名称‘) 就可以打开电脑上的文件

os.mkdir(path[,mode]) 创建目录 如果当前目录存在会报错 例: os.mkdir('001')

os.getcwd() 返回当工作文件所在的目录

os.listdir(path) 返回指定路径下的文件和目录信息

os.remove(path) 删除一个文件

chdir(path) 将当前工作目录移动到path位置

makedirs(path1/path2/path3/......) 创建多级目录、

os.rmdir(path) 删除目录 删除目录时该目录中不能有其他文件才能删除

removedirs(path1/path2/.....) 删除多级目录

练习:批量复制文件并改名

//定义函数

def test_copy(src,dst):

//获取当前工作目录

print(os.getcwd())

//切换目录到源文件目录

os.chdir(src)

//从源当中读取所有的文件信息

list1=os.listdir(src)

//创建出一个新的目标文件夹

os.mkdir(dst)

//循环读写每一文件 遍历每一个文件

for file in list1:

//对文件进行改名,遍历的文件就是需要复制的源文件,改名后的文件就是要复制出来的文件

//先对源文件名进行分割

s_file=file.partition('.')

//拼接成新的文件名

dst_file=dst+'/'s_file[0]+'back'+s_file[1]+s_file[2]

//分别以读写方式打开源文件和新文件

file_r=open(file,'rb')

file_w=open(dst_file,'wb')

//通过循环读取和写入内容

while True:

content=file_r.read(1024)

if content=b'': //因为打开文件时,使用的是二进制模式打开,所以在判断结束时,需要判断是否是一个二进制空字符串 b‘ ’

print(f'{file}复制成功')

file_r.close()

file_w.close()

break

//如果读取内容不是空,则将读取内容写入到目标文件

file_w.write(content)

else:

print(f'一共复制了{len(list1)}个文件') //因为在循环中 else是在循环正常结束后会执行的代码块

源文件夹

src='c:/.....'

目标文件夹

dst=‘c:/......’

调用函数

test_copy(src,dst)

os.path模块

abspath(path):用于获取文件或者目录的绝对路径

exists(path) :用于判断文件或目录是否存在,如果存在返回true,否则返回false

join(path,name):将目录与目录或者文件名拼接起来

splitext():分离文件名和文件路径或将文件和拓展名进行分离

basename(path):从一个目录中提取文件名

dirname(path):从一个路径中提取文件路径,不包括文件名

isdir(path):用于判断是否为路径

远程服务

服务器

变量名:服务器 类型:远程服务

服务器.启动(端口号,处理函数:&子程序1(子程序指针类型),采用串行处理方式)

关于子程序1(也会命名为处理函数) 就是在客户连接 客户发送数据会调用当前子程序 里一般会出现的代码 客户获得消息类型 可以获得的消息内容 这个子程序会有一个参数 内容是客户端发送过来的消息地址,可以通过该地址获得详细的内容

客户端

变量名:客户端 类型:请求客户端

客户端.连接(端口号,服务器地址,请求方式(同步(消息发送过去,立即让其返回数据结果)/异步(消息发过去就不管了 爱要不要,不会立即返回数据,随便服务器什么时候返回数据)))

Python高级

绝对路径与相对路径

绝对路径

从根目录算起的路径叫绝对路径 只要以/开头的都是绝对路径

例如:

/home/python/Desktop

/usr/bin

相对路径

从当前目录算起的路径叫做相对路径

例如:

./text/hello

../static/images

Linux

Linux(内核,李纳斯.托瓦兹) 最初名字叫Minix

系统根据包管理格式分为:

rpm格式:RedHat CentOs(RedHat社区版)

deb格式:ubuntu

Linux内核和发行版的介绍

linux内核:实现操作系统的基本功能

linux发行版:在linux内核基础上,添加定制特有的软件

ubuntu系统介绍

/:根目录

/bin:可执行二进制文件的目录(系统目录中存放的是常规命令)

/etc:系统配置文件存放的目录()

/home:用户家目录

Linux基础命令

linux命令格式

命令名 空格 [选项]...空格[参数]... 在计算机命令中中括号代表括号里的内容是可选项 可以有也可以没有 空格是必须加的不然就不对

每个信息的说明

命令名:比如 ls、pwd

选项:可以有0-多个选项,多个选项可以合并,比如使用 -r就是选项

选项可以分为短选项和长选项:

短选项:‘-’后面接单个字母 比如-r

长选项:‘--’后面接单词 比如 --help

必选参数对长短选项同时适用:

例:-a与--al 是同一个功能l

参数:可以有0-多个参数,比如:touch文件名 mkdir 目录名 cd 目标

参数是命令的操作对象:一般是文件名或者目录名

[]:代表可选

对于命令,它的选项和参数一般情况下没有顺序要求,

用户名@主机名:路径$

$表示普通用户环境

#表示root用户环境

!ls 开始执行最近一次 ls开头的那个命令

清屏命令

clear :清除命令 快捷键:ctrl+l

查看当前目录及显示当前目录下的内容

1.查看当前目录:pwd (print work directory)

2.查看当前目录下的内容:ls (list)

ls -a: 显示当前目录下所有的文件(包含以.开头的隐藏文件)

ls -l:以长信息格式显示当前目录下的文件

ls -h:一般会配合-l使用即ls -lh,一单位格式显示文件大小

ls -alh 路径:显示指定路径下的所有文件的长信息

切换目录命令

cd(change directory)

cd 目录 :切换到指定目录 记得都是有空格的

cd -:切换到当前用户的主目录

cd .. :切换到上一级目录

cd.:切换到当前目录

cd -:切换到上一次目录

cd / :切换到根目录

自动补全

通过按下tab键可以补全命令路径 如果按完tab之后,没有可提示内容时,会不显示任何内容,可以继续按第二次,会将所有的文件或命令给提示出来,选择是否显示,如果在输入几个字母后,按一次tab,会将所有以字符开头的命令或文件显示出来,如果命令或者文件是唯一的,那么按一次tab会直接补全

补充:使用上下方向键可以上下翻动历史命令

优点:避免输入错误,提高输入效率

创建和删除目录命令

创建目录:mkdir 目录名

删除目录:rmdir 目录名 注意只能删空目录,里面不能有文件

-p选项的用法

逐层级创建目录

mkdir -p a/b/c/d/....

逐层级删除目录

rmdir -p a/b/c/d/...... 注意 这些层级里不能有一个是非空的目录

创建和删除文件命令

创建文件:touch 文件名.文件后缀名

删除文件: rm 文件名.文件后缀名 该方式会直接永久删除文件不可找回

一次创建多个文件: touch a b c d e f

删除当前路径下全部目录:rm 如果和-i合用 rm -i 那么会在删除每一个文件前进行依次询问

强制删除目录 :rm -r目录名 不管里面有没有有没有文件

强制删除不询问: rm-f

常用: rm -rf * 删除当前路径下所有内容

复制文件或目录命令

复制文件

cp 源文件1 源文件2..... 空格 目标路径 [文件名] []中的是复制后的文件名 为可选内容 可写可不写 目标路径不写就是默认就是当前文件所在位置 这时必须给一个新文件名

当要复制多个源文件到目标文件夹时

在复制时,源文件可以有多个 cp命令会将最后一个位置默认为目标文件夹,将源文件们复制到其中

利用通配符快捷复制 例: cp *.txt 空格 目标路径

复制目录

cp -r原目录 目标路径 [目录名] []中的是复制后的目录名 为可选内容 可写可不写 目标路径不写就是默认就是当前目录所在位置 这时必须给一个新目录名

-v 在移动或复制时,显示路径信息

移动文件或目录命令

mv 源文件 目标路径 [文件名] 目标路径不写就是默认就是当前文件所在位置 这时必须给一个新文件名

mv 源目录 目标路径 [目录名] 目标路径不写就是默认就是当前目录所在位置 这时必须给一个新目录名

移动目录时不需要和-r搭配使用

mv可以实现重名操作

-v 在移动或复制时,显示路径信息

查看帮助命令

命令名 --help

man 命令名 man是Manual(手册的意思)的缩写 按q键退出

这两个比较 man的这个更详细 help的话较为简略

echo命令

echo 是一个回响命令数据 类似print

例:

echo a 就会显示一下a

echo $? 就会显示上次执行命令或程序的执行状态码

echo $PATH 显示系统环境变量 PATH

重定向命令

输出重定向,用来将输出到屏幕的数据,重定向到一个指定位置(一般是指定一个文件),不管指定的文件是否存在,都会创建新文件保存数据

> 输出重定向,区别在于 它是追加数据,如果文件不存在则创建 如果存在则是在文件中追加

查看文件内容命令

cat 文件名 cat一般用来查看小型文件

查看多个文件内容

cat 文件名1 文件名2...... 其会将查看的几个文件内容都依次显示出来

cat与重定向命令结合可以实现文件的拼接 cat 文件1 文件2... > 合并后的文件3

more 文件名 分屏查看大型文件

分屏查看是的按键说明

空格: 显示下一屏信息

回车:显示下一行信息

b:显示上一屏信息

f:显示下一屏信息

q:退出

管道命令(|)

管道:| 一个命令的输出,可以通过管道作为另一个命令的输入,也可以理解成一个容器,存放在终端显示的内容

例:

ls-l | more ls通过管道和 more结合使用,来达到分配查看终端显示内容

链接命令

链接命令分为软链接命令和硬链接命令

软链接

类似Windows下的快捷方式,当一个源文件的目录层级比较深,我们想要方便使用它可以给源文件创建一个软链接

软链接的方法和规则是同时适用于目录和文件的

格式:

ln -s 源文件路径/文件名 目标路径/链接文件名

特点:

软链接文件,无论对哪一个文件进行操作都会影响另一个文件,当源文件删除或者移动或改名后,那么链接文件将会失效,

链接文件会变成红色,如果在源文件所在路径又手动创建一个与源文件名相同的文件,那么链接将会恢复 虽然新创建的与

之前源文件里的内容不同

当创建软链接文件时,为了避免软链接文件移动后链接失效,那么在创建软链接时,源文件的路径要采用绝对路径指定

硬链接

类似于源文件一个别名,也就是说这两个名字指向的是同一个文件数据

格式 ln 源文件路径 /文件名 目标路径/链接文件名

特点:无法为目录创建硬链,目录中文件信息中的链接数表示当前目录下包含多少子目录

硬链接和源文件保持数据同步,硬链接不受路径的影响建议也写成绝对路径,删除源文件硬链接还可以访问到数据

注意图片中的文件链接数 每创建一个硬链接时 文件链接数就会加一,而创建软链接则不会加一

文本搜索命令(grep)

格式:

grep 选项 ‘内容’ 文件

例: grep -i ‘aa’ t.txt

grep的命令选项

-i 忽略大小写

-n 显示匹配行号

-v 显示不包含匹配文本的所有行

grep命令结合正则表达式的使用

^ 以指定字符串开头

$ 以指定字符串结尾

. 匹配一个非换行符的字符

例:grep -i ‘^aa’ t.txt

grep -i 'aa$' t.txt

grep -i 'ab.c' txt

查找文件命令(find)

find命令及其选项的使用

find:在指定目录及子目录下查找文件(包括目录)

-name 根据文件名(包括目录名)进行查找

格式:

find 查找路径 -name 被查找文件

find命令结合通配符的使用

* 代表0个或多个任意字符

? 代表任意一个字符

压缩和打包命令

压缩命令

linux默认支持的压缩格式

.gz

.bz2

.zip

说明:.gz和.bz2的压缩包需要使用tar命令来压缩和解压缩,.zip的压缩包需要使用zip命令来压缩,使用unzip命令来解压缩

tar命令

打包命令不会对文件进行压缩 文件大小不会发生改变 需要再进行一次压缩命令

tar命令的选项

-c 创建打包文件

-v 显示打包或者解包的详细信息

-f 指定文件名称,必须放在所有选项后面 例 -cvf

-z 压缩或解压缩(.gz)

-j 压缩或解压缩(.bz2)

-x 解包

-C 解压缩到指定目录

例: tar -cvf 包名.tar 要打包的文件名

gzip 包名.tar //将文件压缩成 包名.tar.gz 文件

gzip -d 包名.tar.gz //将文件解压缩

tar -xvf 包名.tar //将文件解包 tar -xvf 包名.tar -C 目标目录路径 // 将包解包到指定目录

为了在打包时可以进行压缩,tar集成了两选项 -z 和 -j 用来在打包的同时进行压缩

z对应 gzip j对应 bzip2

固定格式 :

tar -zxvf xxx.tar.gz 被压缩文件

tar -zxvf xxx.tar.gz -C 路径 解压缩到指定目录 -C看是否需要指定解压目标目录来决定是否使用

tar -jxvf xxx.tar.bz2 被压缩文件

tar -jxvf xxx.tar.bz2 -C 路径 解压缩到指定目录 -C看是否需要指定解压目标目录来决定是否使用

权限管理命令

文件权限

rwx r-x r-x

文件类型 文件所有者权限 文件所有者所属组用户权限 其他用户权限

r: read 读取权限

w:write 写入权限

x:execute 执行权限

-:占位符 无权限

chmod命令

chmod 修改文件权限

有两种修改方法:字母法 和数字法

字母法:

角色说明

u:user,表示文件的所有者

g:group,表示用户组

o:other,表示其他用户

a:all,表示所有用户

权限设置说明

+:增加权限

-:撤销权限

=:设置权限

权限说明:

r:可读

w:可写

x:可执行

-:无任何权限

例: chmod g+w 1.txt //表示给组用户增加了对于1.txt文件的写入权限

chmod u-w,g-w 1.txt //表示 给文件所有者用户和用户组撤销了对于1.txt的写入权限

chmod g=rx 1.txt// 表示给用户组用户l对于1.txt的读和执行权限

数字法:

r:可对,权限值是4

w:可写,权限值是2

x:可执行,权限值是1

-:无任何权限,权限值是0

那么 rwx对应的是 7 rx对应的是5 依次类推

格式:

chmod 权限值 文件

文件和目录的默认权限值: 三个值对应:root用户 组用户 其他用户

文件默认权限:755

目录默认权限:775

获取管理员权限的相关命令

sudo命令的使用

sudo -s :切换到root用户,获取管理员权限

sudo 命令名 :某个命令的执行需要获取管理员权限,可以在执行命令前加数sudo

whoami命令的使用

whoami 查看当前用户

exit命令的使用

exit 退出登录用户

用户相关操作

创建用户

该命令的使用需要使用管理员权限 即需要加上 sudo

useradd 用户名 : 创建(添加)用户

相关命令选项

-m 自动创建用户主目录,主目录的名字就是用户名

-g 指定用户所属的用户组,默认不指定会创建一个同名的用户组 例:sudo useradd -m-g zhangsan

例: sudo useradd -m -g 1003 rose -g1003 表示将新创建的rose用户的组指定为1003那个组

查看用户是否创建成功可以查看 /etc/passwd 这个文件

passwd文件中每项信息说明例:root: x:0:0:root:/root:/bin/bash

第一个:用户名

第二个:密码占位符

第三个:uid,用户id

第四个:gid,用户所在组id

第五个:用户描述,可选

第六个:用户目录所在位置

第七个:用户组shell的类型,一般由bash或者sh。默认不设置是sh类型

查看用户组是否创建成功可以查看 /etc/group这个文件

uid=1001(zhangsan) gid=1001(zhangsan) 组=1001(zhangsan)

第一个:uid 表示用户的id

第二个:gid 表示用户组id

第三个:表示用户所在的用户组

更名用户密码的命令:

sudo passwd 用户名

切换用户命令

su - 用户名 如果是普通用户直接的切换 -可以省略 但是如果要切换到root时 必须加上-

删除用户命令

sudo userdel 用户名

可选项

-r 用户名 删除用户的主目录,必须要设置,否则用户的主目录不会被删除

用户组相关命令

创建用户组命令

sudo groupadd 用户组名 密码是root的密码

删除用户组命令

sudo groupdel 用户组名 但注意当一个用户组作为一个用户的主组时无法进行移除需要先删除用户再去删组

远程操作ssh和scp

远程登录远程拷贝命令

ssh 远程登录

ssh是专门为远程登录提供的一个安全性协议,常用于远程登录,想要使用ssh服务,需要安装相应的服务端和客户端软件,

当软件安装成功后就可以使用ssh命令了,以后就可以通过远程登录之间操作远程的服务器。

软件安装步骤:

ubuntu作为服务端,需要安装ssh服务端软件,执行命令:sudo apt-get install openssh-server

客户端如果是macos系统,则不需要安装ssh客户端软件,默认已经安装过了,直接可以使用ssh命令

客户端如果是Windows系统则需要安装OpenSSH for Windows这个软件

ssh命令格式:

ssh 用户名@ip地址 例:ssh junmuxiu@172.16.47.197

通过ifconfig命令可以查看ip地址

通过 exit来关闭链接

FileZilla软件的使用

filezilla软件是一个免费的开源的FTP软件,使用可视化方式进行上传和下载文件,以后可以通过FileZilla来代替scp命令

scp命令(远端拷贝命令)

上传文件:

scp 本地文件名 用户名@ip:远端直接地址

下载文件

scp 用户名@ip:远端主机资源文件路径 本地路径

编辑器Vim

vim是一linux自带的一款功能强大的文本编辑器,也是早年vi编辑器的加强版,它最大的特殊就是使用命令进行编辑,完全脱离了

鼠标的操作

vim的三种工作模式:vim打开文件进入的是命令模式

命令模式: yy 是复制光标所在行 nyy 是复制光标所在行向下数n行(包括光标所在行) p 是粘贴 np是在粘贴n次 dd 是删除/剪切当前行 ndd是删除n行x是删一个字符 shift x是向前删一个字符 u是撤销 ctrl+r是反撤销

/ 要搜索内容 是搜索指定内容 在查找状态下 n是跳到下一个 N是调到上一个

光标的移动:h向左 j向下 k向上 l向右 G是回到最后一行 gg是回到第一行 nG 是回到指定行 $是移动到行尾 0是移动到行首

>>向右缩进 <<向左缩进



编辑模式:命令模式下 按i 进入编辑模式,在光标位置进行插入文本,按a是在光标后面位置进行插入文本,按o是在光标所在行的下一行

插入一个新行进行编辑 按小r替换一个字符:按一次r键再俺需要替换的内容字符,按R是进入替换模式会进行持续替换 退出编辑模式使用esc键 退出后就进入命令模式了

末行模式:按 ':' 从命令模式 进入末行模式 按w 就是将刚才修改的文件进行写入保存 w!是强制保存 然后 :q 退出 q!是强制退出 如果按 : x 就是保存并退出 x! 是强制保存并退出 设置行号:set nu 取消行号:set nonu 开启语法高亮:syntax on

软件的安装

ubuntu软件安装有两种方式

离线安装(deb文件格式的安装)

格式 dpkg -i 安装包名

在线安装(apt-get方式安装)

多任务的介绍

利用当前所学知识,能够让两个函数或方法同时执行吗?

不能因为之前所写的程序都是单任务的,也就是说一个函数或方法执行完成后,另一个函数或者方法才能执行,要实现这种操作

就需要使用多任务,多任务最大好处就是充分利用CPU资源,提高程序的执行效率

多任务是指,在同一时间内执行多个任务,例如:现在的电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件

多任务执行的方式

并发:在一段时间内交替去执行人物

例如:对于单核cpu处理多任务,操作系统轮流让各个软件交替执行,例如:软件1执行0.01秒,切换到软件2执行0.01秒,再切换到

软件3执行0.01秒......以此类推,表面上看每个软件都是交替执行的,但是对于我们人类来说看起来这些软件就像是同时在执行

但是需要注意到的是,单核cpu是并发的执行多任务的

并行:

对于多核cpu处理多任务,操作系统会给每个内核cpu安排一个执行的软件,多核是真正的同时执行多个软件,这里需要注意多核cpu

是并行的执行多任务,始终有多个软件一起执行

进程

在Python程序中,要实现多任务可以使用进程来完成,进程是实现多任务的一种方式

一个正在运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统就会给其分配一定运行资源(内存资源)保证进程的运行

比如:现实生活中的公司可以理解成一个进程,公司提供办公资源(电脑,办公桌),真正干活的是员工,员工可以理解成线程

注意:一个程序运行后至少有一个进程,一个进程默认有一个线程,进程中可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程

多进程的使用

首先需要导入进程包

import multiprocessing

multiprocessing的使用在Linux和mac中都可以直接使用,但是在windows中,必须加上if name == ‘main’:

process进程类的说明

Process([group[,target[,name[,args[.kwargs]]]]])

group:指定进程组,目前只能使用None

target:执行的目标任务名

name:进程名字 一般很少主动设置

args:以元组方式给执行任务传参

kwargs:以字典方式给执行任务传参

Process创建的实例对象的常用方法

start() :启动子进程实例(创建子进程)、

join():等待子进程执行结束

terminate():不管任务是否完成,立即终止子进程

Process创建实例对象的常用属性

name:当前进程的名字,默认为Process-N,N为从1开始递增的整数

import multiprocessing    #导入进程包
import time   #导入时间包

#跳舞函数
def dance():
    for i in range(3):
        print ("跳舞中")
        time.sleep(2.0)
#唱歌函数
def sing():
    for i in range(3):
        print ("唱歌中")
        time.sleep(2.0)

#创建子进程(自己手动创建的进程为子进程,在__intit__py文件中已经导入Process类)
#group:进程组,目前只能使用None,一般不需要设置   target:进程执行目标任务 一般写函数名  
if __name__ == '__main__':
    dance_process=multiprocessing.Process(target=dance)
#启动进程执行时对应的任务
    dance_process.start()
    sing()  #主进程执行唱歌函数

也可以创建两个子进程 然后都是用.start()来进行执行

获取进程编号

获取进程编号的目的

获取进程编号的目的是去验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的

获取进程编号的两种操作

获取当前进程编号

格式:os.getpid() 需要注意的是如果这个获取进程编号的函数被交给一个进程取执行 那么查看的就是子进程的编号也就是还是这个 要获取的进程编号

例:

            def dance():
            print('dance的进程编号',os.getpid)
            for i in range(3):
            print ("跳舞中")
            time.sleep(2.0)

获取当前父进程的编号

格式:os.getppid()

获取当前进程对象,查看当前代码是由那个进程执行的

格式:multiprocessing.current_process()

根据进程编号杀死指定进程

格式:os.kill(进程编号,9) 9表示强制杀死

进程执行带有参数的任务

Process类执行任务并给任务传参有两种方式:

args 表示以元组的方式给执行任务传参

import multiprocessing    #导入进程包
import time   #导入时间包

def show(name,age):
    print(name,age)

if __name__ == '__main__':
    #以元组方式传参,元组里面的元素顺序要和函数的参数顺序保持一致
    show_process=multiprocessing.Process(target=show,args=('李四',20))
    show_process.start()

kwargs 表示以字典方式给执行任务传参

import multiprocessing    #导入进程包
import time   #导入时间包

def show(name,age):
    print(name,age)

if __name__ == '__main__':
    #以字典方式传参,字典里面的key要和函数里面的参数名保持要求,key位置没有顺序要求
    show_process=multiprocessing.Process(target=show,kwargs={'name':'李四','age':20})
    show_process.start()

当然元组传参和字典传参也是可以混合使用的无非就是 ()中既有 args=() 也有kwargs={}即可

关于进程需要注意的地方

1.进程之间不会共享全局变量

子进程实际就是主进程的一个副本

比如一个子进程是给一个全局变量列表添加数据 一个子进程是读取这个列表里的数据,那么会发现添加的数据并没有被读取出来

即便是使用join让读取数据进程等待添加数据进程执行完一样也读取不到 因为子进程操作的那个全局变量实际是副本中的全局变量


对应linux和mac主进程执行的子进程不会进行拷贝,但是对于window洗头来说,主进程执行的代码子进程也会进行拷贝执行,那么

就会出现一个问题,对应windos来说,创建子进程的代码如果被子进程拷贝,那么就相当于无限递归创建子进程会发生卡死报错,

因此我们需要来解决这个问题 解决的方法如下

将主进程的创建子进程的一些代码放到判断 if name == 'main':里面进行执行即可

2.主进程会等待所有子进程执行结束再结束

这就有可能导致子进程出现问题后出现卡死导致主进程也无法退出最终导致卡死的情况 因此需要解决这个问题

解决办法:主进程退出子进程销毁

1.让子进程设置成为守护主进程,主进程退出子进程销毁,子进程会依赖主进程

格式:

子进程名.daemo=true

2.让主进程退出之前,先让子进程销毁

格式:

子进程名.terminate()

线程

在Python中,想要实现多任务除了使用进程,也可以使用线程来完成,线程是实现多任务的另一种方式

线程的概念

线程是进程执行代码中的一个分支,多个执行分支(线程),要想工作执行代码,需要cpu进行调度,也就是说线程是cpu调度的

基本单位,每个进程至少都有一个线程,而线程就是我们常说的主线程

线程的使用

1.导入线程模块

import threading

2.线程类Thread参数说明

Tread([group[,target[,name[,args[.kwargs]]]]])

group 线程组,目前只能使用None

target 执行目标任务名

args 以元组的方式给执行任务传参

kwargs 以字典方式给执行任务传参

name 线程名,一般不需要设置

3.启动线程

格式:

线程名.start()

4.获取当前线程编码

格式:

threading.current_thread()

5.线程执行带有参数任务的方式

参考进程执行带有参数任务的方式就可

6.线程的注意点

线程之间执行是无序的

主线程会等待所有的子线程执行结束再结束

解决方法

子线程名=threading.Thread(target=函数名,daemon=True) 这个daemon属性就是让子线程守护主线程

另一种写法:子线程名.setDaemon(True)

子线程1名.join() 表示等待子线程1执行完这个线程才开始执行 进程是同理

因为多在同一个进程之中所以线程之间可以共享全局变量

线程之间共享全局变量有可能数据出现错误问题

比如 定义两个函数,每个函数都实现循环100次,每循环一次给全集变量+1 会发现最后得到的全局变量并不是200

因为在执行过程中 可能第一个线程还没加上1第二个线程已经从全局变量那里获取到了变量就或导致最后结果不准确

解决方法 1:线程同步操作中的 线程等待操作(join )保证通一个时刻只能有一个线程去操作全局变量

例: 子线程1名.join() 表示等待子线程1执行完线程2才开始执行

解决方法2:线程同步操作中的 互斥锁操作 对共享数据进行锁定,保证同一时刻只能有一个线程去操作

互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其他等待线程

才能再去抢这个锁

互斥锁的使用:treading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数,可以获取一把互斥锁

创建锁:锁名=threading.Lock()

上锁:锁名.acquire()

释放锁:锁名.release()


例:

import threading
lock=threading.Lock()
num=0
def text1():
    lock.acquire() #上锁
    for i in  range(100):
        global num  #因为整数为不可变序列所以在函数中使用时需要声明一下它
        num=num+1
    print('text1',num)
    lock.release()  #释放锁
def text2():
    lock.acquire()#上锁
    for i  in range(100):
        global num  #因为整数为不可变序列所以在函数中使用时需要声明一下它
        num=num+1
    print('text2',num)
    lock.release()#释放锁
if __name__=='__main__':
    t1=threading.Thread(target=text1)
    t2=threading.Thread(target=text2)
    t1.start()
    t2.start()

互斥锁如果没有用好的话就会出现死锁现象即:一直等待对方释放锁的情景叫做死锁

线程等待和互斥锁都是把多任务执行改成单任务执行,保证了数据的准确性,但执行性能会下降

进程与线程的对比

关系对比

线程是依附在进程里面的,没有进程就没有线程

一个进程默认提供一条线程,进程可以创建多个线程

区别对比

进程之间不会共享全局变量

线程之间共享全局变量,但是要注意资源竞争问题,解决办法:互斥锁,或者线程同步

创建进程的资源开销要比创建线程的资源开销要大

进程是操作系统资源分配的基本单位,线程是cpu调度的基本单位

线程是不能独立执行的,必须依存在进程中

多进程开发比单间差多线程开发稳定性要强

优缺点对比

进程:可以用多核,但资源开销大

线程:不能使用多核,但资源开销小

跟计算密集型的相关操作使用多进程,文件的写入,文件的下载等io操作用线程

总结

进程和线程都是完成多任务的一种方式

多进程要比多线程消耗的资源多,但是多进程开发比单进程多线程开发稳定性要强,某个进程挂掉不会影响其他进程

多进程可以使用cpu多核运行,多线程可以共享全局变量

线程不能单独执行,必须依附在进程里面

网络编程

IP

IP地址

是标识网络中唯一一台设备的地址

分为:ipv4和ipv6

ipv4是目前使用的ip地址 由点和十进制组成

ipv6是未来使用的ip地址 由冒号和十六进制组成

查看ip地址的命令

linux和OS操作系统:ifconfig

Windows系统:ipconfig

本地地址(本地回环地址):127.0.0.1 本地的域名为:localhost 域名是ip地址的别名,通过域名能解析出一个对应的ip地址

检查网络是否正常命令:ping命令

ping www.baidu.com 检查是否能上公网

ping 当前局域网中ip地址,检查是否在同一个局域网内

ping 127.0.0.1 检查本地网课是否正常

端口和端口号

其实每运行一个网络程序就会有一个端口,想要给对应的程序发送数据,找到对应端口即可

端口

端口就是传输数据的通道,就好比门,是数据传输的必经之路

每一个端口都会有一个对应的端口号

端口号

端口号可以标识唯一的一个端口号

操作系统为了统一管理那么多端口,就对端口进行了编号,就是端口号,就好比门牌号 端口号有65536个

通讯软件直接的通讯是通过ip地址找到对应的设备,通过端口号找到对应的端口,然后通过端口把数据传输给应用程序

端口号的分类

知名端口号:指众所周知的端口号,范围从0到1023

这些端口号一般固定分配给一些服务,比如21端口分配给FTP(文件传输协议)服务,25端口分配给SMTP(简单邮件传输协议)服务

80端口分配给HTTP服务

动态端口号:一般程序员开发应用程序使用端口号称为动态端口号,范围从1024到65535

如果程序员开发的程序没有设置端口号,操作系统就会在动态端口号这个范围随机生成一个给开发的应用程序使用

当运行一个程序默认会有一个端口号,当这个程序退出时,所占用的这个端口号就会被释放

TCP协议

网络应用程序之间的通信流程

数据的发送不能随便进行,在发送之前需要选择一个对应的传输协议,保证程序之间按照指定的传输规则进行数据通信

TCP协议

英文全称(Transmisson Contrl Protocol)简称传输控制协议,它是一种面向连接的、可靠的基于字节流的传输层通信协议

TCP通信步骤

1.创建连接

2.传输数据

3.关闭连接

TCP通信模型相当于现实生活中的‘打电话’,在通信开始之前,一定要先建立好连接,才能发送数据,通信结束要关闭连接

TCP的特点

1.面向连接

通信双方必须先建立好连接才能进行数据的传输,数据传输完成后,双方必须断开此连接以释放系统资源

2.可靠传输

TCP采用发送应答机制 超时重传 错误校验 流量控制 和阻塞管理

总结:

TCP是一个稳定可靠的传输协议,常用于对数据进行准确无误的传输,如:文件下载 浏览器上网

socket的介绍

socket的概念

socket(简称 套接字) 是进程之间通信的一个工具,好比生活中的插座,所有的电器想工作都是基于插座进行,进程之间想要进行

网络通信需要基于这个socket

socket的作用

负责进程之间网络数据传输,如想一个电脑的进程给另一个电脑的进程传输数据就必须使用socket

socket应用场景

不夸张的说,只要跟网络相关的应用程序或者软件都使用到了socket

小结:

进程之间的网络传输可以通过socket来完成,socket就是进程间网路数据通信的工具

socket类的介绍

导入socket模块

import socket

创建客户端socket对象

socket.socket(AddressFamily,Type)

参数说明:

AddressFamily 表示IP地址类型,分为IPv4和IPV6

Type 表示传输协议类型

方法说明:

connect((host,port)) 表示和服务端套接字建立连接,host是服务器的ip地址,port是应用程序的端口号

sent(data)表示发送数据,data是二进制数据

recv(buffersize) 表示接收数据,buffersize是每次接收数据的长度

创建服务端socket对象

引入socket模块

import socket

socket.socket(AddressFamily,Type)

方法说明:

bind((host,port)) 表示绑定端口 host是ip地址,port是端口号,ip地址一般不指定,表示本机的任何一个ip地址都可以

listen(backlog) 表示设置监听,backlog参数表示最大等待建立的连接个数

accept()表示等待接受客户端的连接请求

send(data)表示发送数据,data是二进制数据

recv(buffersize) 表示接收数据,buffersize 是每个接收数据的长度

TCP网路应用程序开发流程

TCP客户端程序的开发

1.创建客户端套接字对象

2.和服务端套接字建立连接

3.发送数据

4.接收数据

5.关闭客户端套接字

import socket
#AF_INET:ipv4地址类型 #SOCK_STREAM:tcp传输协议类型
tcp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM )  #创建tcp客户端套接字
tcp_client_socket.connect(('192.168.32.1',8080)) #192.168.32.1是服务端ip地址 8080是端口号
send_content='hello'
send_data=send_content.encode('utf-8')   #将字符串转为二进制数据
tcp_client_socket.send(send_data)   #向服务端发送数据
recv_data=tcp_client_socket.recv(1024)    #接收服务端数据 1024百世每次接收最大字节数
recv_content=recv_data.decode('utf-8') #对接收到的二进制数据进行解码
print(recv_content)
tcp_client_socket.close()           #关闭连接
TCP服务端程序的开发

1.创建服务端套接字对象

2.绑定端口号

3.设置监听

4.等待接收客户端的连接请求

5.接收数据

6.发送数据

7.关闭套接字

import socket
tcp_server_socket=socket.socket(socket.AF_INET ,socket.SOCK_STREAM)  #创建tcp服务端套接字
tcp_server_socket.bind(('',8080))  #因为很多服务端不止一个网卡所以一般ip地址不指定    #8080表示绑定端口号为8080
tcp_server_socket.listen(128)  #128表示最大等待建立连接的个数
#注意:每当客户端和服务端建立连接成功都会返回一个新的套接字 用于服务端和这个客户端进行通信传输
new_client_socket,ip_port=tcp_server_socket.accept()   #等待接收客户端的连接请求 new_client_socket是返回的新套接字
                                                        #ip_port是返回的客户端的ip地址
recv_data=new_client_socket.recv(1024) #接收客户端的数据  可以发现调用接收的套接字需要使用新返回的套接字
recv_content=recv_data.decode('utf-8')  #对接收到的二进制数据进行解码
print(recv_content)
new_client_socket.send('问题正在处理中'.encode('gbk')) #对客户端进行发送数据
new_client_socket.close()#关闭用于和该客户端进行通信的套接字
tcp_server_socket.close()#关闭服务端套接字,表示服务端以后不再等待接受客户端的连接请求 
小结

TCP网络应用程序开发分为客户端程序开发和服务端程序开发

主动发起建立连接请求的是客户端程序

等待接收连接请求的是服务端程序

设置端口号复用

当客户端和服务端建立连接成功后,服务端程序如果退出后,端口号不会立即释放,需要等待大概1-2分钟

解决方法有两种:

1.更好服务端端口号

2.设置端口号复用(推荐使用),也就是说让服务端程序退出后端口号立即释放

在创建tcp服务端套接字后

服务端套接字变量名.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)

socket.SOL_SOCKET表示当前套件字,socket.SO_REUSEADDR表示复用端口选项 True表示开启

TCP网络应用程序的注意点

1.当TCP客户端程序想要和TCP服务端程序进行通信的时候,必须要先建立连接

2.TCP客户端程序一般不需要板顶端口号,因为客户端是主动先发起建立连接的

3.TCP服务端程序必须绑定端口号,否则客户端找不到这个TCP服务端程序

4.listen后的套接字是被动套接字也称为服务端套接字,只负责收新的客户端的连接请求,不能收发消息

5.当TCP客户端程序和TCP服务端程序连接成功后,TCP服务器端程序会产生一个新的套接字,收发客户端

消息都将使用该套接字

6.关闭accept返回的套接字意味着和这个客户端已经通信完毕

7.关闭listen后的套接字也就是服务端套接字意味着服务端的套接字关闭了,会导致新的客户端不能连接服务器,

但是之前已经连接成功的客户端还能正常通信

8.当客户端的套接字调用close后,服务器端的recv会解阻塞,返回的数据长度为0,服务器端可以通过返回的长度

来判断客户端是否已经下线,,反之服务端关闭套接字,客户端的recv也会阻塞,返回的数据长度为0

socket之send和recv原理剖析

认识TCP socket的发送和接收缓冲区

当创建一个TCP socket对象的时候,会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的是内存中的一片空间

send原理剖析

要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说

应用程序必须把发送的数据写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡,把发送缓冲区的数据发送给服务端网卡

recv原理剖析

应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区

引用程序再从接收缓存区获取客户端发送的数据

小结

不管是recv还是send都不是直接接收到对方的数据和发送数据到对方,发送数据会写入到发送缓冲区,接收数据是从接收缓冲区中来读取

发送数据和接收数据最终是由操作系统控制网卡来完成

HTTP协议

HTTP协议的介绍

HTTP协议的全称是 (Hyper Text Transfer Protocol) 翻译过来就是 超文本传输协议

超文本是超级文本的缩写,是指超越文本限制或者超链接,比如图片 音乐 视频 超链接等待都属于超文本

HTTP协议的制作者是蒂姆.伯纳斯.李 1991年设计出来的,HTTP协议设计之前的目的是传输网页数据的,现在运行传输任意类型的数据

传输HTTP协议格式的数据是基于TCP传输协议的,发送数据之前需要先建立连接

HTTP协议的作用

规定了浏览器和web服务器通信数据的格式,也就是说浏览器和web服务器通信需要使用http协议

小结

HTTP协议是一个超文本传输协议

HTTP协议是一个基于TCP传输协议传输数据的

HTTP协议规定了浏览器和web服务器通信数据的格式

URL

URL的概念

URL的英文全拼是(Uniform Resoure Locator) 就是统一资源定位符的意思,通俗理解就是网络资源地址,也就是我们常说的网址

URL的组成

URL的样子 例:https://baidu.com/18/1122/10/E178J2O4000189FH.html

协议部分:https://默认端口号是443 http:// 默认端口号是80 ftp://

域名部分:baidu.com

资源路径部分:/18/1122/10/E178J2O4000189FH.html

URL的扩展

例:https://news.163.com/hello.html/?page=1&count=10

?page=1&count=10表示查询的部分

?后面的page表示第一个参数,后面的参数都用&进行连接

域名

域名就是IP地址的别名,它是用点进行分隔使用英文字母和数字组成的名字,使用域名目的就是为了方便记住某台主机的IP地址

小结

URL就是网络资源的地址,简称网址,通过URL能够找到网络中对应的资源数据

URL组成部分

1.协议部分

2.域名部分

3.资源路径部分

4.查询参数部分(可选)

网页开发者工具的介绍

通过快捷键F12或者鼠标右键点击检查就可以调出开发者工具

通过Network标签选项可以查看每一次请求和响应的通信过程

开发者工具的Header选项一共分为三部分组成

General:主要信息

Response Headers:响应头

Request Header:请求头

Response选项是查看响应具体信息的

HTTP请求报文

HTTP请求报文就是浏览器发送给web服务器程序的http协议数据

HTTP最常见的请求报文有两种

1.GET方式的请求报文 GET:获取web服务器数据

2.POST方式请求报文 POST:向web服务器提交数据 也获取web服务器数据

http get请求报文

-----http get请求报文---------
-----请求行------
GET / HTTP/1.1 \r\n请求方式 请求的资源路径 http协议的版本
------请求头---------
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9 \r\n告诉服务端程序 可以接受的数据类型**
Accept-Encoding: gzip, deflate, br \r\n 告诉服务端程序支持的压缩算法
Accept-Language: zh-CN,zh;q=0.9 \r\n 告诉服务端程序支持的语言
**Cache-Control: max-age=0 \r\n

Connection: keep-alive \r\n表示和服务端程序保持长连接,当客户端和服务端有一段时间没有通信服务端程序会主动断开和客户端的连
Cookie: PHPSESSID=t2tuanfnfpabt1p7are8f484a4; UM_distinctid=181c82b58b6a56-0cc390c3ec8c3e-26021a51-144000-181c82b58b7956; CNZZDATA4617777=cnzz_eid%3D1041339307-1656917455-https%253A%252F%252Fwww.baidu.com%252F%26ntime%3D1656917455; Hm_lvt_0cb375a2e834821b74efffa6c71ee607=1656920693; Hm_lvt_5781a15780e8f638873598472befe1e9=1656920693; qimo_xstKeywords_b2f10070-624e-11e8-917f-9fb8db4dc43c=; href=https%3A%2F%2Fwww.itcast.cn%2F%3Fjingjiaczpz-PC-3; accessId=b2f10070-624e-11e8-917f-9fb8db4dc43c; bad_idb2f10070-624e-11e8-917f-9fb8db4dc43c=318db3e1-fb6d-11ec-a7fe-8fbdb3de0758; nice_idb2f10070-624e-11e8-917f-9fb8db4dc43c=318db3e2-fb6d-11ec-a7fe-8fbdb3de0758; openChatb2f10070-624e-11e8-917f-9fb8db4dc43c=true;客户端用户身份的标识 parent_qimo_sid_b2f10070-624e-11e8-917f-9fb8db4dc43c=31cba740-fb6d-11ec-9aa9-57ef2a5e884b; Hm_lpvt_5781a15780e8f638873598472befe1e9=1656920774; Hm_lpvt_0cb375a2e834821b74efffa6c71ee607=1656920774; qimo_seosource_b2f10070-624e-11e8-917f-9fb8db4dc43c=%E7%AB%99%E5%86%85; qimo_seokeywords_b2f10070-624e-11e8-917f-9fb8db4dc43c=; pageViewNum=4\r\n**
Host: www.itcast.cn \r\n 服务器主机的ip地址和端口号,如果看不见端口号默认端口号是80
**Sec-Fetch-Dest: document\r\n

Sec-Fetch-Mode: navigate\r\n
Sec-Fetch-Site: none\r\n
Sec-Fetch-User: ?1\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 \r\n用户代理 客户端程序名称,后续爬虫的时候可以根据User-Agent进行反爬机制
sec-ch-ua: ".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"\r\n
sec-ch-ua-mobile: ?0\r\n
sec-ch-ua-platform: "Windows"\r\n
--------空行---------
\r\n

http get请求报文的格式

请求行\r\n

请求头中每项信息后\r\n

空行\r\n

注意:每项信息之间都要有一个\r\n进行分隔

这是http协议进行的规定

http post 请求报文

post请求比get请求其他请求头都是差不多的 但多了一个 From Data(请求体) 浏览器发送给服务器的数据通过请求体来完成

http post请求报文的格式

请求行\r\n

请求头中每项信息后\r\n

空行\r\n

请求体

请求体就是浏览器发送给服务器的数据

小结

一个HTTP请求报文可以由请求行 请求头 空行 和请求体四个部分组成

get方式的请求报文没有请求体 只有请求行 请求头 空行组成

post方式的请求报文可以由请求行 请求头 空行 请求体组成 注意:post方式可以允许没有请求体,但是这种格式很少见到

HTTP响应报文

http响应报文是web服务器程序发送给浏览器的http协议的数据

----http 响应报文解析-----
----响应行(状态行)----
HTTP/1.1 200 OK \r\n 协议版本 状态码 状态描述
----响应头-----
Server: nginx \r\n 服务器名称
Date: Mon, 04 Jul 2022 07:46:19 GMT\r\n 服务器的时间 采用的是0时区的时间
Content-Type: text/html \r\n 服务器发送给浏览器的内容类型及编码格式(默认utf-8)
Transfer-Encoding: chunked\r\n 服务器发送给客户端程序(浏览器)的数据不确定数据长度 有些网页这个会是 Content-Length:1024 \r\n表示服务器发送给客户端程序的数据确定长度为1024字节
Connection: keep-alive\r\n 表示和客户端保存长连接 如果长时间不进行通信 服务端会主动断开连接
----以下是自定义响应头信息,自己定义响应头的名字和响应头的值-------
Vary: Accept-Encoding\r\n
Access-Control-Allow-Credentials: true\r\n
Access-Control-Allow-Methods: GET, POST, PUT, DELETE\r\n
Access-Control-Allow-Headers: *\r\n
Content-Encoding: gzip\r\n
-----空行-----
\r\n
响应体
注意:请求体和响应头信息程序员都可以进行自定义,按照客户端和服务器约定好的方式来制定就行

http响应报文的格式

响应行\r\n

响应头\r\n

空行\r\n

响应体\r\n

注意:每项信息之间都要有一个\r\n进行分隔

HTTP状态码

200:请求成功

307:重定向

400:错误的请求,请求地址或者请求参数(即Form Data中的参数)有问题

404:请求资源在服务器不存在

500:服务器内部源代码出现错误

搭建Python自带的静态web服务器

静态web服务器是什么

可以为发出请求的浏览器提供静态文档的程序

平时我们浏览百度新闻数据时候,每天的新闻数据都会发生变化,那访问这个页面的就是动态的,而我们要开发的是静态的

页面的数据是不发生变化的

如何搭建Python自带的静态web服务器

命令:python3 -m http.server 端口号 端口如果不进行设置默认是8000 注意:执行该命令时一定要切换到资源所在目录位置

-m表示运行包里面的模块, -m http.server就是执行http包里server这个模块,需要进入你自己指定的静态文件的目录,然后

通过浏览器就能访问对应的html文件了,这样一个静态的web服务器就搭建好了

开发自己的静态web服务器

实现步骤:

1.编写一个TCP服务端程序

2.获取浏览器发送的http请求报文数据

3.读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器

4.HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字

实例一:开发自己的静态web服务器 --返回固定页面
import socket
if __name__ == '__main__':  #判断是否为主模块代码
    tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建tcp服务端套接字
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) #设置端口号复用
    tcp_server_socket.bind(('',8000)) #绑定端口号
    tcp_server_socket.listen(128)#设置监听
    while True:
        new_socket, ip_port = tcp_server_socket.accept()  # 等待接受客户端的连接请求
        #代码执行到此说明连接建立成功
        recv_data=new_socket.recv(4096)#接受客户端的请求信息
        print(recv_data)
        with open("static/index.html") as file:#这里的这个file表示打开的文件对象
            file_data=file.read()
            #提示:with open 关闭文件这步操作不需要程序员来完成,系统帮助我们完成
            #需要将数据封装成http响应报文格式的数据
        response_line="HTTP/1.1 200 OK\r\n"#响应行
        response_header="Server:PWS/1.0\r\n"#响应头
        response_body=file_data#响应体
        response=response_line+response_header+'\r\n'+response_body   #这里加的这个\r\n是空行的意思
        response_data=response.encode('utf-8') #将字符串编码成二进制
        new_socket.send(response_data)#发送给浏览器的响应报文数据
        new_socket.close()#关闭服务于客户端的这个新套接字
实例二:开发自己的静态web服务器 --返回请求指定页面
import socket
def main():
    tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建tcp服务端套接字
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) #设置端口号复用
    tcp_server_socket.bind(('',8000)) #绑定端口号
    tcp_server_socket.listen(128)#设置监听
    while True:
        new_socket, ip_port = tcp_server_socket.accept()  # 等待接受客户端的连接请求
        #代码执行到此说明连接建立成功
        recv_data=new_socket.recv(4096)#接受客户端的请求信息
        if len(recv_data)==0: #判断接收的数据是否为0
            new_socket.close()
            return
        recv_content=recv_data.decode('utf-8')#对二进制数据进行解码
        request_list=recv_content.split(' ',2)   #对数据按照空格进行分隔
        request_path=request_list[1]  #取分隔后存入元组中第2个元组成员数据
        print(recv_data)
        if request_path=='/':
            request_path='/index.html' #判断请求的是否是根目录,如果是根目录则设置返回首页
        with open("static"+request_path,'rb') as file:#这里的这个file表示打开的文件对象 rb表示以二进制模式读取,兼容图片数据
            file_data=file.read()
            #提示:with open 关闭文件这步操作不需要程序员来完成,系统帮助我们完成
            #需要将数据封装成http响应报文格式的数据
        response_line="HTTP/1.1 200 OK\r\n"#响应行
        response_header="Server:PWS/1.0\r\n"#响应头
        response_body=file_data#响应体
        response=(response_line+response_header+'\r\n').encode('utf-8')+response_body #这里加的这个\r\n是空行的意思
                 #由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
        new_socket.send(response)#发送给浏览器的响应报文数据
        new_socket.close()#关闭服务于客户端的这个新套接字
if __name__ == '__main__':  #判断是否为主模块代码
    main()

注意:此时程序还是有问题的,如果用户访问一个不存在的页面还需要设计返回一个404页面

实例三:开发自己的静态web服务器 --返回404页面

方法一:可以通过调用os模块中的path.exits进行判断

os.path.exists('static/'+request_path)

方法二:使用try-except

import socket


def main():
    tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建tcp服务端套接字
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) #设置端口号复用
    tcp_server_socket.bind(('',8000)) #绑定端口号
    tcp_server_socket.listen(128)#设置监听
    while True:
        new_socket, ip_port = tcp_server_socket.accept()  # 等待接受客户端的连接请求
        #代码执行到此说明连接建立成功
        recv_data=new_socket.recv(4096)#接受客户端的请求信息
        if len(recv_data)==0: #判断接收的数据是否为0
            new_socket.close()
            return
        recv_content=recv_data.decode('utf-8')#对二进制数据进行解码
        request_list=recv_content.split(' ',2)   #对数据按照空格进行分隔
        request_path=request_list[1]  #取分隔后存入元组中第2个元组成员数据
        print(recv_data)
        if request_path=='/':
            request_path='/index.html' #判断请求的是否是根目录,如果是根目录则设置返回首页
        try:
            with open("static" + request_path, 'rb') as file:  # 这里的这个file表示打开的文件对象 rb表示以二进制模式读取,兼容图片数据
                file_data = file.read()
                # 提示:with open 关闭文件这步操作不需要程序员来完成,系统帮助我们完成
        except Exception as e:
            #代码执行到此,说明没有请求的该文件,返回404状态信息
            response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
            response_header = "Server:PWS/1.0\r\n"  # 响应头
            with open('static/error.html','rb') as file: #读取404页面数据
                file_data=file.read()
                response_body=file_data
                response = (response_line + response_header + '\r\n').encode('utf-8') + response_body  # 这里加的这个\r\n是空行的意思
                # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
                new_socket.send(response)  # 发送给浏览器的响应报文数据
        else:
            # 代码执行到此,说明文件存在,返回200状态信息
            # 需要将数据封装成http响应报文格式的数据
            response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
            response_header = "Server:PWS/1.0\r\n"  # 响应头
            response_body = file_data  # 响应体
            response = (response_line + response_header + '\r\n').encode('utf-8') + response_body  # 这里加的这个\r\n是空行的意思
            # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
            new_socket.send(response)  # 发送给浏览器的响应报文数据

        finally:
            new_socket.close()  # 关闭服务于客户端的这个新套接字

if __name__ == '__main__':  #判断是否为主模块代码
    main()
实例四:静态web服务器--多任务版
import socket
import threading
def handle_client_request(new_socket):
    recv_data = new_socket.recv(4096)  # 接受客户端的请求信息
    if len(recv_data) == 0:  # 判断接收的数据是否为0
        new_socket.close()
        return
    recv_content = recv_data.decode('utf-8')  # 对二进制数据进行解码
    request_list = recv_content.split(' ', 2)  # 对数据按照空格进行分隔
    request_path = request_list[1]  # 取分隔后存入元组中第2个元组成员数据
    print(recv_data)
    if request_path == '/':
        request_path = '/index.html'  # 判断请求的是否是根目录,如果是根目录则设置返回首页
    try:
        with open("static" + request_path, 'rb') as file:  # 这里的这个file表示打开的文件对象 rb表示以二进制模式读取,兼容图片数据
            file_data = file.read()
            # 提示:with open 关闭文件这步操作不需要程序员来完成,系统帮助我们完成
    except Exception as e:
        # 代码执行到此,说明没有请求的该文件,返回404状态信息
        response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
        response_header = "Server:PWS/1.0\r\n"  # 响应头
        with open('static/error.html', 'rb') as file:  # 读取404页面数据
            file_data = file.read()
            response_body = file_data
            response = (response_line + response_header + '\r\n').encode('utf-8') + response_body  # 这里加的这个\r\n是空行的意思
            # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
            new_socket.send(response)  # 发送给浏览器的响应报文数据
    else:
        # 代码执行到此,说明文件存在,返回200状态信息
        # 需要将数据封装成http响应报文格式的数据
        response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
        response_header = "Server:PWS/1.0\r\n"  # 响应头
        response_body = file_data  # 响应体
        response = (response_line + response_header + '\r\n').encode('utf-8') + response_body  # 这里加的这个\r\n是空行的意思
        # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
        new_socket.send(response)  # 发送给浏览器的响应报文数据

    finally:
        new_socket.close()  # 关闭服务于客户端的这个新套接字
def main():
    tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建tcp服务端套接字
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) #设置端口号复用
    tcp_server_socket.bind(('',8000)) #绑定端口号
    tcp_server_socket.listen(128)#设置监听
    while True:
        new_socket, ip_port = tcp_server_socket.accept()  # 等待接受客户端的连接请求
        #代码执行到此说明连接建立成功
        sub_thread=threading.Thread(target=handle_client_request,args=(new_socket))#创建子线程
        sub_thread.setDaemon(True)#设置成为守护主线程
        sub_thread.start()#启动子线程对应的任务

if __name__ == '__main__':  #判断是否为主模块代码
    main()
静态web服务器-面向对象开发
import socket
import threading

class HttpWebServer(object):
    def __init__(self):
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建tcp服务端套接字
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)  # 设置端口号复用
        tcp_server_socket.bind(('', 8000))  # 绑定端口号
        tcp_server_socket.listen(128)  # 设置监听
        self.tcp_server_socket=tcp_server_socket
    @staticmethod
    def handle_client_request(new_socket):
        recv_data = new_socket.recv(4096)  # 接受客户端的请求信息
        if len(recv_data) == 0:  # 判断接收的数据是否为0
            new_socket.close()
            return
        recv_content = recv_data.decode('utf-8')  # 对二进制数据进行解码
        request_list = recv_content.split(' ', 2)  # 对数据按照空格进行分隔
        request_path = request_list[1]  # 取分隔后存入元组中第2个元组成员数据
        print(recv_data)
        if request_path == '/':
            request_path = '/index.html'  # 判断请求的是否是根目录,如果是根目录则设置返回首页
        try:
            with open("static" + request_path, 'rb') as file:  # 这里的这个file表示打开的文件对象 rb表示以二进制模式读取,兼容图片数据
                file_data = file.read()
                # 提示:with open 关闭文件这步操作不需要程序员来完成,系统帮助我们完成
        except Exception as e:
            # 代码执行到此,说明没有请求的该文件,返回404状态信息
            response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
            response_header = "Server:PWS/1.0\r\n"  # 响应头
            with open('static/error.html', 'rb') as file:  # 读取404页面数据
                file_data = file.read()
                response_body = file_data
                response = (response_line + response_header + '\r\n').encode(
                    'utf-8') + response_body  # 这里加的这个\r\n是空行的意思
                # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
                new_socket.send(response)  # 发送给浏览器的响应报文数据
        else:
            # 代码执行到此,说明文件存在,返回200状态信息
            # 需要将数据封装成http响应报文格式的数据
            response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
            response_header = "Server:PWS/1.0\r\n"  # 响应头
            response_body = file_data  # 响应体
            response = (response_line + response_header + '\r\n').encode('utf-8') + response_body  # 这里加的这个\r\n是空行的意思
            # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
            new_socket.send(response)  # 发送给浏览器的响应报文数据

        finally:
            new_socket.close()  # 关闭服务于客户端的这个新套接字

    def statr(self):
        while True:
            new_socket, ip_port = self.tcp_server_socket.accept()  # 等待接受客户端的连接请求
            # 代码执行到此说明连接建立成功
            sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket))  # 创建子线程
            sub_thread.setDaemon(True)  # 设置成为守护主线程
            sub_thread.start()  # 启动子线程对应的任务


def main():
    #创建web服务器
    web_server=HttpWebServer()
    #启动服务器
    web_server.statr()



if __name__ == '__main__':  #判断是否为主模块代码
    main()
通过命令行启动静态web服务器

获取终端命令行参数命令

import sys

变量=sys.argv 通过该代码将会获取终端命令行 python3 后面的命令参数 并作为一个列表进行返回

通过终端命令行启动,动态绑定端口号
import socket
import sys
import threading

class HttpWebServer(object):
    def __init__(self,port):
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建tcp服务端套接字
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)  # 设置端口号复用
        tcp_server_socket.bind(('', port))  # 绑定端口号
        tcp_server_socket.listen(128)  # 设置监听
        self.tcp_server_socket=tcp_server_socket
    @staticmethod
    def handle_client_request(new_socket):
        recv_data = new_socket.recv(4096)  # 接受客户端的请求信息
        if len(recv_data) == 0:  # 判断接收的数据是否为0
            new_socket.close()
            return
        recv_content = recv_data.decode('utf-8')  # 对二进制数据进行解码
        request_list = recv_content.split(' ', 2)  # 对数据按照空格进行分隔
        request_path = request_list[1]  # 取分隔后存入元组中第2个元组成员数据
        print(recv_data)
        if request_path == '/':
            request_path = '/index.html'  # 判断请求的是否是根目录,如果是根目录则设置返回首页
        try:
            with open("static" + request_path, 'rb') as file:  # 这里的这个file表示打开的文件对象 rb表示以二进制模式读取,兼容图片数据
                file_data = file.read()
                # 提示:with open 关闭文件这步操作不需要程序员来完成,系统帮助我们完成
        except Exception as e:
            # 代码执行到此,说明没有请求的该文件,返回404状态信息
            response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
            response_header = "Server:PWS/1.0\r\n"  # 响应头
            with open('static/error.html', 'rb') as file:  # 读取404页面数据
                file_data = file.read()
                response_body = file_data
                response = (response_line + response_header + '\r\n').encode(
                    'utf-8') + response_body  # 这里加的这个\r\n是空行的意思
                # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
                new_socket.send(response)  # 发送给浏览器的响应报文数据
        else:
            # 代码执行到此,说明文件存在,返回200状态信息
            # 需要将数据封装成http响应报文格式的数据
            response_line = "HTTP/1.1 200 OK\r\n"  # 响应行
            response_header = "Server:PWS/1.0\r\n"  # 响应头
            response_body = file_data  # 响应体
            response = (response_line + response_header + '\r\n').encode('utf-8') + response_body  # 这里加的这个\r\n是空行的意思
            # 由于response_body已经是二进制数据了所以只需要把前面的内容转成二进制然后进行拼接即可
            new_socket.send(response)  # 发送给浏览器的响应报文数据

        finally:
            new_socket.close()  # 关闭服务于客户端的这个新套接字

    def statr(self):
        while True:
            new_socket, ip_port = self.tcp_server_socket.accept()  # 等待接受客户端的连接请求
            # 代码执行到此说明连接建立成功
            sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket))  # 创建子线程
            sub_thread.setDaemon(True)  # 设置成为守护主线程
            sub_thread.start()  # 启动子线程对应的任务


def main():
    params=sys.argv#命令行中的参数
    if len(params)!=2:  #判断用用户输入的端口号是否正确
        print('输入的端口号格式不正确')
        return 
    if not params[1].isdigit():#判断输入的端口号格式是否是数字格式
        print('输入的端口号格式不正确')      
        return 
    #代码执行到这里,说明命令行参数的个数一定是两个,并且第二个参数是由数字组成的字符串
    port=int(params[1])  #取出端口号那个字符串并转为整数类型

    #创建web服务器
    web_server=HttpWebServer()
    #启动服务器
    web_server.statr()



if __name__ == '__main__':  #判断是否为主模块代码
    main()

前端

html

表单的标签以及相关元素应用

<meta charset="UTF-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

用户名:

**
**

密码:

**
**

性别:

**
**

爱好:

学习

睡觉

打游戏

**
**

照片:

**
**

个人描述:

**
**

籍贯

**
**

表单的提交

form表单属性的设置

action属性:设置表单数据提交的地址

method属性:设置表单提交的方式,一般由‘get’和post方式 不区分大小写

get方式提交表单数据到web服务器 是以地址栏的方式(以查询参数的方式)提交给服务器,能看到提交的数据 不安全

post方式提交表单数据会放到请求体里面 较为安全

表单元素属性设置

name属性:设置表单元素的名称,该名称是提交数据时的参数名

value属性:设置表单元素的值,该值是提交数据时参数名对应的值

实例:

<meta charset="UTF-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

用户名:

**
**

密码:

**
**

性别:

**
**

爱好:

学习

睡觉

打游戏

**
**

照片:

**
**

个人描述:

**
**

籍贯

**
**

爬虫方向

爬虫的概念

网络爬虫就是模拟客户端(主要指浏览器)发送网络请求,接收请求,一种按照一定规则,自动抓取互联网信息的程序

原则上,只要客户端(浏览器)能做的事情,爬虫都是可以做的

爬虫也只能获取到客户端(浏览器)所战术出来的数据

爬虫的分类

根据爬取网站的数量不同可以分为:

通用爬虫:如搜索引擎

聚焦爬虫:如12306抢票,或专门抓取某一个(某一类)网站数据

根据是否以获取数据为目的可以分为:

功能性爬虫:给你喜欢的明星投票 点赞

数据增量爬虫:如招聘信息

根据url地址和对应的页面内容是否改变,数据增量爬虫可以分为:

基于url地址变化,内容也随之变化的数据增量爬虫

url地址不变,内容变化数据增量爬虫

爬虫的流程

1.获取一个url

2.向url发送请求,并获取响应(需要http协议)

3.如果从响应中提取url,则继续发送请求获取响应

4.如果从响应中提取数据,则将数据进行保存

http和https协议

http和https的概念和区别

https比http更安全,但是性能更低一点(几乎感觉不到)

http:超文本传输协议,默认端口是80

超文本 :是指超过文本但不仅限于文本,还包括图片 音频 视频等文件

传输协议:是指使用共用约定的固定格式来传递或转换成字符串的超文本内容

https:http+ssl(安全套接字层),即带有安全套接字层的超文本传输协议,默认端口号:443

ssl对于传输内容(超文本,也就是请求体或响应体)进行加密

可以打开浏览器访问一个url,右键检查,点击net work,点选一个url,查看http协议的形式

爬虫特别关注的请求头和响应头

特别关注的请求头字段

Content-Type

Host(主机域名和端口号)

Connection(链接类型):如keep-alive

Upgrade-Insecune-Requests(升级为HTTPS请求) 会重定向到https协议的对应网站

User-Agent:用户代理,提供系统信息和浏览器信息

Referer(页面跳转处):表示是从那个页面跳转过来的 防盗链(图片/视频)

Cookie(Cookie) 状态保持

Authorization(用于表示HTTP协议中需要认证资源的认证信息,如:web前端中用于jwt认证)

特别关注的响应头字段

Set-Cooke(对方服务器设置cookie到用户浏览器的缓存)

常见的响应状态码

200:成功

302:跳转,新的url在响应的Location头中给出

303:浏览器对于POST请求的响应进行重定向至新的url

307:浏览器对于GET的响应重定向至新的url

403:资源不可用 服务器理解客户的请求,但拒绝处理它(没有权限)

404:找不到该页面

500:服务器内部错误

503:服务器由于维护或负载过重未能应答,在响应中可能会携带Retry-After响应头,有可能是因为爬虫频繁访问url

使服务器忽视爬虫请求,最终返回503响应状态码

在爬虫中,可能该站点的开发人员或者运维人员为了阻止数据被爬虫轻易获取,可能在状态码上做手脚,也就是说返回的状态码

并不一定就是真实情况,比如:服务器已经识别出你是爬虫,但为了让你疏忽大意,所以照样返回状态码200,但响应体中并没有数据

因此爬虫过程中,所有的状态码都不可信,一切以能否从抓包中得到响应中获得到的数据为准

浏览器的运行过程

http请求的过程

1.浏览器拿到域名对应的ip后,先想地址栏中的url发起请求,并获取响应

2.在返回的响应内容(html)中,会带有css js 图片等url地址,以及ajax代码,浏览器按照响应内容中的顺序,依次发送其他的请求,并获得 相应的响应

3.浏览器每获取一个响应就对战术出的结果进行添加(加载),js css 等内容会修改页面的内容,js也可以重新发送请求,获取响应

4.从获第一个响应并在浏览器中展示,直到最终获取全部响应,并在展示的结果中添加内容或修改---这个过程叫做浏览器的渲染

注意:

在爬虫中,爬虫只会请求url地址,对应的拿到url地址对应的响应(该响应的内容可以是html,css,js 图片等)

浏览器渲染出来的页面和爬虫请求的页面很多时候并不一样,因为爬虫不具备渲染的能力(后续课程会借助其它工具或包来帮助爬虫对应响应内容进行渲染)

浏览器最终展示的结果是由多个url地址分别发送的多次请求对应的响应共同渲染的结果,所以字爬虫中,需要以发送请求的一个url地址对应的响应为准来进行数据的提取

requests模块

该模块主要用于发送请求获取响应,该模块有很多替代模块 如 urllib模块,但是在工作中最多的还是使用requests模块,requests的代码简洁易懂,相对于臃肿的urllib模块,使用requests编写的爬虫代码将会少很多,而且实现某一功能将会很简单,因此建议大家掌握该模块的使用

requests模块的作用:

发送http请求,获取响应数据

requests模块是一个第三方模块,需要在你电脑上额外安装

在Windows或者linux系统中都可以通过指令进行安装

pip/pip3 install requests

也可以在pycharm中直接在软件中setting中进行安装

requests模块的使用

import requests//引入requests模块
url='http://www.baidu.com'//设置url链接
#通过requests模块调用get请求
response=requests.get(url)
#打印获取到的源码的数据
print(response.text)
返回的字符编码问题

观察上面代码运行结果发现,有好多的乱码:这是因为编码解码使用的字符集不同造成的,我们尝试使用下面的方法来解决中文乱码问题

import requests
url='http://www.baidu.com'
response=requests.get(url)

#方法一:手动设定编码格式
response.encoding='utf-8'  //通过 response.encoding='编码格式'来设定编码格式  之后response.text将会按照设置格式显示
#打印获取到的源码的数据
print(response.text)
#response.content是存储的bytes类型的响应源码,可以进行一个decode操作
#方法二:
print(response.content.decode('编码格式'))//如果不设置编码格式 默认设置为utf-8

1.response.text是requests模块按照chardet模块推测出的编码字符集进行解码的结果

2.网络传输的字符串都是bytes类型的,所以 response.text=response.content.decode('推测出的编码字符集')

3.我们可以在网页源码中搜索 charset,尝试参考该编码字符集,注意存在不准确的情况

response.text和response.content的区别:

response.text

类型:str

解码类型:requests模块自动根据HTTP头部对响应的编码作出有根据的推测,推测的文本编码

response.content //是储存的bytes类型(二进制)的响应源码

类型:bytes

解码类型:没有指定

通过 response.content进行decode 来解决中文乱码

response.content.decode('编码格式') 不写编码格式的话默认设定为utf-8

response.content.decode("GBK") 手动采用GBK等格式

常见的编码字符集

utf-8

gbk

gb2312

ascii

iso-8859-1

response响应对象的其它常用属性或方法

response.url 响应的url ; 有时候响应的url和请求的url并不一致 例:百度的重定向

response.status_code 响应状态码

response.request.headers 响应对应的请求头

response.headers 响应头

response.request._cookies 响应对应请求的cookie ;返回cookieJar类型

response.cookies 响应的cookie(经过了set-cookie动作(也就是根据请求头进行设置的操作);返回cookieJar类型)

response.json() 自动将json 字符串类型的响应内容转换为python对象(字典类型或者列表类型)

requests模块发送请求

发送一个带header的请求

headers参数接收字典形式的请求头 请求头字段名作为key,字段对应的值作为value

例:

import requests
url='http://www.baidu.com'
#构建一个请求头字典
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62'
}
#发送带请求头的请求
response1=requests.get(url,headers=headers)
print(response1.content.decode())
发送带参数的请求

我们在使用百度搜索的时候进程发现url地址中会有一个?,该问号后边的就是请求参数,又叫查询字符串

两种方式:

1.在url携带参数

直接对含有参数的url发起请求

import requests
url='https://www.baidu.com/s?wd=python'

#构建请求头字典
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62'
}
#发送带请求头的请求
response1=requests.get(url,headers=headers)
print(response1.content.decode())

2.通过params携带参数字典

简单修改url 就是把问号等一些除了参数以外的先给改好

构建请求参数字典

向url发送请求的时候带上参数字典,参数字典设置给params 即params=创建的参数字典名

import requests
url='https://www.baidu.com/s?'

#构建请求头字典
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62'
}
#构建参数字典
params={
'wd':'python'
}
#发送带请求头的请求
response1=requests.get(url,headers=headers,params=params)
在headers参数中携带cookie

网站经常利用请求头中的Cookie字段作为用户访问状态的保持,比如看爱奇艺会员视频需要登录会员账号,那么我们可以在headers参数中添加Cookie,模拟普通用户的请求。我们以github登录为例:

1.输入账号密码点击登录后,访问一个需要登录后才能获取正确内容的url,比如点击右上角的Your profile 进行访问 2.通过检查查看访问的url,再从请求头中找需要的headers与Cookie 然后进行构建字典

例:

import requests
url='https://github.com/hyms0419?tab=projects&type=beta'
#创建请求头
headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)     Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62',
'cookie': '_octo=GH1.1.1786168147.1650647421;         _device_id=72c7ece4c9d7e0ebc76b64ba3402a528; has_recent_activity=1; user_session=S6JoY4UwrogXaQlYZOREwjrIii1qqg_2IPLcBgyR78uTx71T; __Host-user_session_same_site=S6JoY4UwrogXa

代码说明:

从浏览器中复制User-Agent和Cookie

浏览器中的请求头字段和值与headers参数中必须一致

headers请求参数字典中的Cookie键对应的值是字符串

cookies参数的使用

除了通过在headers参数中携带cookie,我们也可以使用专门的cookies参数

1.cookies参数的形式:字典

cookies={“cookie的name”:“cookie的value”}

该字典对应请求头中cookie字符串,以分号 空格 分割每一对字典键值对

等号左边是一个cookie的name,对应cookies字典的key

等号右边对应cookies字典的value

2.cookies参数的使用方法

cookies={“cookie的name”:“cookie的value”}
reponse=response.get(url,cookies)

3.将cookie字符串转换为cookies参数所需的字典

temp='复制过来的那个cook代码'

cookie_list=temp.split('; ')
cookies={}
for cookie in cookie_list:
    cookies[cookie.split('=')[0]]=cookie.split('=')[-1]

或者采用装逼的下面写法

cookies_dict={cookie.split('-')[0]:cookie.split('-')[-1]for cookie in cookies_str.split(';')}:
4.cookieJar对象转换为cookies字典的方法

使用request获取的对象,具有cookies属性,但该属性值是一个cookieJar类型,包含的对方服务器设置在本地的cookie,我, 需要主动将其传唤为cookies字典 但一般很少使用这种方法

转换方法:
    cookies_dict=requests.utils.dict_cookiejar(response.cookies)
其中 response.cookies返回的就是cookieJar类型对象  requests.utils.dict_cookiejar函数用来返回cookies字典
需要注意的是这种转换方法一般 for域名内容会丢失 也就是xxxfor www.baidu.com会丢失

将字典转换回去的方法:
    jar_cookies=requests.utils.cookiejar_from_dict(dict_cookies)  

5.注意 cookie一般是有过期时间的,一旦过期需要重新获取

超时参数timeout的使用

我们平时上网时,经常会遇到网络波动,这个时候,一个请求登录很久可能仍然没有任何结果,在爬虫过程中,一个请求如果很久都没有结果 默认等待时间是180s,就会让整个项目的效率变得特别低,这时候我们就需要对象请求进行强制要求,让他必须在特定的时间返回结果,否则就报错

超时参数timeout的使用方法

response=requests.get(url,timeout=3)

timeout=3表示 发送请求后3秒返回响应,否则抛出异常

ip代理proxies的使用

代理ip是一个ip,指向的是一个代理服务器

代理服务器能够帮我们向目标服务器转发请求

正向代理和反向代理的区别

1.从发送请求的一方角度来区分正向或反向代理

2.为浏览器或客户端(发送请求的一方)转发请求的叫做正向代理 浏览器知道最终处理请求的服务器的真实ip地址 例如vpn

3.不为浏览器或客户端转发请求,而是为最终处理请求的服务器转发请求的,叫做反向代理 浏览器不知道服务器的真实地址 如 nginx

代理ip(代理服务器)的分类

1.根据代理ip的匿名程度,代理i可以分为以下三类:

透明代理(Transparent Proxy) :透明代理虽热可以直接隐藏你的ip地址,但是还是可以查到你是谁。

匿名代理(Anonymous Proxy):使用匿名代理,别只知道你使用了代理,无法知道你是谁。

高匿名代理(Elite Proxy 或 High Anonymous Proxy) :高匿名代理让别根本无法发现你是在用代理,所以是最好的选择,毫无疑问使用高匿名代理效果最好 爬虫一般都是使用高匿名代理

2.根据网站所使用的协议不同,需要使用相应协议的代理服务,从代理服务请求使用的协议可以分为:

http代理:目标url为http协议

https代理:目标url为https协议

socks隧道代理(例如sock5代理)等:

1.socks代理只是简单的传递数据包,不关心是何种应用协议(FTP、HTTP/HTTPS等)

2.socks代理比http/https代理耗时少

3.sock 代理可以转发http和https的请求

proxies代理参数的使用

为了让服务器以为不是同一个客户端在请求:为了防止频繁向同一个域名发送请求被封ip,所有我们需要使用代理ip

用法:

response=requests.get(url,proxies=proxies)

proxies的形式:字典

例:

proxies={

"http":"http://12.34.45.79:1234",

"http":"http://23.34.45.79:1269",

}

注意:如果proxies字典中包含有多个键值对,发送请求时将按照url地址的协议来选择使用相应的代理ip

实例:

import requests
url='http://www.baidu.com'
proxies={
    'http':'223.68.190.136:9091',

}
response=requests.get(url,proxies=proxies)
print(response.text)
verify的使用

有些网站的CA证书没有经过受信任的根证书颁发机构认证 会导致访问时提示网站不安全之类的内容

使用verify参数忽略CA证书

方法 :

response=requests.get(url,verify=False)

requests模块发送post请求

那些地方我们会用到POST请求?

1.登录注册(在web工程师看来POST比GET更安全,url地址中不会暴露用户的账号密码信息)

2.需要传输大文本内容的时候(POST请求对数据长度没有要求)

因此我们爬虫也需要在这两个地方进行模拟浏览器发送POST请求

requests发送post请求的方法

response=requests.post(url=url,data=data,headers=headers)

data参数接收一个字典

requests模块发送post请求函数的其它参数和它发送get请求的参数完全一致

两种形式

当发现在response headers中的 为Content-Type: application/x-www-form-urlencoded; charset=UTF-8 则是常规传递data

response=requests.post(url=url,data=data,headers=headers)

当发现在response headers中的 为content-type: application/json 说明 data传递形式为json型 这是需要以下面方式传递data

response=requests.post(url=url,json=data,headers=headers)
POST请求练习
import requests
import json
class FY(object):
    def __init__(self,word):
       self.url='https://fanyi.baidu.com/v2transapi?from=zh&to=en'
       self.headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36','Cookie':'BIDUPSID=F6DBB896AA1C4A27C9098CD4FF07AC4E; PSTM=1656914930; BAIDUID=F6DBB896AA1C4A27201A43BF2F144ECB:FG=1; H_PS_PSSID=36835_36553_36459_36255_36726_36454_36414_36691_36167_36816_36569_36679_36773_36743_36760_36768_36765_26350_36864; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=0; PSINO=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1658212513; Hm_lpvt_64ecd82404c51e03dc91cb
post数据来源

1.固定值 抓包比较不变值

2.输入值 通过过抓包比较 根据自身变化值

3.预设值-静态文件 需要提前重静态html中获取

4.预设值-发请求 需要对指定地址发送请求

5.在客户端(一般为浏览器)生成的

requests.session进行状态保持

requests模块中Session类能够自动处理发送请求获取响应过程中产生的cookie,进而达到状态保持的目的

requests.session的作用以及应用场景

作用:自动处理cookie,即下一次请求会带上上一次请求的cookie

应用场景:自动处理连续的多次请求过程中产生的cookie

requests.session的使用方法

session=requests.session() #实例化session对象

response=session.get(url,headers,........)

response=session.post(url,data,........)

session对象发送get或post 请求的参数,与requests模块发送请求的参数完全一致

数据的提取

响应内容的分类

在发送请求获取响应之后,可能会存在多种不同类型的响应内容;而且很多时候我们需要响应内容中的一部分数据

结构响应类型:
json字符串

可以使用re、json等模块来提取特定数据

json字符串的例子如下图

xml字符串

可以使用re lxml 等模块来提取特定数据

xml字符串的例子如下

非结构化的响应内容
html字符串

可以使用re ;lxml等模块来提取特定数据

认识xml

xml是一种可扩展标记语言,样子和html很相似,功能更专注于对传输和储存数据

对应的树结构例子:

xml和html的区别

html:超文本标记语言 为了更好的显示数据,侧重点是为了显示

xml:可扩展标记语言,为了传输和存储数据,侧重点是在于数据内容的本身

常用的数据解析方法

MySql数据库

数据库的分类

关系型数据库

是指采用了关系模型来组织数据的数据库,简单来说,关系模型指的就是二进制表格模型,好比Excel文件中的表格,强调使用表格方式存储数据

关系型数据库中核心元素:

数据行

数据列

数据表

数据库(数据表的集合)

常用的关系型数据库:

Oracle

Microsoft SQL Server

MySQL

SQLite 这个数据库是用在手机中的 体积非常小

非关系型数据库

又被称为 NoSQL(Not Only SQL) 意为不仅仅是SQL,对NoSQL 最普遍的定义是"非关联型的" 强调 Key Value 的方式储存数据

常用的非关系数据库:

MongoDB

Redis

最后修改:2023 年 08 月 14 日
如果觉得我的文章对你有用,请随意赞赏