Python – 高阶使用基础
闭包
基础概念
在Python中,闭包就是定义一个双层嵌套函数,内层函数可以访问外层函数的变量,将内层函数作为处层函数的返回,此内层函数就是闭包函数
问题案例:把一个ATM取款作为一个模块,提供一个存取款的方法:
# 闭包
account_amount = 0
# 定义一个方法,它用于存取款
# is_saveMoney 是否存款
def atm(num, is_saveMoney=True):
global account_amount
if is_saveMoney:
account_amount += num
print(f"当前存入{num}元,账号总计为{account_amount}元")
else:
account_amount -= num
print(f"当前取款{num}元,账号总计为{account_amount}元")
atm(100) # 当前存入100元,账号总计为100元
atm(100) # 当前存入100元,账号总计为200元
atm(200) # 当前存入200元,账号总计为400元
atm(100, is_saveMoney=False) # 当前取款100元,账号总计为300元
从上面的代码我们看出了问题,当这作为一个模块时,account_amount 是可以被外部代码所引入并进行修改的,对于业务逻辑来说是不合理的,因为账户金额不应该被允许引入和修改。这是因为,account_amount在这里已经成为了一个全局变量,若我们有两个人以上对atm操作的话,必然会使account_amount的值发生混乱。
通过定义闭包,把临时变量作为方法中的一员,每一位调用该方法的人,都应该创建一个只属于他自己使用的临时变量,每个人只能使用属于他自己的变量,这时我们可以使用闭包,通过嵌套方法来实现变量的私有化
def create_account(account_amount=0):
def atm(num, is_save_money=True):
nonlocal account_amount
if is_save_money:
account_amount += num
print(f"当前存入{num}元,账号总计为{account_amount}元")
else:
account_amount -= num
print(f"当前取出{num}元,账号总计为{account_amount}元")
return atm
atm = create_account() # 调用外层方法返回内层atm方法,同时定义了临时变量account_amount
atm(100) # 当前存入100元,账号总计为100元
atm(100) # 当前存入100元,账号总计为200元
atm(200) # 当前存入200元,账号总计为400元
atm(100, is_save_money=False) # 当前取出100元,账号总计为300元
nonlocal 关键字的作用
在闭包的内部函数中,若想有能力修改外部方法函数的变量值,就需要在使用该外部变量值的内部方法中使用nonlocal声明这个变量,那么内部方法就可以使用外部方法的变量了,如果不声明,则只能使用,不能修改
装饰器
装饰器其实也是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能,有点类似Java的切面编程
针对闭包的与python的功能,我们可以把传入的临时变量可以是一个函数,通过传入一个函数,并在函数的调用前后进行功能增加
示例:
def outer(func):
def inner():
print("我要开始睡觉了")
func() # 调用接收回来的方法,在方法调用前后增加功能
print("我要起床了")
return inner
# 定义一个睡觉的方法
def sleep():
import time
import random
print("我要睡觉了")
time.sleep(random.randint(1, 5))
fn = outer(sleep)
fn()
针对这类对函数功能的扩展的闭包,Python提供了一种语法糖,可以直接使用【@外部闭包名】于需要增强功能的函数上,就可以实现函数功能的增强,无需传入参数等
def outer(func):
def inner():
print("我要开始睡觉了")
func() # 调用接收回来的方法,在方法调用前后增加功能
print("我要起床了")
return inner
# 在需要增强的方法上,加上 @闭包 名称,就可以被增强方法
@outer
def sleep():
import time
import random
print("我要睡觉了")
time.sleep(random.randint(1, 5))
# 在调用时只需直接调用原方法即可,也不需要把方法传入闭包
sleep()
设计模式(基础)
单例模式
在某些使用场境下,我们只希望在整个软件生命周期内,只需要出现某个类的一个实例,可以定义一个单例模式
# 单例模式
class StrTools:
pass
strtools = StrTools()
工厂模式
工厂模式,在Python 中是指可以定义一个方法或类,去统一化创建多个不同的对象,这样可以有效的解耦,当业务代码中出现需要修改时,我们可以直接在工厂类中修改,而免除在业务代码中大量修改的情况:
# 工厂模式
class Person:
pass
class Student(Person):
pass
class Teacher(Person):
pass
class Manager(Person):
pass
class Factory:
def create_person(self, type):
if type == "Student":
return Student()
elif type == "Teacher":
return Teacher()
else:
return Manager()
# 使用工厂类调用工厂方法实现使用一个方法创建多个类对象
factory = Factory()
stu = factory.create_person("Student")
tea = factory.create_person("Teacher")
man = factory.create_person("Manager")
多线程(基础)
引入包
Python 中提供了一种用于多线程执行的包,Threading 模块
语法:
import threading
thread_obj = threading.Thread([group [, target [, name [, args [, kwargs]]]]])
- group: 暂时无用,未来功能的预留参数
- target: 执行的目标任务名
- args: 以元组的方式给执行任务传参
- kwargs: 以字典方式给执行任务传参
- name: 线程名,一般不用设置
启动线程
thread_obj.start()
示例:
import threading
import time
def sing(msg):
while True:
print(msg)
time.sleep(1)
def dance(msg):
while True:
print(msg)
time.sleep(0.5)
if __name__ == '__main__':
t1 = threading.Thread(target=sing, kwargs={"msg":"我在唱歌"})
t2 = threading.Thread(target=dance, kwargs={"msg":"我在跳舞"})
t1.start()
t2.start()
网络编程(基础)
Socket
socket 是进程之间通信的工具,进程之间想要进行网络通讯需要socket
服务端
要在Py中创建一个服务端,只需要几个步骤就可以
- 1.创建socket对象(未区分服务端还是客户端)
-
import socket socket_server = socket.socket()
- 2.绑定socket_server到指定ip地址端口
-
# 定义服务端ip与端口 socket_server.bind(('localhost', 1234))
- 3.服务端开始监听端口
-
# 监听服务端,设置监听数为1 socket_server.listen(1)
- 4.接收客户端连接,获得连接对象
-
# 等待接收客户端的数据过来,会接收一个元组数据 # socket_client 为当前客户端连接对象,通过对象进行回复操作 # address 为当前客户端的连接信息 socket_client, address = socket_server.accept() print(f"客户端对象:{socket_client},连接信息为:{address}")
- 5.客户端连接后,通过recv方法,接收客户端发送的消息
-
# 接收客户端的数据,定义缓冲区大小为1024 msg = socket_client.recv(1024).decode() print(f"发来消息:{msg}")
- 6.通过socket_client(客户端当次连接对象),调用send方法可以回复消息
-
# 输入消息,转为bytes bytes_data = input("输入要回传的消息").encode("utf-8") # 发送消息 socket_client.send(bytes_data)
- 7.socket_client(客户端当次连接对象)和socket_server对象调用close方法,关闭连接
-
# 关闭服务 socket_client.close() socket_server.close()
客户端
客户端的创建分别如下几步:
- 1.创建socket对象
-
import socket client_socket = socket.socket()
- 2.连接到服务端
-
client_socket.connect(("localhost", 1234))
- 3.发送消息
-
msgBytes = input("请输入信息").encode("utf-8") client_socket.send(msgBytes)
- 4.接收返回消息
-
msg = client_socket.recv(1024).decode("utf-8") print(f"收到信息:{msg}")
- 5.关闭连接
-
client_socket.close()
正则表达式
使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式的文本。
元字符匹配表
- . -> 匹配任意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次
- 关于边界匹配
- ^ -> 匹配字符串开头
- $ -> 匹配字符串结尾
- \b -> 匹配一个单词的边界
- \B -> 匹配非单词边界
- 分组匹配
- | -> 匹配左右任意一个表达式
- () -> 将括号中字符作为一个分组
注意:在Python中,使用 \ 符号有可能会引起转义,在我们写正则时,我们应该让正则的字符以普通字符显示而非转义功能性字符
# 在字符串前加上 r 则表示该字符串中不进行转义
str = r"[0-9a-zA-Z]"
match 从头匹配
使用match 会将文本从头部开始匹配,如果头部匹配不成功,则后面的不再考虑,返回None,如果匹配成功,则返回Result,可以通过调用span方法返回该匹配的范围
import re
str = "itheima1 @@python2 !!666 ##itcacast3"
# 从头开始匹配,匹配成功返回结果
print(re.match("[a-zA-Z]{1,4}", str)) # <re.Match object; span=(0, 4), match='ithe'>
search 搜索一个
search 可以搜索整个字符串,找出匹配的。但它只找一个,当在文本中找到后,就不再寻找了。
import re
str = "itheima1 @@python2 !!666 ##itcacast3"
# 全局匹配,匹配一个后就停止
print(re.search("[a-zA-Z]{1,4}", str)) # <re.Match object; span=(0, 4), match='ithe'>
findall 找到所有
findall 可以搜索整个字符串,并找出所有匹配的,并以列表方式返回
import re
str = "itheima1 @@python2 !!666 ##itcacast3"
# 全局匹配,匹配出所有
print(re.findall("[a-zA-Z]{1,4}", str)) # ['ithe', 'ima', 'pyth', 'on', 'itca', 'cast']
共有 0 条评论