python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归

一 闭包

  • 闭包:通过封装一个函数, 该函数内部又封装一个函数用于返回,使得得到一个无法直接修改值的临时变量,只能通过函数调用修改。
  • 闭包的作用:代替全局变量,避免全局变量的滥用。
  • 闭包的优缺点:1)优点是可以避免全局变量;2)缺点是该临时变量会一直占用内存。 但是内存都非常大,所以缺点往往可以忽略它。

例如,下图是使用全局变量,当别人导入该包,是可以得到该全局变量account_amount,然后进行修改,那么这样你的金额就有风险了。
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第1张图片

使用闭包代替。

在讲闭包之前,首先先讲nonlocal的作用。nonlocal的作用是,要想在内部函数修改外部函数的变量值,需要在内部函数中使用nonlocal声明该变量,才能使用。
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第2张图片

# 使用nonlocal关键字修改外部函数的值
def outer(num1):

    def inner(num2):
        # nonlocal num1
        num1 += num2
        print(num1)

    return inner

fn = outer(10)
fn(10)

例如,当我们注释掉nonlocal关键字,会报错:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第3张图片

上面了解nonlocal后,我们使用闭包实现ATM小案例。

# 使用闭包实现ATM小案例
def account_create(initial_amount=0):

    def atm(num, deposit=True):
        nonlocal initial_amount
        if deposit:
            initial_amount += num
            print(f"存款:+{num}, 账户余额:{initial_amount}")
        else:
            initial_amount -= num
            print(f"取款:-{num}, 账户余额:{initial_amount}")

    return atm

# 调用account_create后,相当于得到一个全局变量initial_amount
atm = account_create()

# 往后调用atm函数,就是对initial_amount形参的操作。
atm(100)
atm(200)
atm(100, deposit=False)

结果:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第4张图片

上面看到,不管别人如何导包,都无法得到变量initial_amount,因为此时它是一个形参,要想修改initial_amount的值,只能通过调用返回的函数。

二 装饰器

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第5张图片
最简单的实现就是:

print("我睡觉了")
time.sleep(random.randint(1, 5))
print("我起床了")

但是,这种太low了,我们可以使用装饰器进行实现。

# 装饰器的一般写法(闭包)
def outer(func):
    def inner():
        print("我睡觉了")
        func()
        print("我起床了")

    return inner


def sleep():
    import random
    import time
    print("睡眠中......")
    time.sleep(random.randint(1, 5))

fn = outer(sleep)
fn()

结果:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第6张图片

上图看到,我们将sleep传给形参,这样就相当于一个全局变量,即通过闭包实现了装饰器。

下面我们来升级一下装饰器的写法,叫做装饰器的语法糖写法。
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第7张图片

# 装饰器的快捷写法(语法糖)
def outer(func):
    def inner():
        print("我睡觉了")
        func()
        print("我起床了")

    return inner

@outer
def sleep():
    import random
    import time
    print("睡眠中......")
    time.sleep(random.randint(1, 5))

sleep()

结果是一样的。
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第8张图片

上面看到,使用装饰器的语法糖写法,通过在sleep前声明@outer关键字,当我们调用sleep()时,相当于将sleep()函数作为参数传给了outer()函数,然后调用outer函数。

三 单例模式

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第9张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第10张图片

单例模式实现:

str_tools_py.py:

class StrTools:
    pass

str_tool = StrTools()

test.py:

from str_tools_py import str_tool

s1 = str_tool
s2 = str_tool

print(id(s1))
print(id(s2))

在这里插入图片描述

四 工厂模式

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第11张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第12张图片

"""
演示设计模式之工厂模式
"""

class Person:
    pass

class Worker(Person):
    pass

class Student(Person):
    pass

class Teacher(Person):
    pass


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


pf = PersonFactory()
worker = pf.get_person('w')
stu = pf.get_person('s')
teacher = pf.get_person('t')

五 多线程并行执行概念

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第13张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第14张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第15张图片

六 多线程编程

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第16张图片

"""
演示多线程编程的使用
"""
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()

    # # 等待线程结束并回收
    # sing_thread.join()
    # dance_thread.join()
    # 
    # # 测试主线程是否退出
    # print("main结束")

结果,就是两个线程一直在打印,这个打印顺序是无序的,看CPU的调用顺序决定,CPU先调用谁,那么就哪个线程执行。
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第17张图片

七 Socket服务端开发

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第18张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第19张图片

服务端编程步骤:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第20张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第21张图片

"""
演示Socket服务端开发
"""
import socket

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

# 2 绑定ip地址和端口
socket_server.bind(("localhost", 8888))

# 3 监听端口
socket_server.listen(1)
# listen方法内接受一个整数传参数,表示接受的客户端链接数量

print(f"等待客户端连接,服务器端口是:8888")

# 4 等待客户端链接。
# 这里没有客户端连接时会阻塞
# result: tuple = socket_server.accept()
# conn = result[0]        # 客户端和服务端的链接对象
# address = result[1]     # 客户端的地址信息
conn, address = socket_server.accept()

# accept方法返回的是二元元组(链接对象, 客户端地址信息)
# 可以通过 变量1, 变量2 = socket_server.accept()的形式,直接接受二元元组内的两个元素
# accept()方法,是阻塞的方法,等待客户端的链接,如果没有链接,就卡在这一行不向下执行了

print(f"接收到了客户端的链接,客户端的信息是:{address}")

while True:
    # 5 接受客户端信息,要使用客户端和服务端的本次链接对象,而非socket_server对象
    # recv客户端没有接收到数据也会阻塞,这里只演示1个客户端的例子.
    data: str = conn.recv(1024).decode("UTF-8")
    # recv接受的参数是缓冲区大小,一般给1024即可
    # recv方法的返回值是一个字节数组也就是bytes对象,不是字符串,可以通过decode方法通过UTF-8编码,将字节数组转换为字符串对象

    print(f"客户端发来的消息是:{data}")
    # 6 发送回复消息
    msg = input("请输入你要和客户端回复的消息:")
    if msg == 'exit':
        break
    conn.send(msg.encode("UTF-8"))

# 7 关闭链接
conn.close()
socket_server.close()

下载网络调试助手作为客户端:
https://github.com/nicedayzhu/netAssist/releases。

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第22张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第23张图片

八 Socket客户端开发

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第24张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第25张图片

那么我们在pycharm启动上面服务端和客户端的代码,注意在pycharm是可以同时运行的。

客户端打印:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第26张图片

服务端打印:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第27张图片

九 正则表达式-基础方法

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第28张图片

match():
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第29张图片

这里注意,match()要求开头就要匹配,如果开头都不匹配,那么直接返回None,例如上图的s='1python xxx’与’python’比较,因为开头就不匹配了。所以直接返回None。

search():
搜索整个字符串,返回找到匹配的第一个字符串。类似C++的string.find()。

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第30张图片

findall():搜索整个字符串,返回找出的全部匹配项。
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第31张图片

"""
演示Python正则表达式re模块的3个基础匹配方法
"""
import re

s1 = "python itheima python python"
s2 = "1python itheima python python"

# 1 match 从头匹配
result = re.match("python", s1)
print(result)
# print(result.span())
# print(result.group())

result = re.match("python", s2)
print(result)

# 2 search 搜索匹配
result = re.search("python", s1)
print(result)

# 3 findall 搜索全部匹配
result = re.findall("python", s1)
print(result)


python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第32张图片

十 正则表达式-元字符匹配

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第33张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第34张图片

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第35张图片

案例。
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第36张图片

"""
演示Python正则表达式使用元字符进行匹配
"""
import re

# 1. 查找小写b到e,大写F-Z,数字3-9的所有字符
s1 = "itheima1 @@python2 !!666 ##itccast3"
result = re.findall(r'[b-eF-Z3-9]', s1)   # 字符串前面带上r的标记,表示字符串中转义字符无效,就是普通字符的意思
print(result)

print("==========================")

# 2 匹配账号,只能由字母和数字组成,长度限制6到10位
# ^[0-9a-zA-Z]:表示开头第一个字符只能是数字和字母
# {6,10}:表示上一个规则出现6-10次。
# $:表示我要结尾,后面不能有内容
r = '^[0-9a-zA-Z]{6,10}$'
s2 = '123456_'
s21 = '123456'
print(re.findall(r, s2))
print(re.findall(r, s21))

print("==========================")

# 3 匹配QQ号,要求纯数字,长度5-11,第一位不为0
# ^[1-9]:表示第一个字符是1-9的数字。
# [0-9]:表示第二个字符串是0-9的数字
# {4,10}:表示前一个规则的字符出现4-10次。注意,本规则是修饰上一个规则,
# 这个4包含上一个规则([0-9]),所以是{4,10},而不是{3,10}。上面的{6,10}同理
r = '^[1-9][0-9]{4,10}$'
s3 = '123453678'
s31 = '12345'
print(re.findall(r, s3))
print(re.findall(r, s31))

print("==========================")

# 4 匹配邮箱地址,只允许qq、163、gmail这三种邮箱地址
# [email protected]
# [email protected]
# 我们假设邮箱地址是[email protected]的形式,反正内容可以出现很多次,例如:
# {内容}.{内容}.{内容}.{内容}.{内容}.{内容}.{内容}.{内容}@{内容}.{内容}.{内容}
# 1)^[\w-]:表示开头只能是单词字符,然后-表示也可以出现它,即表示第一个字符可以出现 单词字符 和 -。
# 2)+:表示前一个规则可以出现1到无数次。
# 3)(\.[\w-]+):外层括号表示这个括号内是一整个规则; \.表示点,\只是转义;  [\w-]+表示单词字符和-可以出现1到无数次,即代表内容。
#       所以这里意思就是:第一个字符是., 然后后面的内容是单词字符可以出现1到无数次。
#       注意(个人理解),这里的+应该只修饰[\w-],而不是修饰\.[\w-],即不带上点(.),因为[\w-]只代表一个字符,
#       如果带上点的话,那么(\.[\w-]+)就表示:.内容.内容.内容... 一直这样,而内容[\w-]只代表一个字符,
#       例如:.a.b.c 所以这是不合理的。

# 4)*:表示上一个规则可以出现0-无数次,即.内容可以是0-无数次。
# 5)@(qq|163|gmail):匹配 @ 加上qq、163、gmail其中的一种。
# 6)(\.[\w-]+):和第3)点一样。
# 7)+:表示前一个规则可以出现1到无数次。
# 8)$:表示结尾。
# 9)注意,我们看到,最外层加上了大括号,r'(xxx)',这是因为如果不加,findall会把结果安装每个小规则分成多个元素返回
r = r'(^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$)'
s4 = '[email protected]'
s41 = '[email protected]'
s42 = 'a.b.c.d.e.f.g@163'
s43 = '[email protected]'
print(re.match(r, s4))
print(re.match(r, s41))
print(re.match(r, s42))
print(re.match(r, s43))

print("==========================")
# 9)注意,我们看到,最外层加上了大括号,r'(xxx)',这是因为如果不加,findall会把结果安装每个小规则分成多个元素返回
r44 = r'^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$'
s44 = '[email protected]'
print(re.findall(r44, s44))

结果:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第37张图片

十一 递归

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第38张图片
先讲几个与文件目录相关的接口:

def test_os():
    """演示os模块的3个基础方法"""
    print(os.listdir("D:/test"))        	# 列出路径下的内容
    # print(os.path.isdir("D:/test/a"))   # 判断指定路径是不是文件夹
    # print(os.path.exists("D:/test"))    # 判断指定路径是否存在

我们以递归找文件为例,下面是文件目录结构:
python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第39张图片

"""
演示Python递归操作
需求:通过递归,找出一个指定文件夹内的全部文件

思路:写一个函数,列出文件夹内的全部内容,如果是文件就收集到list
如果是文件夹,就递归调用自己,再次判断。

"""
import os


def test_os():
    """演示os模块的3个基础方法"""
    print(os.listdir("C:/Users/asus/Desktop/python-code/递归"))        # 列出路径下的内容
    # print(os.path.isdir(C:/Users/asus/Desktop/python-code/递归"))   # 判断指定路径是不是文件夹
    # print(os.path.exists(C:/Users/asus/Desktop/python-code/递归"))    # 判断指定路径是否存在


def get_files_recursion_from_dir(path):
    """
    从指定的文件夹中使用递归的方式,获取全部的文件列表
    :param path: 被判断的文件夹
    :return: list,包含全部的文件,如果目录不存在或者无文件就返回一个空list
    """
    print(f"当前判断的文件夹是:{path}")
    file_list = []
    if os.path.exists(path):
        for f in os.listdir(path):
            new_path = path + "/" + f
            if os.path.isdir(new_path):
                # 进入到这里,表明这个目录是文件夹不是文件
                file_list += get_files_recursion_from_dir(new_path)
            else:
               file_list.append(new_path)
    else:
        print(f"指定的目录{path},不存在")
        return []

    return file_list

if __name__ == '__main__':
    file_lists = get_files_recursion_from_dir("C:/Users/asus/Desktop/python-code/递归")
    for f in file_lists:
        print(f)

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归_第40张图片

你可能感兴趣的:(python学习,python,开发语言)