Python函数

  • Python有一些自带的函数需要进行使用和了解,一些前面了解过的函数比如inputprintformat等函数已经具体介绍过了,这里就不多介绍了

  • python的函数与其他语言的函数一样,你也可以把它看做带有名字的子程序

  • 格式:python函数使用def关键字定义,函数名可以是任何有效的python标识符

1
2
3
4
5
6
7
8
def <函数名>(<参数列表>):
<函数体>
return <返回值列表>

def f(a,b,c):
a = b+c+a;
c = a+b;
return a,c

参数传递

  • Python函数中的传递有三种
    • 指定参数传递:一开始设置传递几个参数,就传递几个参数。指定参数传递还可以按照名称传递
    • 默认参数传递:可以一开始设置一个默认参数值,使用函数时就可以不用传递设置默认参数值。
    • 可变参数传递:与其他语言类似,Python可以进行可变参数的传递
  • 指定参数传递:设置几个参数就传几个参数,也可按照参数名称传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def f(a,b,c):
a = b*c
b = a*c
return a

m = 2
n = 3
h = f(m,n,4)
print(h)
# 12
# 按照参数名称传递,就可以不用按照a,b,c这样的顺序进行传递参数
# 可以这样进行参数的传递
h = f(c = 4,a = m, b = n)
print(h)
# 12
  • 默认参数传递:在定义函数的时候给一个默认值
1
2
3
4
5
6
7
8
9
def dup(str,times = 2):
print(str*times)
# 下面是使用默认参数传递
dup("knock~")
# knock~knock~

# 下面是指定参数传递
dup("knock~",4)
# knock~knock~knock~knock~
  • 可变参数传递,允许传递未知参数的个数
  • 输入的可变参数会当做元组这一数据类型进行处理
1
2
3
4
5
6
7
8
9
10
def vfunc(a,*b):
print(type(b))
for n in b:
a+=n
return a

h = vfunc(1,2,3,4)
print(h)
<class 'tuple'>
10

返回值

  • Python函数与其他函数一样都有返回值。但是Python的函数可以返回一个值,也可以返回多个值
  • 当返回值的个数与接收返回值的变量一致时,就会按照顺序将返回值逐个赋值给接受的变量
1
2
3
4
5
6
def vfunc(a,b,c,d):
return a

h = vfunc(1,2,3,4)
print(h)
# 1
  • 当一个变量接受多个返回值时,这些多个返回值就会按照返回顺序构成一个元组,然后再赋值给接受返回值的变量
1
2
3
4
5
6
def vfunc(a,b,c,d):
return a,b,c,d

h = vfunc(1,2,3,4)
print(h)
# (1, 2, 3, 4)
  • 当接受变量与返回值不一致的时候,就会出现报错
1
2
3
4
5
6
7
8
9
def vfunc(a,b,c,d):
return a,b,c,d

h ,i = vfunc(1,2,3,4)
print(h,i)

h ,i = vfunc(1,2,3,4)
^^^^
ValueError: too many values to unpack (expected 2)

函数里变量的作用域

  • 在函数中变量用作用的范围,具体的不多介绍。介绍作用域多排错有关系,不过目前是初步学习

eval函数

  • eval函数可以将传递的参数做为命令进行执行,并返回执行后的值

    • 由于eval函数会执行用户输入的命令,这就导致了一些安全问题
    • 当然eval函数危险的同时也带来了便捷,更方便了用户与程序之间的交互功能
  • 语法:

1
eval(expression[, globals[, locals]])
  • 例如:
1
2
3
4
5
6
7
8
9
a = 7
b = eval('3*a')
print(b)
# 21

a = 7
b = eval('pow(2,2)')
print(b)
# 4
  • 还可以使用eval函数实现自动化脚本,通过与其他程序的交互实现自动计算功能

  • 比如:有一个靶机,它会发送一个算式,要求我们在短时间内计算其结果。

image-20240904092517795

  • 这时就可以使用eval函数进行快速的计算,而且考虑较少的交互功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pwn import *
context(log_level = 'debug')
p = remote("172.19.8.207",51924)
p.sendlineafter(b'1 + 1 = ',str(2))
p.sendlineafter(b'4 / 3 - 1 = ',str(0))
p.recvuntil(b'OK, then some tough ones. Be WELL PREPARED!')
while True:
a = p.recvuntil(b'=')
a = a.decode("utf-8")
b = ''
for i in a:
if i == '\n':
continue
if i == '=':
continue
if i == '/':
b += '//'
continue
b += i
print('b = ',b)
c = eval(b)
print(c)
p.send(str(int(c)).encode('utf-8'))
p.sendline(b'')
# moectf{ar1tHMETiC-IS_NOT_M@th3mATIcs1d259bb}

lambda函数

  • lambda函数:lambda可以有任意的参数,但是只有一个表达式。

  • 所以只用一行或者一句话表达出来的函数都可以使用lambda表达式写出来。

  • lambda函数也可以和def这个函数定义结合起来

  • 格式:

1
lambda x: 关于x的表达式
  • 示例1:
1
2
3
4
5
6
7
8
9
def f(x):
return x*x
# 这个函数可以修改为
f = lambda x: x*x

def f(x,y,z):
return x+y+z
# 这个函数可以修改为
f = lambda x,y,z: x + y + z
  • 示例2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def f(n):
retrun lambda a: n*a

h = f(3) # 即h = lambda a: 3*a
printf(h(11))
# 33
# 或者
def myfunc(n):
return lambda a : a * n

mydoubler = myfunc(2)
mytripler = myfunc(3)

print(mydoubler(11)) # 22
print(mytripler(11)) # 33

包、模块、库、框架

概念

  • 模块:单独的.py文件,将一组相关功能的代码写入一个单独的**.py**文件中,供别人导入使用,这个.py文件就被称作是一个模块

  • 包:有层次的文件目录结构,它定义了由n个模块或者n个子包。包含__init__.py文件目录,这个目录下一定得有__init__.py文件和其他模块或子包

  • 库:完成一定功能的代码集合,具体表现可以是包,也可以是模块(可以理解为包和模块)

  • 框架:架构层面的概念

    • 从库功能的角度看:解决一个开放性问题而设计的具有一定约束性的支撑结构
    • 通过一个框架,可以快速实现一个问题的骨架,到时候按照框架角色区填充,交互就可以完成一个质量好,维护性高的项目,例如:web框架,框架里面有FlaskDjango
  • 总结:最主要的就是包和模块,框架都指的是包和模块,个人感觉框架一般用于开发,一般用于解决一些问题,具体还没深入体会

image-20241016204505502

  • 创建包,自动会生成__init__.py的文件

包和模块的分类

  • 标准的包和模块
    • 安装Python后,自动帮我们安装好了一些模块,可以直接导入
    • 每个安装Python用户都有这些模块,在你电脑上写的系统模块的代码,在别人的电脑上仍然能运行
    • 可以满足我们基本的需求
    • 特殊:(内建包和模块),当我们再编码的过程中,Python自动帮助我们导入的模块(buildin模块)。我们使用这些功能的时候就不用再次手动导入该模块,直接使用就可以。例如:print函数就可以不用导入直接就可以使用。在Python3.x是buildins
  • 第三方的包和模块
    • 有些功能,在标准模块和包中没有实现,或者实现了但是用起来很复杂
    • 开发人员为此开发了更加方便的模块供其他人使用,在这些包中可能用到了系统的某些模块,也可能没有使用
    • 其他开发人员只要下载该模块就可以直接使用了
  • 自定义的包和模块
    • 我们自己写好的模块,如果将该模块发布给别人用,就变成了三方包/模块

包和模块的操作

包和模块的创建

  • 创建模块:即创建一个.py文件

  • 创建包:

    • 创建一个文件夹,文件夹内务必创建一个__init__.py文件
    • 其实python3.3版本往后可以不用创建,但是为了代码版本的兼容,以及做一些其他包处理操作,目前还是建议创建
  • 创建一个模块其实也就是创建一个.py文件,下图为创建一个模块的示范,右键点击对应的文件夹,然后点击新建,点击Python文件

image-20241016173620697

  • 然后为该Python文件命名,这样就可以创建一个包了

image-20241016173712670

  • 创建包,选择相应的文件夹,然后点击右键,点击python软件包

image-20241016173927246

  • 然后给包命名,这样就创建了一个Python包

image-20241016173950812

image-20241016174017409

包和模块的基本信息

  • 包和模块的名称
  • 包和模块的存放位置
  • 包和模块里面的内容

包和模块的导入

  • 接下来导入包和模块:

    • import ccc :导入单个模块和单个包里面的所有模块
    • import aaa.bbb:导入包里面的单个模块
    • import p1.sub_p.sub_xxx:导入包里的子包中的模块
    • import aaa,bbb,ccc:一次导入多个模块,也可以一个一个的导入
    • import aaaaaaaaaaaaa.bbbbbbbbbbbbb as a:导入模块,并给改模块取一个别名(更方便记忆和使用),也可以优化代码提高效率

    image-20241016193348330

    image-20241016193414172

    image-20241016193704715

    • from A import B as C :其中as C是可选操作,从A包或者A模块中导入B(函数,变量名等等)。A的范围更大,B的范围更小。包 > 模块 > 模块资源。包里面只能看到模块,但是看不到模块里面的资源,而模块中只能看到模块里面的资源
      • 所以能从包里面导入模块,导入单个、多个、起别名、包有多层级
      • 能从模块中导入模块资源,导入单个、多个、起别名、模块也有多个层级
    • from A import B,C:从一个包中导入多个模块
    • from A import B as T1,C as T2:从一个包中导入多个模块,并分别取别名,如果使用from A import B ,C as T2,这个语句只会给C取别名成T2,而不会给B取别名成T2
    • from A import *:导入包中的所有模块或者导入模块中的所有模块资源当使用该语句时,程序就会在相应的包或者相应的模块中去执行该语句,一键导入该列表变量中的变量,__all__相当于告诉哪些变量是要有用的,是需要一键导入进去的(这是在模块中导入模块资源建议最好不要一建导入,可能会造成变量名冲突或者模块名冲突

    image-20241016203657263

    在包中要一键导入应该使用下图操作:要在__init__这里写上一个__all__=[],这样一键导入的时候才会将需要的模块一键导入进去

    image-20241016203955420

    • 注意:在从模块中导入模块资源,例如from p1 import num,其中num = 111p1这个模块中被定义,但是导入之后并不能使用p1.num来引用这个num,直接使用num即可

    image-20241016202034374

image-20241016202051025

  • 应该怎么看包的多级,使用Pycharm目录查看即可,我们展开包aaa可以看到里面的文件,但是看不到子包bbb的文件,所以此时
  • bbb里面也有一个模块:
    • from aaa import abcd这个导入是正确的
    • from aaa import bbb这个也是正确的
    • from aaa import oasda这个就错误了,因为在aaa包中不能直接看到bbb子包中的oasda.py文件,这样就不能导入。如果要正确的导入,就应该使用from aaa.bbb import oasda
    • from aaa import bbb.oasda:这个语句也是不行的,要保持import后面的语句最简化

image-20241016202800965

image-20241016202750960

第三方包和模块

  • 标准模块或者包有时不能满足我们的需求,所以一些开发人员就开发了第三方库,以便满足我们的需求

  • 第三方包和模块与标准模块的区别

    • 相同点:都是为了完成某个功能需求
    • 标准模块:由官方发布,使用可靠。几乎没有bug,即使由bug也有团队及时修护,有健全的文档说明
    • 第三方模块:由网友提供,文档可能不是很健全,更新不及时或者已经不再维护更新了,功能有bug
  • 第三方模块本质就是别人写的代码给其他人使用,安装方式有两种:

    • 源码安装:自己动手去某个地方下载,然后安装到本地
    • 使用包管理器安装(相对简单):更加简单的自动化的为用户安装包管理器和模块,只需要一个或者几个命令就可以完成
  • Python包管理器几个常用的简单介绍:

    • distutils:是标准库的一部分,通过setup.py进行安装
    • setuptools:现在的包安装标准,曾经差点被废弃,现在又继续开发,自带一个easy_install安装脚本,后来出现更牛的pip安装脚本来替代它
    • 所以现在setuptools是目前的主要选择
  • 常见已发布三方包和模块的形式

    • 源码:单文件模块和多文件模块
    • .egg文件:本质上是压缩包,setuptools引入的一种格式,setuptools可以识别它,安装它
    • whl:本质是zip格式,是为了替代.egg
  • 安装包,这里主要介绍pip安装包和模块,因为经常使用pip安装,这里就重点介绍pip安装

异常处理

概念

  • 错误:没法通过其他的代码进行处理的问题
    • 语法错误:这种错误要修改代码,比如将def关键字写成了dfe,解释器无法识别
    • 逻辑错误:语法层面没有问题,但是设计逻辑出现了问题,这种问题只能,通过代码输入输出样例测试才能发现,或者是看代码发现,解释器不会给出错误提示
1
2
3
例如:
if age < 18:
print("已经成年")
  • 异常:多指程序在执行过程中,出现的未知错误,语法和逻辑都是正确的,可以通过其他代码处理修复(异常面向的是处理用户的错误输入)例如:
    • 设计除法的时候,如果用户输入除数为0,这就会出现报错
    • 列表或字典,在使用的过程中,出现的索引或者key的错误
    • 让用户输入年龄,判定是否成年。代码如下:
1
2
3
4
age = input("请输入年龄")
if int(age) >= 18:
print("你已经成年")
# 这个代码语法和语义都没问题,但是用户输入了abc,这时在执行程序的时候就会出现问题

常见的系统异常

  • 除零异常、名称异常、类型异常、索引异常、键异常、值异常、属性异常、迭代器异常、系统异常类继承树

解决异常

  • 关于异常处理的几个关键字,tryexceptelsefinally