Python学习总结——③

第三章 高阶

1. 闭包

① 在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。

def outer(logo):
    def inner(msg):
        print(f"<{logo}>{msg}<{logo}>")
​
    return inner
​
# 将 logo 传给外部函数,此时调用外部函数,此时内部函数使用的 logo 将一直是“黑马程序员”
fn1 = outer("黑马程序员")
# 此时再调用 fn1,就是使用上一步传过来的 inner 内部函数了
# 此时可以看作是 inner("大家好呀")
fn1("大家好呀")             # <黑马程序员>大家好呀<黑马程序员>
fn1("学Python就来")         # <黑马程序员>学Python就来<黑马程序员>

② 如果修改外部函数变量的值,需要使用 nonlocal 关键字修饰外部函数的变量才可在内部函数中修改它。

def outer(num1):
    def inner(num2):
        nonlocal num1   # 使用 nonlocal 关键字修饰后才能修改
        num1 += num2
        print(num1)
​
    return inner

③ 注意

Ⅰ. 优点:

  • 无需定义全局变量即可实现,通过函数持续的访问、修改某个值。

  • 闭包使用的变量是在函数内,难以被错误的调用修改。

Ⅱ. 缺点:

  • 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存。

2. 装饰器

① 装饰器其实也是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。

② 语法

Ⅰ. 装饰器的一般写法(闭包写法),定义一个闭包函数,在闭包函数内部执行目标函数、完成功能的添加:

例子:希望给 sleep 函数,增加一个功能:

  • 在调用 sleep 前输出:我要睡觉了。

  • 在调用 sleep 后输出:我起床了。

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()

Ⅱ. 糖写法,使用 @outer 定义在目标函数 sleep 之上

def outer(func):
    def inner():
        print("我要睡觉了")
        func()
        print("我起床了")
​
    return inner
​
@outer  # 在目标函数上使用 @outer
def sleep():
    import random
    import time
    print("睡眠中......")
    time.sleep(random.randint(1, 5))
​
sleep()

3. 设计模式

① 单例模式

Ⅰ. 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

  • 定义:保证一个类只有一个实例,并提供一个访问它的全局访问点。

  • 适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。

Ⅱ. 实现方式

  • 在一个文件中定义如下代码

class StrTools:
    pass
​
str_tool = StrTools()
  • 在另一个文件中导入对象

from test import str_tool
​
s1 = str_tool
s2 = str_tool
​
print(s1)       # <test.StrTools object at 0x0000013D001DB910>
print(s2)       # <test.StrTools object at 0x0000013D001DB910>

Ⅲ. 单例模式就是对一个类,只获取其唯一的类实例对象,持续复用它:

  • 节省内存

  • 节省创建对象的开销

② 工厂模式

Ⅰ. 当需要大量创建一个类的实例的时候,可以使用工厂模式,即从原生的使用类的构造去创建对象的形式,迁移到基于工厂提供的方法去创建对象的形式。

  • 大批量创建对象的时候有统一的入口,易于代码维护

  • 当发生修改,仅修改工厂类的创建方法即可

  • 符合现实世界的模式,即由工厂来制作产品(对象)

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()
​
factory = Factory()
worker = factory.get_person('w')
stu = factory.get_person('s')
teacher = factory.get_person('t')

4. 多线程

① 基本概念:

  • 进程:就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程 ID 方便系统管理。

  • 线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位。

② 操作系统中可以运行多个进程,即多任务运行;一个进程内可以运行多个线程,即多线程运行。

③ 注意:

  • 进程之间是内存隔离的,即不同的进程拥有各自的内存空间。这就类似于不同的公司拥有不同的办公场所。

  • 线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。

④ 多个进程同时在运行,即不同的程序同时运行,称之为:多任务并行执行。一个进程内的多个线程同时在运行,称之为:多线程并行执行。

⑤ 语法

import threading
​
# 创建线程
thread_obj = threading.Thread([group [, target [, name [, args [, kwargs]]]]])
- group: 暂时无用,未来功能的预留参数
- target: 执行的目标任务名
- args: 以元组的方式给执行任务传参
- kwargs: 以字典方式给执行任务传参
-name: 线程名,一般不用设置
​
# 启动线程
thread_obj.start()
import time
import threading
​
def sing(msg):
    while True:
        print(msg)
        time.sleep(1)
​
def dance(msg):
    while True:
        print(msg)
        time.sleep(1)
​
# 创建一个唱歌的线程,通过元组的方式传参,如果元组只有一个元素,要加上一个 "," 号
sing_thread = threading.Thread(target=sing, args=("我要唱歌哈哈哈", ))
# 创建一个跳舞的线程,通过字典的方式传参
dance_thread = threading.Thread(target=dance, kwargs={"msg": "我在跳舞哦啦啦啦"})
​
# 启动线程
sing_thread.start()
dance_thread.start()

5. Socket

① socket(简称套接字)是进程之间通信一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,进程之间想要进行网络通信需要 socket。

② 客户端和服务端

  • Socket 服务端:等待其它进程的连接、可接受发来的消息、可以回复消息

  • Socket客户端:主动连接服务端、可以发送消息、可以接收回复

③ 实现

Ⅰ. 服务端

1. 创建 socket 对象

import socket
socket_server = socket.socket()

2. 绑定 socket_server 到指定 IP 和地址(只有服务端会有 2、3 步

socket_server.bind(host,port)

3. 服务端开始监听端口

# backlog 为 int 整数,表示允许的连接数量,超出的会等待,可以不填,不填会自动设置一个合理值
socker_server.listen(backlog)

4. 接收客户端连接,获得连接对象(服务端后续的接收和回复消息都是使用 conn 进行的,因为服务端可以连接多个客户端,而 conn 指定了某个客户端的连接;客户端后续都是使用 socket_client

# accept 方法是阻塞方法,如果没有连接,会卡在当前这一行不向下执行代码
# accept 返回的是一个二元元组,可以使用上述形式,用两个变量接收二元元组的2个元素
conn, address = socket_server.accept()
print(f"接收到客户端连接,连接来自:{address}")

5. 客户端连接后,通过 recv 方法,接收客户端发送的消息

# 可以通过 while True 无限循环来持续和客户端进行数据交互
while True:
    # recv 方法的返回值是字节数组(Bytes),可以通过 decode 使用 UTF-8 解码为字符串
    # recv 方法的传参是 buffsize,缓冲区大小,一般设置为 1024 即可
    data = conn.recv(1024).decode("UTF-8")
    # 可以通过判定客户端发来的特殊标记,如 exit,来退出无限循环
    if data == 'exit':
        break
    print("接收到发送来的数据:",data)

6. 通过 conn(客户端当次连接对象),调用 send 方法可以回复消息

while True:
    data = conn.recv(1024).decode("UTF-8")
    if data == 'exit':
        break
    print("接收到发送来的数据: ",data)
    msg = input("请输入要发送给客户端的信息:")
    # 消息需要编码为字节数组(UTF-8编码)
    conn.send(msg.encode("UTF-8"))

7. conn(客户端当次连接对象)和 socket_server 对象调用 close 方法,关闭连接

conn.close()
socket_server.close()

Ⅱ. 客户端

1. 创建 socket 对象

import socket
socket_client = socket.socket()

2. 连接到服务端

socket_client.connect(("localhost", 8888))

3. 发送消息

# 可以通过无限循环来确保持续的发送消息给服务端
while True:
    send_msg = input("请输入要发送的消息")
    # 通过特殊标记来确保可以退出无限循环
    if send_msg == 'exit':
        break
    socket_client.send(send_msg.encode("UTF-8"))

4. 接收返回消息

while True:
    send_msg = input("请输入要发送的消息").encode("UTF-8")
    socket_client.send(send_msg)
    recv_data = socket_client.recv(1024)
    print("服务端回复消息为: ", recv_data.decode( "UTF-8"))

5. 关闭链接

# 最后通过 close 关闭连接,但服务端不会断开连接
socket_client.close()

6. 正则表达式

① 基础方法

Ⅰ. re.match(匹配规则, 被匹配字符串),从被匹配字符串开头进行匹配,匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空。

s = 'python itheima python itheima python itheima'
​
result = re.match('python', s)
print(result)           # <re.Match object; span=(0, 6), match='python'>
print(result.span())    # (0, 6)
print(result.group())   # python
​
s = '1python itheima python itheima python itheima'
​
result = re.match('python', s)
print(result) # None

注意:re.match() 会从头部开始匹配,如果头部都不匹配,后面就不会理会。

Ⅱ. re.search(匹配规则, 被匹配字符串),搜索整个字符串,找出匹配的。从前向后,找到第一个后,就停止,不会继续向后;整个字符串都找不到,返回 None。

s = '1python666itheima666python666'
​
result = re.search('python', s)
print(result)           # <re.Match object; span=(1, 7), match='python'>
print(result.span())    # (1, 7)
print(result.group())   # python
​
s = 'itheima666'
result = re.search('python', s)
print(result)           # None

Ⅲ. re.findall(匹配规则, 被匹配字符串),匹配整个字符串,找出全部匹配项;找不到返回空 list: []。

s = '1python666itheima666python666'
​
result = re.findall('python', s)
print(result)       #['python', 'python']
​
s = '1python666itheima666python666'
​
result = re.findall('itcast', s)
print(result)       # []

② 元字符匹配

Ⅰ. 单字符匹配

字符功能
.匹配任意 1 个字符(除了\n), \. 匹配点本身
[]匹配 [] 中列举的字符
\d匹配数字,即 0-9
\D匹配非数字
\s匹配空白,即空格、tab 键
\S匹配非空白
\w匹配单词字符,即a-z、A-Z、0-9、_
\W匹配非单词字符

Ⅱ. 数量匹配

字符功能
*匹配前一个规则的字符出现 0 至无数次
+匹配前一个规则的字符出现 1 至无数次
?匹配前一个规则的字符出现 0 次或 1 次
{m}匹配前一个规则的字符出现 m 次
{m, }匹配前一个规则的字符出现最少 m 次
{m,n}匹配前一个规则的字符出现 m 到 n 次(m, 和 n 之间不能写空格

Ⅲ. 边界匹配

字符功能
^匹配字符串开头
$匹配字符串结尾
\b匹配一个单词的边界
\B匹配非单词边界

Ⅳ. 分组匹配

字符功能
|匹配左右任意一个表达式
()将括号中字符作为一个分组
# 匹配账号,只能由字母和数字组成,长度限制 6 到 10位
r = '^[0-9a-zA-Z]{6,10}$'
s = '123456_'
print(re.findall(r, s))     # None
​
# 匹配 QQ 号,要求纯数字,长度 5-11,第一位不为0
r = '^[1-9][0-9]{4,10}$'
s = '123453678'
print(re.findall(r, s))     # ['123453678']
​
# 匹配邮箱地址,只允许 qq、163. gmail 这三种邮箱地址
# abc.efg.daw@qq.com.cn.eu.qq.aa.cc
# abc@qq.com
# {内容}.{内容}.{内容}.{内容}.{内容}@{内容}.{内容}.{内容}
# 字符串的 r 标记,表示当前字符串是原始字符串,即内部的转义字符无效而是普通字符
r = r'(^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$)'
s = 'a.b.c.d@qq.com.a.z.c'
print(re.match(r, s))       # <re.Match object; span=(0, 30), match='a.b.c.d@qq.com.a.z.c'>

7. 递归

① 递归:即方法(函数)自己调用自己的一种特殊编程,写法如:

def func():
    if ...:
        func()
​
    return ...

函数调用自己,即称之为递归调用。

② 演示 os 模块的 3 个基础方法:

path = xxx                      # 路径
​
print(os.listdir(path))         # 列出路径下的内容
print(os.path.isdir(path))      # 判断指定路径是不是文件夹
print(os.path.exists(path))     # 判断指定路径是否存在

③ 递归案例——获取文件列表

def get_files_recursion_from_dir(path):
    """
    从指定的文件夹中使用递归的方式,获取全部的文件列表
    :param path: 被判断的文件夹
    :return: list,包含全部的文件,如果目录不存在或者无文件就返回一个空 list
    """
    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__':
    print(get_files_recursion_from_dir("D:/test"))

更多学习内容持续更新中……

  • 36
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值