第七章 Python-函数进阶

第七章 函数进阶

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: 6

20

  • 三元运算实现以上功能:
    • 结果 = 条件成立 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: 10

100

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__等于对应的模块名
  • 在项目实际开发过程中,一般咋记住文件会写上main标记,但不是绝对的,因为其他文件在开发调用的时候也可能会有main

  • 正常情况下,在导入模块是,会执行该模块的内部的代码。如果在该文件下执行,那么__name__就等于__main__,如果是在导入时执行,那么__name__等于模块名。

7.8.3 第三方模块

  • Python之所以流行,其中一个非常重要的原因就是支持非常丰富的第三方库,这里说的第三方库指的就是第三方模块。使用第三方模块,即使用其他人写好的开源代码来直接使用。

  • 第三方模块必须先安装才能使用,常见的安装方法有以下几种

    • pip
    python 复制代码
    pip install 模块名
    • 源码安装

    • Wheel格式

7.8.4 常见内置模块

  • random

    • randint:在一个范围内随机取一个整数
    python 复制代码
    import random
    res = random.randint(1,10)  # 在1,10的区间(包含1,10)随机取一个整数
    print(res)

    3

    • uniform:在一个范围内随机取一个小数
    python 复制代码
    import random
    res = random.uniform(1,10)  # 在1,10的区间随机取一个小数
    print(res)

    6.379375473602465

    • choice:在列表中随机挑选一个值
    python 复制代码
    import random
    res = random.choice([1,2,3,4,5,6,7,8,9])  # 在列表中随机挑选一个值
    print(res)

    8

    • sample
    python 复制代码
    import random
    res = random.sample(range(1,10),2) # 可以在列表中实际取出两个元素
    print(res)

    [6, 9]

    • shuffle
    python 复制代码
    import random
    
    l1 = [1, 2, 3, "a", "b", "c"]
    random.shuffle(l1)  # 该函数没有返回值,随机将里表中的元素重新排列
    print(l1)

    ['b', 3, 'a', 2, 'c', 1]

  • hashlib

    • hash算法的特征:

      • 雪崩效应:微小的原内容差别,加密结果完全 不同
      • 单向不可逆:从结果无法推到原数据内容。
      • 随机输入,固定输出:
    • 示例:hashlib模块加密

    python 复制代码
    import 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
    python 复制代码
    import time
    res = time.time()  # 返回结果是一个时间戳,从1970年1月1日到现在时间经过的秒数
    print(res)

    1711823470.377047

    • timezone
    python 复制代码
    import time
    res = time.timezone  # 输出结果为28800,是按照s计算,显示当前东八区(按秒计算)
    print(res)

    -28800

    • sleep
    python 复制代码
    import time
     time.sleep(1)  # 让程序暂停1s
  • datetime

    • timetime.now:获取当前时间
    python 复制代码
    import datetime
    
    t = datetime.datetime.now()
    print(type(t))
    print(t)

    <class 'datetime.datetime'>

    2024-03-31 13:17:05.120115

    • timezone+timedelta:设置和获取时区信息
    python 复制代码
    from datetime import datetime, timezone, timedelta
    
    v1 = timedelta(hours=8)
    t1 = timezone(v1)
    print(t1)

    UTC+08:00

    • utcnow:显示utc类型时间,显示结果+8小时才是我国时间
    python 复制代码
    from datetime import datetime
    
    t1 = datetime.utcnow()
    print(t1)

    2024-03-31 05:33:04.027470

    • 时间相加
    python 复制代码
    from 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

    • 时间相减
    python 复制代码
    from 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类型
    python 复制代码
    from 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类型
    python 复制代码
    from 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,称为"序列化"
    python 复制代码
    import 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转化为数据类型,称为"反序列化"
    python 复制代码
    import 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])
      • x.匹配三次:公式{3}
    • 最后公式:(((\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)

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
    """
    • 导入模块时,按照内置模块,第三方,自定义的顺序
    python 复制代码
    import time  # 内置模块
    import ncclient  # 第三方模块
    import test1  # 自定义模块
    • 全局变量要大写
    python 复制代码
    NAME = "张三"  # 全局变量大写
    • 函数命名规则和函数功能注释
    python 复制代码
    def set_student_id():
        """
        这是一个设置学号的函数
        student_id:学号
        :return:返回值 
        """
        student_id = "123456"
    • TODO信息:用来描述后续需要做的事情
    python 复制代码
    """
    TODO:
        这是第几版本的代码,还需要完善什么
    """
    • 部分功能代码注释

    • 主文件:主文件一般不包含其他功能,只是主题程序的入口,其他空能都在其他模块中。

    python 复制代码
    if __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
相关推荐
LZXCyrus10 分钟前
【杂记】vLLM如何指定GPU单卡/多卡离线推理
人工智能·经验分享·python·深度学习·语言模型·llm·vllm
Enougme14 分钟前
Appium常用的使用方法(一)
python·appium
ZZZCY200315 分钟前
华为ENSP--IP编址及静态路由配置
网络·华为
懷淰メ19 分钟前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
EasyCVR33 分钟前
私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?
大数据·网络·音视频·h.265
hummhumm33 分钟前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
hgdlip1 小时前
主IP地址与从IP地址:深入解析与应用探讨
网络·网络协议·tcp/ip
珹洺1 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
每天吃饭的羊1 小时前
python里的数据结构
开发语言·python