22. 闭包

当内层函数使用外层函数的变量 同时外层函数将内存函数作为返回 此时内层函数就叫做闭包函数

def 外层函数(传入参数1):
    def 内层函数(传入参数2):
        print(f"{传入参数1}+{传入参数2}")

    return 内层函数

# 此时当我们调用外层函数
fn = 外层函数(传入参数值1)       # => return给fn的是 内层函数

# 所以当我们使用fn时 相当于使用了内层函数 此时传入参数1也固定在调用外层函数时传入的那个值
fn(传入参数值2)

当我们想在闭包中修改外层函数的变量时候 可以在内层函数中添加 nonlocal 外层变量名 即可修改

def outer(num1):
    def inter(num2):
        nonlocal num1
        num1 += num2
        print(num1)
    return inter

fn = outer(1)
fn(2)            # 得到3
fn(2)            # 得到5   因为外层变量num1已经修改为3

23. 装饰器

装饰器也是一种闭包 作用是 在不破坏函数原有代码和功能的情况下 为目标函数添加新功能

def 外层函数(目标函数):
    def 内层函数():
        想要在原函数基础上添加的功能
        原函数()
        想要在原函数基础上添加的功能
    return 内层函数

def 原函数():
    执行体(原有功能)

# 此时当调用外层函数时将原函数作为参数传入
fn = 外层函数(原函数)
fn()
def outer(func):
    def inter():
        print("睡觉了")
        func()
        print("睡醒了")
    return inter

def sleep():
    import random
    import time
    print("睡眠中..")
    time.sleep(random.randint(1,5))  # time.sleep()让程序暂停一段时间  random.randint(1,5)随机生成一个[1,5]之间的数

fn = outer(sleep)
fn()
def 外层函数(目标函数):
    def 内层函数():
        想要在原函数基础上添加的功能
        原函数()
        想要在原函数基础上添加的功能
    return 内层函数

@外层函数
def 原函数():
    执行体(原有功能)

sleep()
def outer(func):
    def inter():
        print("睡觉了")
        func()
        print("睡醒了")
    return inter

@outer
def sleep():
    import random
    import time
    print("睡眠中..")
    time.sleep(random.randint(1,5))  # time.sleep()让程序暂停一段时间  random.randint(1,5)随机生成一个[1,5]之间的数

sleep()

24. 设计模式

a. 单例模式

当我们想要将一个类(如工具类等) 只获取它唯一的类实例对象 从而能够持续复用它 而不是每次使用都重新构建一次类实例对象 节省内存和创建对象的开销

class StrTools:
    pass

t1 = StrTools()
t2 = StrTools()
print(t1,t2)

# 结果
# <__main__.StrTools object at 0x7fc8f456cd10> 
# <__main__.StrTools object at 0x7fc8f456cc80>

# 所以是分别构建了两个实例对象
class StrTools:
    pass

strtool = StrTools()
from 引用类所在文件 import StrTools

t1 = StrTools
t2 = StrTools
print(t1,t2)

# 结果同在一个地址下 这样就避免了 实现的是一个功能 但反复构建实例对象的开销以及内存的不必占用

b. 工厂模式

当需要构建大量类实例对象时 可使用统一的工厂模式构建 这样可以

  • 大批量创建对象时有统一的接口 易于维护
  • 当发生修改的时候 仅修改工厂类即可
class Person():
    pass

class Worker(Person):
    pass

class Student(Person):
    pass
    
class Teacher(Person):
    pass

worker = Worker()
student = Student()
teacher = Teacher()

worker1 = Worker()
worker2 = Worker()
# 像这样的构建方式 如果某一个类的构建方式改变 我们需要一个一个去改
class Person():
    pass

class Worker(Person):
    pass

class Student(Person):
    pass
    
class Teacher(Person):
    pass

class Factory:
    def get_person(self,p_type):
        if p_type == 'w':
            return Worker()
        elif p_type == 's':
            return Student()
        else:
            return Teacher()

f = Factory()
worker = f.get_person('w')
student = f.get_person('s')
teacher = f.get_person('t')

25. 多线程

  • 进程就是一个程序 运行在系统之上 便称他为一个运行进程 并分配进程ID方便管理
  • 线程是归属于进程的 一个进程可以开启多个线程 执行不同的工作 是进行的实际工作的最小单位
  • 操作系统可同时运行多个进程 -- 多任务运行
  • 一个进程内可运行多个线程 -- 多线程进行
  • 进程之间的内存是隔离的 也就是都拥有自己的内存空间 不可互相访问
  • 而线程之间是内存共享的 一个进程的内存空间是被它中的所有线程所共享的

多线程编程 - threading模块

import time

def sing(msg):
    while True:
        print(msg)
        time.sleep(1)

def dance(msg):
    while True:
        print(msg)
        time.sleep(1)

if __name__ == '__main__':
    sing('我在唱歌')
    dance('我在跳舞')
# 此处我的本意是想让两个交替进行执行 但是实际上因为是单线程的 只会等另一个执行完 
# 从而导致dance()并不会执行
import time
import threading

def sing(msg):
    while True:
        print(msg)
        time.sleep(1)

def dance(msg):
    while True:
        print(msg)
        time.sleep(1)

if __name__ == '__main__':
    sing_thread = threading.Thread(target=sing, args=('我在唱歌',))
    dance_thread = threading.Thread(target=dance, kwargs={"msg" : "我在跳舞"})

    # 让每一个线程开始运行
    sing_thread.start()
    dance_thread.start()
# 此时才会达到两个线程并行运行的结果

26. 网络编程

socket 进程之间通信的工具 -- 即当两个进程之间需要传递数据时 可以通过socket完成通信

socket -- 服务端开发

-- 等待其它进程的连接、可接收发来的消息、可回复消息

"""
socket服务端
"""

import socket

# 创建socket对象
socket_server = socket.socket()

# 绑定ip地址和端口
socket_server.bind(("localhost", 8080))  # bind() - 接收一个参数 这个参数必须是一个地址元组

# 监听连接
socket_server.listen(1)     # listen方法的参数表示允许多少个(整数)客户端连接到服务器,超过这个数量的连接请求会被拒绝

# 等待客户端连接
# result : tuple = socket_server.accept()   # accept方法会阻塞程序 直到有客户端连接到服务器为止
# conn = result[0]    # 获取连接对象
# address = result[1]    # 获取客户端地址

# 当有客户端连接到服务器时,accept方法会返回一个元组
# 包含两个元素:一个是新的socket对象,用于与客户端通信;另一个是客户端的地址信息
conn , address = socket_server.accept()

print("客户端连接成功,地址为:", address)

while True :
    # 接收客户端发送的数据
    data : str = conn.recv(1024).decode("utf-8")   # recv方法的参数表示一次最多接收多少字节的数据,返回值是一个字节对象,需要使用decode方法将其解码为字符串
    print("接收到客户端数据:", data)

    # 发送数据给客户端
    msg = input("请输入要发送给客户端的数据:")
    if msg == "exit" :
        print("服务器已关闭!")
        break
    conn.send(msg.encode("utf-8"))    # send方法用于发送数据给客户端  encode方法将字符串编码为字节对象
    print("数据发送成功!")

# 关闭连接
conn.close()    # 关闭与客户端的连接
socket_server.close()    # 关闭服务器socket对象

socket -- 客户端开发

-- 主动连接服务端、可发送消息、可接收消息

"""
socket客户端
"""
import socket

# 创建socket对象
socket_client = socket.socket()

# 连接服务器
socket_client.connect(("localhost", 8080))    # connect() - 接收一个参数 这个参数必须是一个地址元组

# 发送数据给服务器
while True:
    # 发送消息给服务器
    send_msg = input("请输入要发送给服务器的数据:")
    if send_msg == "exit":
        print("客户端已关闭!")
        break
    socket_client.send(send_msg.encode("utf-8"))    # send方法用于发送数据给服务器  encode方法将字符串编码为字节对象
    print("数据发送成功!")

    # 接收服务器发送的数据
    recv_data = socket_client.recv(1024).decode("utf-8")   # recv方法的参数表示一次最多接收多少字节的数据,返回值是一个字节对象,需要使用decode方法将其解码为字符串
    print("接收到服务器数据:", recv_data)

# 关闭连接
socket_client.close()

27. 正则表达式

re模块匹配

"""
正则表达式匹配
"""
import re

s = "roy is a good boy"
double = "lanlan roy lanlan lanlan"

# match函数  --  从头开始匹配
# 从字符串的起始位置开始匹配,如果匹配成功,返回一个Match对象,否则返回None

print("---match函数---")
result = re.match("roy", s)        # ("roy"是正则表达式,s是要匹配的字符串)
print(result)                      # <re.Match object; span=(0, 3), match='roy'>   span表示匹配的字符串在原字符串中的位置,match表示匹配的字符串

result = re.match("is", s)
print(result)                      # None   因为"roy"不是字符串s的起始位置,所以匹配失败,返回None


# search函数  --  从任意位置匹配
# 从字符串的任意位置开始匹配,如果匹配成功,返回一个Match对象,否则返回None

print("---search函数---")
result = re.search("is", s)
print(result)                      # <re.Match object; span=(4, 6), match='is'>   

result = re.search("lanlan ", s)
print(result)                      # None   因为"lanlan "在字符串s中不存在,所以匹配失败,返回None


# findall函数  --  查找所有匹配的字符串
# 在字符串中查找所有匹配的字符串,返回一个列表,如果没有找到匹配的字符串,返回一个空列表

print("---findall函数---")
result = re.findall("lanlan", double)
print(result)                      # ['lanlan', 'lanlan', 'lanlan']   因为字符串double中有三个"lanlan",所以返回一个包含三个"lanlan"的列表

result = re.findall("haha ", double)
print(result)                      # []   因为"haha "在字符串double中不存在,所以返回一个空列表

元字符匹配

元字符匹配

记得要在字符串前面加上r,表示原始字符串,这样就不需要对反斜杠进行转义了

单字符匹配

  • . -- 匹配除换行符以外的任意单个字符 \. - 匹配点本身
  • [] -- 匹配括号内的任意一个字符
  • \d -- 匹配任意一个数字字符 即[0-9]
  • \D -- 匹配任意一个非数字字符
  • \s -- 匹配任意一个空白字符 包括空格、制表符、换行符等
  • \S -- 匹配任意一个非空白字符
  • \w -- 匹配任意一个字母、数字或下划线字符 即[a-zA-Z0-9_]
  • \W -- 匹配任意一个非字母、数字或下划线字符

数量匹配

  • * -- 匹配前一个字符出现0次或多次
  • + -- 匹配前一个字符出现1次或多次
  • ? -- 匹配前一个字符出现0次或1次
  • {n} -- 匹配前一个字符出现n次
  • {n,} -- 匹配前一个字符出现至少n次
  • {n,m} -- 匹配前一个字符出现至少n次,至多m次

边界匹配

  • ^ -- 匹配字符串的开头
  • $ -- 匹配字符串的结尾
  • \b -- 匹配单词的边界
  • \B -- 匹配非单词的边界

分组匹配

  • | -- 匹配左右任意一个表达式
  • () -- 将()中的字符作为一个分组
import re

# 匹配账号 -- 只能由字母和数字组成  长度限制6-10位
print("---账号匹配---")
rule = '^[0-9a-zA-Z]{6,10}$'      # 代表 从字符串的开头开始匹配  只能由数字和字母组成 且[0-9a-zA-Z]字符只能出现6-10次  然后到字符串的结尾结束匹配
s = "lanlan roy"
result = re.findall(rule,s)
print(result)                     # []   因为字符串s中有空格,所以不满足规则,返回一个空列表

# 匹配QQ号 -- 只能由数字组成  长度限制5-12位  不能以0开头
print("---QQ号匹配---")
rule = '^[1-9][0-9]{4,11}$'       # 代表 从字符串的开头开始匹配  只能由数字组成 且[0-9]字符只能出现4-11次  不能以0开头  然后到字符串的结尾结束匹配
s = "0123456789"
result = re.findall(rule,s)
print(result)                     # []   因为字符串s以0开头,所以不满足规则,返回一个空列表

# 匹配邮箱地址 -- 只允许qq、163和gmail三种邮箱  账号只能由字母、数字和下划线组成
# abc.efg.hij@qq.com.cn
# abc@163.com
print("---邮箱地址匹配---")
# 当规则中有分组()时 findall函数返回的元组中 是每一个分组所匹配到的字符串
# 所以此时需要将整个规则用()括起来  这样findall函数返回的元组中 就只有一个元素  就是整个规则匹配到的字符串
# rule = r'(^\w+(\.\w+)*@(qq|163|gmail)(\.\w+)+$)'

# 或者可以直接改成用match函数来匹配
rule = r'^\w+(\.\w+)*@(qq|163|gmail)(\.\w+)+$'
s = "abc@163.com"
result = re.match(rule,s)       
print(result)                     # [('','163','.com')]   因为字符串s满足规则

28. 递归

递归是 在满足条件的情况下 函数自己调用自己的一种特殊编程技巧

需要注意的是:

  • 递归一定要有退出的条件 否则会陷入一直调用自己的无限循环
  • 同时当调用了自己后 一定要用变量去接收调用后返回的数据 否则就只是在调用 但并没获得调用的结果
import os

# 列出该路径下的内容
print(os.listdir("D:/test"))         # 结果 某文件 某文件夹...

# 判断指定路径是不是文件夹
print(os.path.isdir("D:/test/a"))    # True/False

# 判断该路径是否存在
print(os.path.exists("D:/test"))     # True/False
"""
递归目录遍历
"""

import os

def get_files_recursion_from_dir(path):
    """
    从指定的文件夹中 使用递归的方式获取所有的文件路径
    :param path : 判断的文件夹路径
    :return : list 所有的文件路径列表 如果没有文件 则返回一个空列表[]
    """

    file_list = []
    if os.path.exists(path):
        for file in os.listdir(path):
            new_path = path +"/" + file
            if os.path.isdir(new_path):
                file_list += get_files_recursion_from_dir(new_path)
            else:
                file_list.append(new_path)
    else:
        print("路径不存在")
        return []
    return file_list

if __name__ == '__main__':
    files = get_files_recursion_from_dir("D:/6")
    print(files)

    """
    最终返回
     ['D:/6/1.txt', 'D:/6/2.txt', 'D:/6/3.txt',
      'D:/6/a/a1.txt', 'D:/6/a/a2.txt', 'D:/6/a/a3.txt',
      'D:/6/a/aa/aa1.txt', 'D:/6/a/aa/aa2.txt', 'D:/6/a/aa/aa3.txt',
      'D:/6/b/b1.txt', 'D:/6/b/b2.txt', 'D:/6/b/b3.txt']
    """

更多推荐