第七章 函数进阶
7.1 函数的作用域
7.1.1 初识作用域
- 在Python当中,是以函数作为作用域的,在不同函数中生成和创建的变量,所能够使用的范围也只能在函数包含的代码块内,不同函数之间的变量互不先关,也不能互相调用。
python
def func1():
name = "张三"
print(name)
def func2():
name = "李四"
print(name)
func1()
func2()
张三
李四
- 在同一个函数内,也就是在一个作用域内,变量可以相互调用
python
def func():
if 2 > 1:
name = "张三" # 确保先定义后调用,如果条件不满足则不会被定义
print(name)
func()
张三
7.1.2 全局作用域和局部作用域
- 通俗来讲,在函数外定义的变量叫做全局变量,作用域叫做全局作用域,而在函数内定义的变量叫做局部变量,作用域为局部作用域。
python
def func():
if 2 > 1:
name = "张三"
print(name) # 打印"张三"一次
return name # 将name值作为函数的返回值
name = func() # 函数func()运行后的返回值赋值给name
print(name) # 根据返回值打印"张三"第二次
张三
张三
- 在局部作用域中调用一个变量时,会先在局部作用域下进行查找,如果找不到,则在向上级作用域进行查找
python
name = "李四"
age = 18
gender = "male"
def func():
if 1 < 2:
name = "张三"
print(name)
print(age) # 在局部作用域没有age,在全局作用域查找age并在调用函数时打印
print(gender) # 在局部作用域没有gender,在全局作用域下查找gender并在调用函数时打印
func()
print(name) # 查找全局作用域的变量,并打印
print(age) # 查找全局作用域的变量,并打印
print(gender) # 查找全局作用域的变量,并打印
张三
18
male
李四
18
male
- 在函数中设置全局变量,变量的值在函数内反射改变也会影响到全局变量。
python
name = "李四"
age = 18
gender = "male"
def func():
global age # 将age定义为全局变量,因此在该函数中修改改变量,也会体现到全局中
if 1 < 2:
name = "张三"
age = 19
print(name)
print(age)
print(gender)
func()
print(name)
print(age)
print(gender)
张三
19
male
李四
19
male
7.1.3 Global关键字
- 如果定义一个全局变量为不可变的数据类型,如果函数内部进行修改的话,则需要通过global关键字进行声明,如果定义了一个全局变量为可变类型的数据,那么在函数内部可以直接基于可变联系相关方法进行修改。
python
name = "李四"
age = 18
gender = "male"
data_list = [1, 2, 3]
def func():
global age
if 1 < 2:
name = "张三"
age = 19
data_list.append(666) # 修改data_list列表,添加666元素
print(name)
print(age)
print(gender)
func()
print(name)
print(age)
print(gender)
print(data_list)
张三
19
male
李四
19
male
[1, 2, 3, 666]
7.1.4 nolocal关键字
python
name = "王二"
age = 19
gender = "male"
def outer():
name = "李四"
age = 18
def inner():
name = "张三"
print(name) # 局部变量存在name变量,打印"张三"
print(age) # inner()作用域中不存在局部变量,因此去上一级outer(),发现outer()的局部变量,打印age为18
print(gender) # 局部变量没有找到,去上级也没有找到,去全局找到变量值,打印"male"
inner() # 必须要调用该函数,否则调用outer()时执行outer()的时候不会调用inner()
outer() # 执行函数
张三
18
male
- 函数内赋值修改全局的变量值:
python
name = "王二"
age = 19
gender = "male"
def outer():
name = "李四"
age = 18
def inner():
global name
name = "张三" # 该赋值变量name,name被定义成全局变量,因此全局name随着该赋值值变为张三,而outer()中的name没有被改变。
print(name)
print(age)
print(gender)
inner()
print(name)
outer()
print(name)
张三
18
male
李四
张三
- 函数内赋值,不修改全局变量,修改上级变量,声明nonlocal 变量
python
name = "王二"
age = 19
gender = "male"
def outer():
name = "李四"
age = 18
def inner():
nonlocal name
name = "张三" # name赋值"张三",name变量时nonlocal变量,因此outer()中的name被赋值为"张三",但是全局的name没有发生改变。
print(name)
print(age)
print(gender)
inner()
print(name)
outer()
print(name)
张三
18
male
张三
王二
7.2 函数的嵌套
7.2.1 嵌套
- 函数在定义和调用时跟变量一样存在作用域的问题
python
def func(): # 全局作用域中定义了func函数
print("Hello")
def outside(): #全局作用域中定义了outside函数
func() # 定义了局部作用域,调用了函数func()函数
outside() # 执行该函数的时候,内部的func()函数调用,查找局部作用域,是否存在该函数,不存在该函数在全局作用域中查找到该函数,因此输入"hello"
def func(): # 在全局作用域又定义了func()函数,覆盖掉了最开始定义的func()函数
print("你好")
outside() # 再次执行该函数的时候,内部的func()函数调用,查找局部作用域,是否存在该函数,不存在该函数在全局作用域中查找到该函数,全局的func()函数已被覆盖,因此打印"你好"
Hello
你好
- 函数嵌套后,因为内部函数存在,因此不会再查找外部函数执行,输出结果都是相同的,即函数作为局部变量一样,只有在函数内部作用域才能被调用,也就是函数的嵌套
python
def func(): # 全局作用域中定义了func函数
print("Hello")
def outside(): #全局作用域中定义了outside函数
def func(): # 本作用域存在func()函数,因此在第8行调用func()的时候始终调用局部组作用域的函数,打印"我是被嵌套的函数"
print("我是被嵌套的函数")
func() # 定义了局部作用域,调用了函数func()函数
outside() # 执行该函数的时候,内部的func()函数调用,查找局部作用域,打印"我是被嵌套的函数"
def func(): # 在全局作用域又定义了func()函数,覆盖掉了最开始定义的func()函数,此时覆盖无效,因为局部作用域中存在对应的func()函数
print("你好")
outside() # 执行该函数的时候,内部的func()函数调用,查找局部作用域,打印"我是被嵌套的函数"
我是被嵌套的函数
我是被嵌套的函数
7.2.2 嵌套相关作用域问题
python
name = "张三"
def outside():
name = "李四"
def inner():
print(name)
inner() # inner被调用,打印name,inner中未见name变量,去上级outside()找,找到"李四",打印
outside()
李四
python
name = "张三"
def outside():
name = "李四"
def inner():
print(name)
return inner
v1 = outside() # 结果v1为返回的inner字符串,与下一行括号组成一个函数inner()
v1() # 相当于执行函数inner(),打印name,在局部作用域找不到name,去上级找到,打印"李四"
李四
python
def outside():
name = "李四"
def inner():
print(name)
return [inner, inner]
v1 = outside() # 返回值为列表[inner,inner]
v1[0]() # 根据索引,找到字符串inner,结合(),执行inner()函数,打印name,在局部与中没有找到,去上级找,查到上级name为李四,打印"李四"
v1[1]() # 根据索引,找到字符串inner,结合(),执行inner()函数,打印name,在局部与中没有找到,去上级找,查到上级name为李四,打印"李四"
李四
李四
7.2.3 嵌套函数
- 嵌套函数:如果一个函数在内部代码中调用了本身,那么就认为该函数是嵌套函数。
python
def num(n):
print(n)
if int(n / 2) == 0:
return n
else:
return num(int(n / 2)) # 该语句调用了函数自己
res = num(10)
print(res)
10
5
2
1
1
7.3 闭包
7.3.1 闭包的基本概念
- Python中的闭包从标签形式上定义为:如果在一个内部函数里,对外部函数作用域但不是全局作用域的变量进行引用(简言之,就是在嵌套函数的环境下,内部函数引用了外部函数的局部变量),呢么内部函数就认为是闭包。
- 尽可能避免避免对全局作用域的变量的影响。
python
def outer(a1):
def inner(a2):
return a1 + a2
return inner
v1 = outer(5)
print(v1)
v2 = v1(10)
print(v2)
<function outer..inner at 0x00000283FFE14D60>
15
7.3.2 闭包的应用场景
- 闭包是一个很大的应用场景在于尽可能的避免使用全局变量,以免对全局作用域产生污染,外部函数时可以为内部函数提供一个封闭的环境,调用外部函数时传入相应的参数生成变量,内部函数可以引用外部函数生成局部变量。
python
name = "张三"
def outer(name):
# 给外部函数设置一个参数,内部函数如果没有在自己的局部作用域找到参数,则去外层函数的局部作用域,
# 外层局部作用域没有则去查找全局变量,此时,函数的参数为name,因此不会去全局作用域查找name
def inner():
print(name)
return inner
v1 = outer("李四") # 执行outer,返回值为inner赋值给v1
print(v1)
v1()
<function outer..inner at 0x0000024418C54D60>
李四
7.4 装饰器
7.4.1 装饰器
- 在Python中装饰器(decorator)的功能是将装饰的函数作为参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数。
python
# 第一种方法
def login():
print("交换机开始登录")
print("交换机正在登录")
print("交换机登录成功")
return "huawei"
v1 = login()
print(v1)
交换机开始登录
交换机正在登录
交换机登录成功
huawei
python
# 第二种方法,定义闭包函数
def login():
print("交换机正在登录")
return "huawei"
def outer(func):
def inner():
print("交换机开始登录")
res = func() # func是形参
print("交换机登录成功")
return res
return inner
v1 = outer(login) # 调用outer函数,参数是login
v2 = v1()
print(v2)
交换机开始登录
交换机正在登录
交换机登录成功
huawei
- 语法糖
- Python中语法糖(syntactic sugar),就是在计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,由更好的可读性。
python
def outer(func):
def inner():
print("交换机开始登录")
res = func() # func是形参
print("交换机登录成功")
return res
return inner
@outer # 将login作为outer函数的参数执行outer函数,outer函数也被称为装饰器,通过@加上装饰器函数,后续执行函数的时候,将需要执行的函数名作为装饰器的参数执行。
def login(): # 加了装饰器的login函数实际上是执行了outer(login),执行的结果是返回值inner函数,即此处login()=inner()
print("交换机正在登录")
return "huawei"
v2 = login()
print(v2)
配合可变长参数使用:
python
def outer(func):
def inner(*args, **kwargs):
print("交换机开始登录")
res = func(*args, **kwargs) # func是形参
print("交换机登录成功")
return res
return inner
@outer # 将login作为outer函数的参数执行outer函数,outer函数也被称为装饰器,通过@加上装饰器函数,后续执行函数的时候,将需要执行的函数名作为装饰器的参数执行。
def login(a1): # 加了装饰器的login函数实际上是执行了outer(login),执行的结果是返回值inner函数,即此处login(a1)=inner(a1),a1是形参
print("交换机正在登录")
return a1
print(login.__name__) # 打印结果为inner
v1 = login("huawei")
print(v1)
inner
交换机开始登录
交换机正在登录
交换机登录成功
huawei
7.4.2 fuctools
_ _name_ _
和_ _doc_ _
- 通过函数
__name__
和函数__doc__
分别可以显示函数的名称和函数内部的注释说明 - 示例一:
python
def func():
"""
这是一个funnc函数
"""
pass
print(func.__name__)
print(func.__doc__)
输出结果:
python
func
这是一个funnc函数
- 示例二:
python
def outer(func):
def inner(*args, **kwargs):
"""
我是敌人
"""
print("交换机开始登录")
res = func(*args, **kwargs) # func是形参
print("交换机登录成功")
return res
return inner
@outer # 将login作为outer函数的参数执行outer函数,outer函数也被称为装饰器,通过@加上装饰器函数,后续执行函数的时候,将需要执行的函数名作为装饰器的参数执行。
def login(a1): # 加了装饰器的login函数实际上是执行了outer(login),执行的结果是返回值inner函数,即此处login(a1)=inner(a1),a1是形参
"""
我是好人
"""
print("交换机正在登录")
return a1
print(login.__name__)
print(login.__doc__) # 显示程序中的文本
# v1 = login("huawei")
# print(v1)
输出结果:
python
inner
我是敌人
- 示例三:引入functools模块
python
import functools
def outer(func):
@functools.wraps(func) #此处添加装饰器functools
def inner(*args, **kwargs):
"""
我是敌人
"""
print("交换机开始登录")
res = func(*args, **kwargs) # func是形参
print("交换机登录成功")
return res
return inner
@outer # 将login作为outer函数的参数执行outer函数,outer函数也被称为装饰器,通过@加上装饰器函数,后续执行函数的时候,将需要执行的函数名作为装饰器的参数执行。
def login(a1): # 加了装饰器的login函数实际上是执行了outer(login),执行的结果是返回值inner函数,即此处login(a1)=inner(a1),a1是形参
"""
我是好人
"""
print("交换机正在登录")
return a1
print(login.__name__)
print(login.__doc__) # 显示程序中的文本
# v1 = login("huawei")
# print(v1)
结果显示:结果显示与不加functools不同,显示"我是好人"
python
login
我是好人
7.5 匿名函数
-
基于Lambda定义的函数格式为:lambda"参数:函数体"
-
其中
-
参数:支持任意参数
-
函数体:只能够支持单行的代码
-
返回值:默认将函数单行执行的结果返回出来。
f = lambda x: x + x # 该函数没有名字,参数为x,返回值为x+x,即"参数:返回值"的形式
v1 = f(10)
print(v1)
-
20
7.5.1 三元计算
-
结果 = 条件成立 if 条件 else 条件不成立
-
不采用三元运算:
python
if x > 5:
def func(x):
return x + x
res = func(10)
print(res)
else:
def inner(x):
return x * x
res = inner(10)
print(res)
Enter a number: 4
100
Enter a number: 620
- 三元运算实现以上功能:
- 结果 = 条件成立 if 条件 else 条件不成立
python
x = int(input('Enter a number: '))
res = lambda x: x + x if x < 5 else x * x # res为函数类型,当条件成立,res返回值为x+x,条件不成立,res返回值为x*x
v1 = res(x)
print(v1)
Enter a number: 4
8
Enter a number: 10100
7.6 生成器
7.6.1 迭代器
python
data_list = [1, 2, 3, 4]
data_iter = iter(data_list) # data_iter是一个列表迭代器
print(type(data_iter))
print(next(data_iter)) # 迭代器可以通过next的方式调用其中元素
print(next(data_iter))
print(next(data_iter))
print(next(data_iter))
<class 'list_iterator'>
1
2
3
4
7.6.2 生成器
- 生成器是一种特殊的迭代器
- 当一个函数内部出现了yield字样,那么此时这个函数就不再是一个普通的函数,而是一个生成器函数。生成器函数在调用的时候不会执行内部代码。执行生成器函数会返回一个生成器的对象,然后正对该生成器对象,可以通过next方法来执行生成器内部代码
python
def func():
print("Hello")
print("World")
yield 4
print("Done")
print("nice to meet you")
v1 = func() # func函数反悔了一个生成器函数对象
print(type(v1))
print(v1)
- 生成器会记录当前程序运行的状态,如果代码在执行过程中遇到yield字样,则会将程序终止,并且将yield后面的值返回出来,下次再使用next方法执行内部代码时,会接着上次运行的位置继续向下运行,直到生成器函数内的代码全部执行完毕。
python
def func():
print("Hello")
print("World")
yield 4
print("Done")
print("nice to meet you")
yield "你好"
print("last")
v1 = func()
print(next(v1))
print("."*50)
print(next(v1))
Hello
World
4
...
Done
nice to meet you
你好
7.6.3 生成器的应用场景
1. 做一个简单的秒表
python
import time
def func():
s = 0
while True:
yield s
s += 1
time.sleep(1)
v1 = func()
while True:
print(next(v1))
- 结果:持续逐个打印数字
0
1
2
...
2. 逐一生成数据
- 使用循环生成1000个三位数,并存储在data_list里表中,打印列表
python
import random
def func():
n = 0
data_list = []
while n < 10:
res = random.randint(100, 999)
data_list.append(res)
n += 1
print(data_list)
func()
[571, 554, 953, 109, 251, 634, 302, 142, 386, 809]
- for循环实现
python
import random
def func():
data_list = []
for i in range(10):
data_list.append(random.randint(100,999))
print(data_list)
func()
[246, 818, 690, 424, 154, 451, 421, 740, 381, 424]
- 生成器打印三位数,一个print语句一个三位数,不占用内存空间
python
import random
def func():
n = 0
while n < 10:
res = random.randint(100, 999)
yield res
n += 1
v1 = func()
print(next(v1))
print(next(v1))
434
630
3. 流量控制
-
统计第一次和第二次的流量统计的平均值
import time
traffic_info = [1233, 2235, 4236, 5237, 7239, 8240]
traffic_iter = iter(traffic_info)def traffic_monitor(interval):
traffic_first = next(traffic_iter)
time_first = time.time() # 记录当前时间
time.sleep(interval) # 程序暂定间隔时间
traffic_second = next(traffic_iter) # 提取下一个流量
time_second = time.time()
time.sleep(interval)
res = (traffic_second - traffic_first) / (time_second - time_first)
yield res # 函数返回res,但是函数作为迭代器,下次运行返回第二次数值g = traffic_monitor(3)
print(next(g))
333.8757675215772
- 加入循环,使得后续每两次的流量统计也能进行统计
python
import time
traffic_info = [1233, 2235, 4236, 5237, 7239, 8240]
traffic_iter = iter(traffic_info)
def traffic_monitor(interval):
traffic_first = next(traffic_iter)
time_first = time.time() # 记录当前时间
time.sleep(interval) # 程序暂定间隔时间
while True: # t通过该循环来获取第三次数据和第二次数据之间的平均值
traffic_second = next(traffic_iter) # 提取下一个流量
time_second = time.time()
time.sleep(interval)
res = (traffic_second - traffic_first) / (time_second - time_first)
yield res # 函数返回res,但是函数作为迭代器,下次运行返回第二次数值
traffic_first = traffic_second
time_first = time_second
time.sleep(interval) # 等待一定的时间,以免下次进入循环时,time_first 、 time_second的值差在执行第15句的时候为0,导致分母为零
g = traffic_monitor(3)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
333.882186499591
333.430202551403
166.80730389426577
333.5984909916254
166.79326184347846
- 流量控制
python
import time
traffic_info = [1233, 2235, 4236, 5237, 7239, 8240]
traffic_iter = iter(traffic_info)
def traffic_monitor(interval):
traffic_first = next(traffic_iter)
time_first = time.time() # 记录当前时间
time.sleep(interval) # 程序暂定间隔时间
while True: # t通过该循环来获取第三次数据和第二次数据之间的平均值
traffic_second = next(traffic_iter) # 提取下一个流量
time_second = time.time()
time.sleep(interval)
res = (traffic_second - traffic_first) / (time_second - time_first)
yield res # 函数返回res,但是函数作为迭代器,程序暂停在此处,下次运行从此处开始,返回第二次数值。
traffic_first = traffic_second # 将第二次的值赋值变成下次计算的第一个值
time_first = time_second # 将第二次的值赋值变成下次计算的第一个值
time.sleep(interval) # 等待一定的时间,以免下次进入循环时,time_first 、 time_second的值差在执行第15句的时候为0,导致分母为零
def traffic_control(interval):
traffic = traffic_monitor(interval)
while True:
res = next(traffic)
time_first = time.time()
yield res # 返回值后,程序暂停在此处,下次键入next后,从此处开始运行。
time_last = time.time()
if(time_last - time_first)<interval:
time.sleep(interval - (time_last - time_first))
g = traffic_control(3)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
333.94535497936386
222.29714626831958
111.19819377086986
222.40125885996704
111.19662993958487
7.6.4. send方法
python
def func():
print("Hello")
print("World")
v1 = yield 2
print(v1)
print("." * 50)
print("2c")
print("Done")
v2 = yield 3
print(v2)
print("." * 50)
print("你好")
v1 = func()
print(v1.send(None)) # 第一个必须设置None值
print(v1.send(20))
Hello
World
2
20
...
2c
Done
3
7.6.5. For循环遍历生成器
python
def func():
print("Hello")
print("World")
v1 = yield 2
print(v1)
print("." * 50)
print("2c")
print("Done")
v2 = yield 3
print(v2)
print("." * 50)
print("你好")
v1 = func()
for i in v1:
print(i)
Hello
World
2
None
...
2c
Done
3
None
...
你好
7.7 推导式
- 推导式是python中提供了一个非常方便的功能,可以通过一行代码实现创建数据类型的同时初始化一些值。
- 将1到101的数字都放入到列表l1中,使用for循环实现
python
l1 = []
for i in range(1, 101):
l1.append(i)
print(l1)
- 推导式实现以上需求,一行代码即可实现生成列表
python
l1 = [i for i in range(1, 101)] # 通过for循环遍历range,让后将i放入到列表l1
print(l1)
- 推导式生成字典
python
switch_info = {f"CE{i}": {"ip": f"192.168.100.{i}", "username": "admin", "password": "Huawei@123"}
for i in range(1, 101)}
print(switch_info)
- 推导式生成生成器对象
- 在注意事项:在使用列表,字典,集合来使用推导式生成数据时,可以直接返回生成好大的数据,但是如果元组使用推导式生成数据,返回的是一个生成器对象。
python
t1 = (i for i in range(1,101)) # 不会生成元组,而是生成生成器对象
print(t1)
print(next(t1))
<generator object at 0x0000022A3FC96B00>
1
- 推导式也支持嵌套
python
l1 = [(i, j) for i in range(1, 5) for j in range(i)] # 返回(i,j)元组
print(l1)
[(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3)]
- 推导式支持添加判断条件
python
l1 = [(i, j) for i in range(1, 5) for j in range(i) if i > 2] # i>2的时候才会输出(i,j)
print(l1)
[(3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3)]
- 案例
- 使用return返回数据,如果数据需要计算会先计算再返回。
- 因此以下函数运算完之后,i=4,x是result语句传递的2,最终结果是5个8的列表
python
def num():
return [lambda x: i * x for i in range(5)] # for语句循环了5次,全部执行玩不,才返回,整句生成了[函数,函数,函数,函数,函数],此时i=4
result = [m(2) for m in num()] # m就是函数,执行结果放入到里表result中,x=2执行lambda语句
print(result)
[8, 8, 8, 8, 8]
- 如果是元组,发回的时生成器对象,不是元组。
python
def num():
return (lambda x: i * x for i in range(5)) # for语句循环了5次,但是结果是生成器对象,因此不能完全执行完成,此时返回的结果不是元组而是生成器对象
result = [m(2) for m in num()] # for循环在执行生成器对象中的代码,因此显示结果如下。
print(result)
[0, 2, 4, 6, 8]
7.8 模块
- 为了避免一个文件过于庞大,需要根据功能来进行拆分,如果说将其中的一部分功能封装在其他的一个py文件当中,那么此时这些py文件就叫做模块,如果说功能太多,那么我们也可以创建一个文件夹,用多个py文件来封装这个功能,此时的这个文件夹我们可以把他叫做包。
- 模块的分类:
- 内置模块
- 之定义模块
- 第三方模块
- 一般在引入模块时候有两种方式:
- import xxx
- from xxx import xxx
7.8.1 引入模块的路径问题
- sys.path 可以查看到python默认查找相关模块的路径
python
import sys
print(sys.path)
- sys.path.append可以向默认查找路径中添加新的路径
- 除了这些模块查找的路径外,会最优在当前执行文件目录下查找,因此要避免在当前目录下出现 名称与内置模块相同的py文件
7.8.2 引入模块时的其他问题
- 模块重名可以通过as取别名
python
import random as ra # 引入模块通过as取别名,后续调用可以采用别名,也可以重名时候通过别名区分。
print(ra.randint(1, 10))
-
主文件
- 在执行py文件时,当前文件(模块)
__name__
="__main__"
- 如果是导入的其他模块,则
__name__
等于对应的模块名
- 在执行py文件时,当前文件(模块)
-
在项目实际开发过程中,一般咋记住文件会写上main标记,但不是绝对的,因为其他文件在开发调用的时候也可能会有main
-
正常情况下,在导入模块是,会执行该模块的内部的代码。如果在该文件下执行,那么
__name__
就等于__main__
,如果是在导入时执行,那么__name__
等于模块名。
7.8.3 第三方模块
-
Python之所以流行,其中一个非常重要的原因就是支持非常丰富的第三方库,这里说的第三方库指的就是第三方模块。使用第三方模块,即使用其他人写好的开源代码来直接使用。
-
第三方模块必须先安装才能使用,常见的安装方法有以下几种
- pip
pythonpip install 模块名
-
源码安装
-
Wheel格式
7.8.4 常见内置模块
-
random
- randint:在一个范围内随机取一个整数
pythonimport random res = random.randint(1,10) # 在1,10的区间(包含1,10)随机取一个整数 print(res)
3
- uniform:在一个范围内随机取一个小数
pythonimport random res = random.uniform(1,10) # 在1,10的区间随机取一个小数 print(res)
6.379375473602465
- choice:在列表中随机挑选一个值
pythonimport random res = random.choice([1,2,3,4,5,6,7,8,9]) # 在列表中随机挑选一个值 print(res)
8
- sample
pythonimport random res = random.sample(range(1,10),2) # 可以在列表中实际取出两个元素 print(res)
[6, 9]
- shuffle
pythonimport random l1 = [1, 2, 3, "a", "b", "c"] random.shuffle(l1) # 该函数没有返回值,随机将里表中的元素重新排列 print(l1)
['b', 3, 'a', 2, 'c', 1]
-
hashlib
-
hash算法的特征:
- 雪崩效应:微小的原内容差别,加密结果完全 不同
- 单向不可逆:从结果无法推到原数据内容。
- 随机输入,固定输出:
-
示例:hashlib模块加密
pythonimport hashlib hash_obj = hashlib.md5("张三".encode('utf-8')) hash_obj.update("admin".encode('utf-8')) print(hash_obj.hexdigest()) # 显示结果,结果是将hash因子"张三"与"admin"一起进行hash计算,得出结果。
496116f287e138b9c1dbec515272a7be
-
-
time
- time
pythonimport time res = time.time() # 返回结果是一个时间戳,从1970年1月1日到现在时间经过的秒数 print(res)
1711823470.377047
- timezone
pythonimport time res = time.timezone # 输出结果为28800,是按照s计算,显示当前东八区(按秒计算) print(res)
-28800
- sleep
pythonimport time time.sleep(1) # 让程序暂停1s
-
datetime
- timetime.now:获取当前时间
pythonimport datetime t = datetime.datetime.now() print(type(t)) print(t)
<class 'datetime.datetime'>
2024-03-31 13:17:05.120115
- timezone+timedelta:设置和获取时区信息
pythonfrom datetime import datetime, timezone, timedelta v1 = timedelta(hours=8) t1 = timezone(v1) print(t1)
UTC+08:00
- utcnow:显示utc类型时间,显示结果+8小时才是我国时间
pythonfrom datetime import datetime t1 = datetime.utcnow() print(t1)
2024-03-31 05:33:04.027470
- 时间相加
pythonfrom datetime import datetime,timedelta t1 = datetime.now() print(t1) t2 = timedelta(hours=1) print(t1+t2)
2024-03-31 13:37:28.496658
2024-03-31 14:37:28.496658
- 时间相减
pythonfrom datetime import datetime, timedelta t1 = datetime.now() t2 = datetime.utcnow() v1 = timedelta(hours=5) t3 = t1 - t2 # 可以是now()格式的时间与utcnow()格式的时间相减,结果等于多少小时 t4 = t1 - v1 # 可以是now()格式的时间与timedelta()时间相减,结果等于减去多少小时,显示时间格式。 print(t3) print(t4)
8:00:00
2024-03-31 08:40:18.389056
- str类型转换为Datetime类型
pythonfrom datetime import datetime, timedelta msg = "2022-8-22" t1 = datetime.strptime(msg, "%Y-%m-%d") print(t1) print(type(t1))
2022-08-22 00:00:00
<class 'datetime.datetime'>
- datetime类型转换为str类型
pythonfrom datetime import datetime, timedelta today = datetime.now() msg = datetime.strftime(today, "%B-%d-%Y") # 年:Y;月:m;日:d;时分秒对应大写。B是英文月份。 print(msg,type(msg))
March-31-2024 <class 'str'>
7.9 JSON格式数据
-
Json格式数据主用用途:
- 本质是一个字符串,用于网络传输
-
跨语言传输,例如:
- A系统使用N语言开发,有自己的数据类型
- B系统使用M语言开发,也有自己的数据类型
- 由于语言的不同,那么基础数据格式也都不通
- 为了方便呢数据传输,约定一个格式:Json格式,每种语言都是将自己的数据类型转换为Json格式,也可以将Json格式的数据转换为自己的数据类型
-
Python中的Json模块
- 数据类型转换为Json,称为"序列化"
pythonimport json data_list = [{"ip": "1.1.1.1", "device_type": "huawei", "role": "leaf"}, {"ip": "2.2.2.2", "device_type": "Cisco", "role": "spine"}] res = json.dumps(data_list) print(res)
[{"ip": "1.1.1.1", "device_type": "huawei", "role": "leaf"}, {"ip": "2.2.2.2", "device_type": "Cisco", "role": "spine"}]
- Json转化为数据类型,称为"反序列化"
pythonimport json data = '[{"ip": "1.1.1.1", "device_type": "huawei", "role": "leaf"}, {"ip": "2.2.2.2", "device_type": "Cisco", "role": "spine"}]' res = json.loads(data) print(res)
[{'ip': '1.1.1.1', 'device_type': 'huawei', 'role': 'leaf'}, {'ip': '2.2.2.2', 'device_type': 'Cisco', 'role': 'spine'}]
7.10 正则表达式
7.10.1 字符相关
xxxx
匹配固定内容xxxx[abc]
匹配a或者b或者c字符(单个字符空间)[^abc]
匹配除了a,b,c之外的其他字符[a-z]
匹配a-z的任意字符.
代指除换行符以外的任意字符\w
代指字母或数字或下划线(汉字)\d
代指数字\s
代指任意的空白符,包括空格、制表符
7.10.2 数量相关
*
重复0次或者多次+
重复1次或多次?
重复0次或多次,也可以跟在数量相关符号后面表示非贪婪匹配{n}
重复n次{n.}
重复n次或更多次{n,m}
重复n到m次,先匹配m次,再看是否匹配n次
7.10.3 括号(分组)
()
并不会影响到匹配的结果,而是在匹配到之后,只提取出括号当中的值,如果加了多个括号,则将多个括号的内容都提取出来,然后将每个括号中匹配到的结果作为一个元组中的元素。
7.10.4 特殊标识
-
用于验证必须是指定的内容开头和结尾
-
^
起始 -
$
结束 -
(?=)
后置肯定符,匹配后置内容,但是不匹配前置内容。- 匹配公式:
.+(?=12323_123)
,匹配时匹配字符串==##%#12323_123 中的12323_123==部分内容
- 匹配公式:
-
(?<=)
前置肯定符,匹配前置内容,不匹配后置内容。- 匹配公式:
(?=<.+)12323_123
,匹配的时匹配字符串==**##%#**12323_123内容中的##%#==部分内容
- 匹配公式:
-
前后置一起使用:
- 匹配公式:
(?<=##)%#12323(?=_123)
,同时利用前置肯定符和后置肯定符匹配字符串##%#12323_123中的%#12323部分内容,不包含起始肯定符和后置肯定符的内容。
- 匹配公式:
-
7.10.5 转义-IP地址匹配
- 由于正则表达式中
*.\{}()
等都具有特殊含义,所以如果想要在正则表达式中匹配这种特定的字符,就需要进行转义,\.
匹配.
本身。 - 匹配交换机配置脚本中所有的IP地址x.x.x.x
- x的范围为0-255,进行拆分
- 0-9:公式
(\d)
- 10-99:公式
([1-9]\d)
- 100-199:公式
(1\d\d)
- 200-255:
- 200-249:公式
(2[0-4]\d)
- 250-255:公式
(25[0-5])
- 200-249:公式
- 将
x.
匹配三次:公式{3}
- 0-9:公式
- 最后公式:
(((\d)|([1-9]\d)|(1\d\d)|(2[0-4]\d)|(25[0-5]))\.){3}(((\d)|([1-9]\d)|(1\d\d)|(2[0-4]\d)|(25[0-5]))\s)
- x的范围为0-255,进行拆分
7.10.6 re模块常见方法
- findall,获取匹配到的所有数据。
python
import re
msg = "我知道1你知道我2知道他3知道!"
res = re.findall("\d知", msg)
print(res)
['2知', '3知']
- findall,只返回条件括号中的的内容。
python
import re
msg = "我知道1你知道我2知道他3知道!"
res = re.findall(".(\d)知", msg) # 整个表达式".(\d)知"是匹配条件,但是返回结果只需要括号中的(\d)
print(res)
['2', '3']
- match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
python
import re
msg = "我知道1你知道我2知道他3知道!"
res = re.match(".\d知", msg)
print(res)
None
- search,流量整个字符串去匹配第一个,未匹配成功返回None
python
import re
msg = "我知道1你知道我2知道他3知道!"
res = re.search(".\d知", msg)
print(res)
<re.Match object; span=(7, 10), match='我2知'>
- sub,替换匹配成功的位置
python
import re
msg = "我知道1你知道我2知道他3知道!"
res = re.sub(".\d知", "猜猜",msg,1) # 替换为"猜猜",替换1个
print(res)
我知道1你知道猜猜道他3知道!
- split
python
import re
msg = "我知道1你知道我2知道他3知道!"
res = re.split(".\d知", msg) # 参数为分隔符,分割字符串成为列表
print(res)
['我知道1你知道', '道', '道!']
- 可迭代
python
import re
msg = "我知道1你知道我2知道他3知道!"
res = re.finditer(".\d知", msg)
print(type(res))
print(next(res).group())
print(next(res).group())
<class 'callable_iterator'>
我2知
他3知
7.11 函数变成项目开发规范
7.11.1 单文件应用
-
当基于Python开发简单应用时(一个Python文件可以搞定),需要注意以下几点:
- 脚本注释:
python""" Created on xxx """
- 导入模块时,按照内置模块,第三方,自定义的顺序
pythonimport time # 内置模块 import ncclient # 第三方模块 import test1 # 自定义模块
- 全局变量要大写
pythonNAME = "张三" # 全局变量大写
- 函数命名规则和函数功能注释
pythondef set_student_id(): """ 这是一个设置学号的函数 student_id:学号 :return:返回值 """ student_id = "123456"
- TODO信息:用来描述后续需要做的事情
python""" TODO: 这是第几版本的代码,还需要完善什么 """
-
部分功能代码注释
-
主文件:主文件一般不包含其他功能,只是主题程序的入口,其他空能都在其他模块中。
pythonif __name__ == '__main__': run()
7.11.2 单可执行文件
- 新创建一个项目,假设名字为叫【test】,可以创建如下文件和文件来存放代码和数据
python
test
|--app.py 文件,程序的主文件(尽量精简)
|--config.py 文件,配置文件(放相关配置信息,代码中读取配置信息,如果想要修改配置,即可以在此修改,不用去代码中注意修改了)
|--db 文件夹,存放数据
|--files 文件夹,存放文件
|--src 包,业务处理的代码
|--utils 包,公共功能
7.11.3 多可执行文件
- 新创建一个项目,假设名字为叫【test_new】,可以创建如下文件和文件来存放代码和数据
python
test
|--bin 主文件夹,存放多个主文件(可运行)
| |--app1.py
| |--app2.py
|--config 包,配置文件
| |--__init__.py
| |--settings.py
|--db 文件夹,存放数据
|--files 文件夹,存放文件
|--src 包,业务处理的代码
| |--__init__.py
|--utils 包,公共功能
| |--__init__.py