1. 闭包
定义双层嵌套函数,内层函数可以访问外层函数的变量,将内存函数作为外层函数的返回,此时内层函数就是闭包函数
def account_create(initial_amount = 0):
def atm(num, flag = True):
nonlocal initial_amount
if flag:
initial_amount += num
print(f"存款:+{num},账户余额:{initial_amount}")
else:
initial_amount -= num
print(f"取款:-{num},账户余额:{initial_amount}")
return atm
fn = account_create()
fn(100) # 存款:+100,账户余额:100
fn(200) # 存款:+200,账户余额:300
fn(100,False) # 取款:-100,账户余额:200
当内部函数需要修改外部函数变量时,可以使用
nonlocal关键字注意:在外部函数内返回内部函数时,只需要写内部函数的名称,不需要带括号
()
闭包优点:
- 无需定义全局变量,即可实现通过函数,持续访问、修改某个值
- 闭包使用的变量的所用于函数内,难以被修改
闭包缺点:
- 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
2. 装饰器
装饰器其实也是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能
1. 装饰器一般写法,闭包写法
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()
注意:在调用外部函数时,只需要传入目标函数的名称,不需要带括号
(),这种写法目标函数和外部函数的顺序可以不定
2. 装饰器快捷写法
在目标函数上使用@outer关键字
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()
注意:这种写法,外部函数必须写在目标函数之前
3. 设计模式
面向对象、单例模式、工厂模式等等统称为设计模式
1. 单例模式
一般创建类的实例后,就可以得到一个完整的、独立的类对象。在创建类对象时,一般不同的对象的内存地址不同
单例模式所要实现的效果:一个类无论获取多少次类对象,都仅仅提供一个具体的实例用于节省创建类对象的开销和内存开销
单例模式的主要目的:确保某一个类只有一个实例存在,并提供一个访问它的全局访问点
单例模式的使用场景:当一个类只能有一个实例,而客户只能从一个访问点访问他
# TODO 文件名为str_tool_py
"""
在一个独立的文件中定义类,并创建一个类对象
"""
class StrTools:
pass
str_tool = StrTools()
from str_tool_py import str_tool
"""
在实际中,只需要导入单例模式中创建的类对象,利用这个类对象作为访问点,创建不同的对象,此时所有的对象地址都相同,从而 实现了创建某一类的不同对象,节省了创建对象的开销和内存开销
"""
s1 = str_tool
s2 = str_tool
print(s1)
print(s2)
"""
<python_study.设计模式.单例模式.单例模式代码定义.StrTools object at 0x000001F59E825408>
<python_study.设计模式.单例模式.单例模式代码定义.StrTools object at 0x000001F59E825408>
"""
优点:节省内存,节省创建对象的开销
2. 工厂模式
使对象的创建由使用原生类本身创建转换到由特定的工厂方法来创建
方法:单独创建一个工厂类,在工厂类中实现各种类的实例创建与返回,然后创建工厂类对象,调用响应的工厂类中创建各种类对象的方法,从而实现了大量创建各种类对象的方法
# TODO 非工厂模式
class Person:
pass
class Worker(Person):
pass
class Teacher(Person):
pass
class Student(Person):
pass
worker = Worker()
teacher = Teacher()
student = Student()
# TODO 工厂模式
class Person:
pass
class Worker(Person):
pass
class Teacher(Person):
pass
class Student(Person):
pass
class Factory:
def get_person(self,p_type):
if p_type == "Worker":
return Worker()
elif p_type == "Teacher":
return Teacher()
elif p_type == "Student":
return Student()
factory = Factory()
worker = factory.get_person("Worker")
teacher = factory.get_person("Teacher")
student = factory.get_person("Student")
优点:
- 大批量创建对象的时候有统一的入口,易于代码维护
- 当需要修改时,仅修改工厂类的创建方法即可
- 符合现实世界的模式,即由工厂来制作产品(对象)
4. 多线程
- 进程:就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程ID方便系统管理
- 线程:归属于进程的,,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位
OS中可以运行多个进程,即多任务运行,一个进程中可以运行多个线程,即多线程运行
- 进程之间是内存隔离的,即不同的进程拥有各自的内存空间
- 线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间
1. 并行执行
指同一时间做不同的工作
进程的并行执行:操作系统可以同时运行好多程序,这些程序称为并行执行
线程的并行执行:一个程序在同一时间做两件乃至多件不同的事情,这些线程称为多线程并行执行
2. 多线程编程
import threading
import time
"""
thread_obj = threading.Thread([group [, target [, name[, args[, kwargs]]]]])
group:暂时无用,未来功能的预留参数
target:执行的目标任务名,即需要工作的函数名
args:以元组的方式给执行任务传参
kwargs:以字典的方式给执行任务传参
name:线程名,一般不设置
# 启动线程,让线程开始工作
thread_obj.start()
"""
# while True的意义是为了让每一个线程持续不断的执行,如果只需要执行一次,则不需要写
def sing(msg):
while True:
print(msg)
time.sleep(1)
def dance(msg):
while True:
print(msg)
time.sleep(1)
if __name__ == '__main__':
# 注意args传入的参数形式为元组,则如果是传入一个参数,必须在参数后加逗号,以表示传入的参数形式为元组
sing_thread = threading.Thread(target=sing, args=("sing", ))
# 注意kwargs传入的参数形式为字典,字典中的Key为传入的形参名,value为实际使用的实参
dance_thread = threading.Thread(target=dance, kwargs={"msg": "dance"})
sing_thread.start()
dance_thread.start()
5. 网络编程
socket
socket(简称 套接字)是进程之间通信一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,进程之间想要进行网络通信需要socket
进程之间是互不干扰的,如果进程之间需要进行数据传输,则需要用到socket
两个进程之间通过socket进行相互通讯,必须有服务端和客户端
socket服务端:等待其他进程的连接、可接受发来的消息、可以回复消息
socket客户端:主动连接服务端、可以发送消息、可以接收消息
1. 服务器端开发
"""
步骤:
1. 创建socket对象
import socket
socket_server = socket.socket()
2. 绑定socket_server到指定ip地址
socket_server.bind(host, port)
# bind中传入的是一个元组,一般为(ip地址, 端口号)
3. 服务端开始监听端口
socket_server.listen(backlog)
# backlog为int整数,表示允许的连接数量,超出的会等待,可以不填,不填会自动设置一个合理值
4. 接收客户端连接,获取连接对象
conn, address = socket_server.accept()
# conn表示客户端和服务端的连接对象
# address表示客户端的地址信息
print(f"接收到客户端连接,连接来自:{address}")
# accept方法是阻塞方法,如果没有链接,会卡在当前这一行不向下执行代码
# accept返回的是一个二元元祖,可以使用上述形式,用两个变量接收二元元祖的两个元素
5. 客户端连接后,通过recv方法,接收客户端发送的消息
while True:
data = conn.recv(1024).decode("UTF-8")
# recv方法的返回值是一个字节数组(Bytes),可以通过decode使用UTF-8解码为字符串
# recv方法的传参是buffsize,缓冲区大小一般设置为1024即可
if data == "exit":
break
print("接收到发送来的数据:", data)
# 可以通过while True无限循环来持续和客户端进行数据交互
# 可以通过判定客户端发来的特殊标记,如exit来推出无限循环
6. 通过conn(客户端当前连接对象),调用send方法回复客户端消息
while True:
data = conn.recv(1024).decode("UTF-8")
if data == "exit":
break
print("接收到发送来的数据:", data)
conn.send("发送给客户端的消息".encode("UTF-8"))
7. conn(客户端当前连接对象)和socket_server对象调用close方法,关闭连接
"""
import socket
if __name__ == '__main__':
socket_server = socket.socket()
socket_server.bind(("localhost", 8888)) # localhost即为本地服务器地址,一般为127.0.0.1
socket_server.listen(1)
conn, address = socket_server.accept()
print(f"接收到客户端连接,连接来自:{address}")
while True:
data_client = conn.recv(1024).decode("UTF-8") # 接收的消息需要decode解码
if data_client == 'exit':
break
print(f"服务端接收到客户端发送来的数据:{data_client}")
msg = input("请输入你要和客户端回复的消息:")
conn.send(msg.encode("UTF-8")) # 发送的消息需要encode编码
if msg == 'exit':
break
conn.close()
socket_server.close()
2. 客户端开发
"""
步骤:
1. 创建socket对象
import socket
socket_client = socket.socket()
2. 连接到服务器
socket_client.connect((服务器端ip地址, 端口号))
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("请输入要发送的消息:")
if send_msg == "exit":
break
socket_client.send(send_msg.encode("UTF-8"))
recv_data = socket_client.recv(1024) # 1024是缓冲区大小,一般设置1024
print("服务端返回的消息是:", recv_data.decode("UTF-8"))
5. 关闭连接
socket_client.close()
"""
import socket
if __name__ == '__main__':
socket_client = socket.socket()
socket_client.connect(("localhost", 8888))
while True:
send_msg = input("请输入要发送的消息:")
socket_client.send(send_msg.encode("UTF-8")) # 发送的消息需要encode编码
if send_msg == "exit":
break
recv_data = socket_client.recv(1024)
print("服务端返回的消息是:", recv_data.decode("UTF-8")) # 接收的消息需要decode解码
socket_client.close()
注意
- 要对需要发送的消息进行
encode编码 - 要对需要接收的消息进行
decode解码
6. 正则表达式
正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本
正则表达式就是使用字符串定义规则,并通过规则去验证字符串是否匹配
import re
s = 'python java c python'
# TODO match 从头匹配
"""
re.match("xxx", s) 在s字符串中从头开始匹配,打印第一个匹配到的下标,如果从第一个字母就开始不匹配,则返回None
"""
result1_match = re.match("python", s)
result2_match = re.match("java", s)
result3_match = re.match("Go", s)
print(result1_match) # <re.Match object; span=(0, 6), match='python'>,span表示匹配的下标,左包含,右不包含
print(result2_match) # None
print(result3_match) # None
# TODO search 搜索匹配
"""
re.search("xxx", s) 在s字符串中搜索匹配,打印第一个完全匹配的下标,如果全没匹配到,则返回None
"""
result1_search = re.search("python", s)
result2_search = re.search("java", s)
result3_search = re.search("Go", s)
print(result1_search) # <re.Match object; span=(0, 6), match='python'>
print(result2_search) # <re.Match object; span=(7, 11), match='java'>
print(result3_search) # None
# TODO findall 搜索全部匹配
"""
re.findall("xxx", s) 在s字符串中搜索全部匹配,打印所有的完全匹配的字符串,注意不是返回下标,如果没有完全匹配的字符串,则返回空列表
"""
result1_findall = re.findall("python", s)
result2_findall = re.findall("java", s)
result3_findall = re.findall("Go", s)
print(result1_findall) # ['python', 'python']
print(result2_findall) # ['java']
print(result3_findall) # []
元字符匹配
单字符匹配
| 字符 | 功能 |
|---|---|
. | 匹配任意1个字符(除了\n),\.表示匹配点本身 |
[] | 匹配[]中列举的字符,可以对a-z、A-Z、0-9三个任意匹配搜索,也可以搜索匹配自定义的字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字 |
\s | 匹配空白,即空格、tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_ |
\W | 匹配非单词字符 |
比如,验证一个字符串是否是符合条件的电子邮箱地址,只需要配置好正则规则,即可匹配任意邮箱。比如通过正则规则: (^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$,即可匹配一个字符串是否是标准邮箱格式
import re
s = "python 666 java 888 c 111 !!!..."
result_One = re.findall(r'.', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_One) # ['p', 'y', 't', 'h', 'o', 'n', ' ', '6', '6', '6', ' ', 'j', 'a', 'v', 'a', ' ', '8', '8', '8', ' ', 'c', ' ', '1', '1', '1', ' ', '!', '!', '!', '.', '.', '.']
result_NoOne = re.findall(r'\.', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_NoOne) # ['.', '.', '.']
result_SomeWord = re.findall(r'[a-zA-Z0-9]', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_SomeWord) # ['p', 'y', 't', 'h', 'o', 'n', '6', '6', '6', 'j', 'a', 'v', 'a', '8', '8', '8', 'c', '1', '1', '1']
result_must_python = re.findall(r'[python]', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_must_python) # ['p', 'y', 't', 'h', 'o', 'n']
result_Num = re.findall(r'\d', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_Num) # ['6', '6', '6', '8', '8', '8', '1', '1', '1']
result_NoNum = re.findall(r'\D', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_NoNum) # ['p', 'y', 't', 'h', 'o', 'n', ' ', ' ', 'j', 'a', 'v', 'a', ' ', ' ', 'c', ' ', ' ', '!', '!', '!']
result_None = re.findall(r'\s', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_None) # [' ', ' ', ' ', ' ', ' ', ' ']
result_NoNone = re.findall(r'\S', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_NoNone) # ['p', 'y', 't', 'h', 'o', 'n', '6', '6', '6', 'j', 'a', 'v', 'a', '8', '8', '8', 'c', '1', '1', '1', '!', '!', '!']
result_word = re.findall(r'\w', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_word) # ['p', 'y', 't', 'h', 'o', 'n', '6', '6', '6', 'j', 'a', 'v', 'a', '8', '8', '8', 'c', '1', '1', '1']
result_NoWord = re.findall(r'\W', s) # 注意字符串前加上r的标志,表示字符串中转义字符\无效,仅表示普通的\
print(result_NoWord) # [' ', ' ', ' ', ' ', ' ', ' ', '!', '!', '!']
数量匹配
| 字符 | 功能 |
|---|---|
* | 匹配前一个规则的字符出现0至无数次 |
+ | 匹配前一个规则的字符出现1至无数次 |
? | 匹配前一个规则的字符串0次或1次 |
{m} | 匹配前一个规则的字符出现m次 |
{m,} | 匹配前一个规则的字符出现最少m次 |
{m,n} | 匹配前一个规则的字符出现m到n次 |
边界匹配
| 字符 | 功能 |
|---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词边界 |
\B | 欧匹配非单词边界 |
在匹配规则出最前端和最后端出现
^和$,表示验证全部字符串
分组匹配
| 字符 | 功能 |
|---|---|
| ` | ` |
() | 将括号中字符作为一个分组 |
综合应用
"""
需求1:
匹配账号,只能由字母和数字组成,长度限制6到10位
规则:^[0-9a-zA-Z]{6,10}$
需求2:
匹配qq号,要求纯数字,长度5-11,第一位不为0
规则:^[1-9][0-9]{4-10}&
需求3:
匹配邮箱地址,只允许qq、163、gmail这三种邮箱地址
规则:^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+&
"""
# TODO 需求1
def word_num():
str = input("请输入账号(只能由字母和数字组成,长度限制6到10位):")
if re.findall(r'^[0-9a-zA-Z]{6,10}$', str):
print("输入成功")
else:
print("输入失败")
# TODO 需求2
def QQ_account():
str = input("请输入QQ账号(匹配qq号,要求纯数字,长度5-11,第一位不为0):")
if re.findall(r'^[1-9][0-9]{4,10}$', str):
print("输入成功")
else:
print("输入失败")
# TODO 需求3
def mail_account():
str = input("匹配邮箱地址,只允许qq、163、gmail这三种邮箱地址")
# [\w-]+表示 必须出现\w和- 其中\w表示0-9、a-z、A-Z、_ 并且[]中的内容可以出现1至无数次
# (\.[\w-]+)*表示 必须出现\.和[\w-]+为一个分组 并且()中的内容可以出现0至无数次
# @表示 必须出现@
# (qq|163|gmail)表示 必须出现qq或163或gmail
# (\.[\w-]+)+表示 必须出现\.和[\w-]+为一个分组 并且()中的内容可以出现1至无数次
if re.findall(r'^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$', str):
print("输入成功")
else:
print("输入失败")
7. 递归
即方法(函数)自己调用自己的一种特殊编写手法,函数自己调用自己成为递归调用
- 经典递归场景
- 递归找文件
2380

被折叠的 条评论
为什么被折叠?



