目录
一、类、对象和成员方法
二、构造方法
三、面向对象
(1)封装
(2)继承
单继承
多继承
复写
super:调用父类同名成员
(3)多态
(4)抽象类
五、Python操作MySQL数据库
六、闭包和装饰器
闭包概念
闭包作用
装饰器
装饰器一般写法
装饰器语法糖写法
七、设计模式:单例模式和工厂模式
单例模式
工厂模式
八、多线程编程
进程与线程
并发与并行
多线程编程:threading模块
传参方式 args\kwargs
九、网络编程
socket概念
socket服务端编程
socket客户端编程
十一、正则表达式
正则表达式
正则基础方法
正则元字符匹配
一、类、对象和成员方法
类是一组相关属性和行为的集合。
对象是类的实例,由于对象是根据类创建出来的,所以对象具备类中的属性和行为。
创建类语法:
class 类名称: # class 是关键字,表示要开始定义类了
类的属性 # 类的属性(定义在类中的变量:成员变量)
类的行为 # 类的行为(定义在类中的函数:成员方法)
创建类对象语法: (Java中创建对象语法是 类名称 对象名 = new 类名称() ,Python与Java不同,牢记 )
对象名 = 类名称()
创建类成员方法语法:
def 方法名(self,形参1,,,形参n):
方法体
注意点:1、写在类外面的函数叫做函数,而写在类里面的函数叫做方法
2、在方法定义的参数列表中,self关键字是必须填写的
3、self 用来表示类对象本身的意思
4、当我们使用类对象调用方法的时候,self会自动被Python传入
5、在方法内部,想要访问类的成员变量,必须使用self
6、self 虽然出现在形参列表中,但在调用方法时无需理会,不需要传入该self参数
示例:创建一个Student类,属性有学生姓名和年龄,创建方法
二、构造方法
构造方法通常可以用来初始化对象,Java和Python 都使用构造方法来初始化对象的成员变量。
Python中类通过使用__init__()方法,称为构造方法。构造方法是一种特殊方法(init),以两个下划线开头,两个下划线结尾
构造方法可以实现:
- 在创建对象的时候,构造方法会自动执行
- 在创建对象的时候,构造方法会将传入参数自动传递给__init__()方法使用。(两个下划线开头,两个下划线结尾)
三、面向对象
Python面向对象的特征包括封装、继承和多态。(Java语言也是面向对象,可参考Java)
1.封装:根据职责将属性和方法封装到一个抽象的类中
2.继承:实现代码的重用,相同的代码不需要重复的写
3.多态:以封装和继承为前提,不同的子类对象调用相同的方法,产生不同的执行结果
(1)封装
- 封装就是使用特殊的语法,对成员属性和成员方法进行包装,达到保护和隐藏的目的。
- 被封装的成员(又叫做私有成员)主要是供类的内部使用,类对象无法使用。
私有成员:现实事物有部分属性和行为是不公开对使用者开放的。同样在类中描述属性和方法的时候也需要达到这个要求,就需要定义私有成员了。
私有成员包括私有成员变量和私有成员方法。其中类对象无法访问私有成员。
定义私有成员方式:
- 私有成员变量:变量名以__开头(2个下划线)
- 私有成员方法:方法名以__开头(2个下划线)
封装案例分析:
(2)继承
Python 语言中一个类可以通过继承来使用另外一个类的方法和属性。这样做的好处是继承通用的方法和属性,使代码更加的简洁、明了。
在Python中继承可以分为单继承和多继承。
注意点:Java中的继承只有单继承,没有多继承。Java不允许一个子类同时继承多个父类
单继承
一个类只能维承一个父类的方式。
语法格式:
class father(): # 父类
pass
class sun(father): # 子类 继承父类
pass
多继承
一个类去继承多个父类的方式 。
语法格式:
class father(): # 定义第一个父类
pass
class mother(): # 定义第二个父类
pass
class sun(father,mother): # 子类 继承多个父类
pass
多继承注意事项:
一个子类继承多个父类,当子类调用成员方法或者成员属性时。此时若多个父类都具有共同的成员方法或者成员属性,那么输出内容以继承顺序(从左到右)为优先级。
复写
子类继承父类的成员方法、成员属性之后,如果需要对继承的父类方法、属性进行修改的话,就可以直接进行重新修改。
super:调用父类同名成员
当子类对父类的方法和属性进行复写重新修改后,此时当子类对象调用成员的时候,就会调用复写后的新成员。
如果我们需要调用父类复写前的成员时,此时就需要使用super关键字来指示调用父类的成员。
注意点:方式一、方式二只可以在子类内部方法里面调用父类的同名成员,而且子类的对象调用默认是调用子类复写的。
(3)多态
多态指的是:同一个行为,使用不同的对象获得不同的状态。
如定义函数(方法),通过类型注解声明需要父类对象,实际传入子类对象进行工作,从而获得不同的工作状态。
多态 一般 是 通过 继承 和 方法重写 实现 , 多个子类 继承 同一个父类 。
代码示例:
class Animal: # 创建 animal父类
name = "Animal"
age = 0
def make_sound(self):
print("动物发音")
class Dog(Animal): # 创建 dog子类
def make_sound(self):
print("汪汪")
class Cat(Animal): # 创建 cat子类
def make_sound(self):
print("喵喵")
# 创建对象,调用同一个方法 make_sound
animal = Animal()
animal.make_sound()
dog = Dog()
dog.make_sound()
cat = Cat()
cat.make_sound()
运行结果:
(4)抽象类
方法体为空 , 也就是 pass 的方法 , 称为 " 抽象方法 " 。有 " 抽象方法 " 的类 , 称为 抽象类 。
父类只定义抽象方法 , 没有具体实现 。
抽象类只定义有哪些方法,子类负责实现具体的方法逻辑。
五、Python操作MySQL数据库
复习总结见此视频链接:https://www.bilibili.com/video/BV1qW4y1a7fU?p=136&vd_source=1a462675092f937f148277eae6265dff
六、闭包和装饰器
闭包概念
在双层函数嵌套的前提下,内部函数使用了外部函数的变量并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
闭包格式要求:
1、必须有一个双层内嵌函数。
2、内嵌函数必须引用外部函数的变量。(使用关键字nonlocal可以修改外部函数的变量)
3、外部函数的返回值必须是内嵌函数。
闭包作用
当我们定义一个函数时,此时需要使用到外部的全局变量时,可以使用global关键字来接收全局变量。但此时这个全局变量若被其他类导入就有可能有被修改的风险。
因此为了保证全局变量不被其他类篡改,于是就使用闭包将这个全局变量定义在外部函数的参数列表中,形成一个嵌套函数。
以下代码为演示闭包操作存取款账户余额的案例:
方式一:使用global关键字来接收全局变量
因使用此种方法,全局变量容易有被修改的风险,于是将使用闭包的方法来实现以上存取款账户余额功能。
方式二:使用闭包方法来实现:使用关键字nonlocal可以修改外部函数的变量 (nonlocal使用在内部函数方法体中)
闭包的使用方法可以分为两种:内部函数中不需要修改外部函数的全局变量(简单闭包)、内部函数中需要修改外部函数的全局变量(复杂闭包)。
一、简单闭包
# 简单闭包:不需要修改外部函数的全局变量
def func(name):
def func1(msg):
print(f"{name}对我说:{msg}")
return func1
fu = func("小红")
fu("早上好")
fu1 = func("小军")
fu1("你刚刚在干嘛?")
二、复杂闭包(带有参数,使用关键字nonlocal可以修改外部函数的变量)
nonlocal使用方式:在内部函数方法体中使用
# 复杂闭包(带有参数,使用关键字nonlocal可以修改外部函数的变量)
def func(name):
def func1(msg):
nonlocal name
name = "小猫"
print(f"{name}对我说:{msg}")
return func1
fu = func("小红")
fu("早上好")
fu1 = func("小军")
fu1("你刚刚在干嘛?")
闭包优缺点
优点:不用定义全局变量,也可以让函数持续的访问和修改一个外部变量。全局变量是外部函数的参数列表,其不容易被误操作修改。
缺点:内部函数需要使用外部函数的全局变量,因此需要占据额外的内存空间。
闭包使用场景
- 装饰器:在不修改原有函数代码的情况下,增加额外的功能。
- 回调函数:封装了状态的函数可以作为回调函数传递给某些操作。
- 函数工厂:根据输入参数的不同返回不同行为的函数。
装饰器
装饰器的概念就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新的功能。
装饰器使用方法:使用创建一个闭包函数,在闭包函数的内部函数里面来调用目标函数。
案例:有一个睡觉sleep功能,现在需要在sleep功能前加一个功能:
- 在调用sleep功能前输出:我要睡觉了
- 在调用sleep功能后输出:我要起床了
装饰器一般写法
装饰器语法糖写法
装饰器语法糖使用格式:在目标函数的上方使用 @+修饰函数名 ,然后再 直接调用目标函数方法 即可。
七、设计模式:单例模式和工厂模式
设计模式是一种编程套路,可以极大的方便程序的开发,最常见、最经典的设计模式,就是我们所学习的面向对象了。
除了面向对象外,在编程中也有很多既定的套路可以方便开发,我们称之为设计模式
- 单例模式、工厂模式
- 建造者、责任链、状态、备忘录、解释器、访问者、观察者、中介、模板、代理模式等模式
单例模式
工厂模式
工厂模式就是将对象的创建由使用原生类本身创建转换到由特定的工厂方法来创建。
使用工厂类的get_person()方法去创建具体的类对。
工厂模式的优点:
- 大批量创建对象的时候有统一的入口,易于代码维护
- 当发生修改,仅修改工厂类的创建方法即可
- 符合现实世界的模式,即由工厂来制作产品(对象)
八、多线程编程
进程与线程
进程:就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程D方便系统管理。
线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位。
进程就好比一家公司,是操作系统对程序进行运行管理的单位
线程就好比公司的员工,进程可以有多个线程(员工),是进程实际的工作者
1、操作系统中可以运行多个进程,即多任务运行
2、一个进程内可以运行多个线程,即多线程运行
注意点:
- 进程之间是内存隔离的,即不同的进程拥有各自的内存空间。这就类似于不同的公司拥有不同的办公场所
- 线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。这就好比,公司员工之间是共享公司的办公场所。
并发与并行
并发:并发是指两个或多个事件在同一时间间隔发生
并行:并行是指两个或者多个事件在同一时间时刻发生
区别如下:
处理方式不同
1、并发:并发在一台处理器上“同时”处理多个任务。
2、并行:并行在多台处理器上同时处理多个任务。
物理方式不同
1、并发是逻辑上的同时发生
2、并行是物理上的同时发生
来个比喻:并发是一个人同时吃3个馒头,而并行是3个人同时吃3个馒头
关键区别
- 并发是指多个任务交替地执行,即多个任务按照时间片轮转的方式交替执行,它们共享同一个处理器
- 并行是指同时执行多个任务,每个任务有自己独立的处理器
1、多个进程同时在运行,即不同的程序同时运行,称之为多任务并行执行
2、一个进程内的多个线程同时在运行,称之为多线程并行执行
多线程编程:threading模块
演示代码:
"""多线程编程演示:交替执行打印111、222"""
import threading
import time
# 定义两个函数来执行打印111、222
def func1():
while True:
print("111")
time.sleep(1)
def func2():
while True:
print("222")
time.sleep(1)
func1_thread = threading.Thread(target=func1) # 创建线程
func2_thread = threading.Thread(target=func2) # 创建线程
func1_thread.start() # 开启线程
func2_thread.start()
传参方式 args\kwargs
多线程编程中使用args或者kwargs来进程参数传递。
args是以元组的方式进行传参,kwargs是以字典的方式进行传参。
九、网络编程
网络编程就是使用Socket来进行服务端和客户端开发
socket概念
socket(简称 套接字)是进程之间通信一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行。
进程之间想要进行网络通信需要socket。
socket负责进程之间的网络数据传输,好比数据的搬运工。
socket服务端编程
代码如下:
import socket
# 创建Socket对象
socket_server = socket.socket()
# 绑定ip地址和端口(使用元组形式传参)
socket_server.bind(("localhost", 8888))
# 监听端口
socket_server.listen(1) # listen 方法内接受一个整数传参数,表示接受的链接数量
# 等待客户端链接
conn, address = socket_server.accept() # accept方法返回的是二元元组(链接对象, 客户端地址信息)
# 可以通过 变量1,变量2= socket_server.accept()的形式,直接接受二元元组内的两个元素
# accept()方法,是阻塞的方法,等待客户端的链接,如果没有链接,就卡在这一行不向下执行了
print(f"接收到了客户端的链接,客户端的信息是: {address}") # 接受客户端信息,要使用客户端和服务端的本次链接对象,而非socket_server对象
while True: # 使用循环可以进行无数次服务端与客户端通信
data: str = conn.recv(1024).decode("UTF-8")
# recv接受的参数是缓冲区大小,一般给1024即可
# recv方法的返回值是一个字节数组也就是bytes 对象,不是字符,可以通过decode 方法通过UTF-8编码,将字节数组转换为字符申对象
print(f"客户端发来的消息是: {data}")
# 发送回复消息
msg = input("请输入你要和客户端回复的消息:")
if msg == "exit": # 通信结束条件
break
conn.send(msg.encode("UTF-8")) # encode可以将字符用编码为字节数组对象
# 关闭链接
conn.close()
socket_server.close()
socket客户端编程
代码如下:
# 演示Socket客户端开发
import socket
# 创建socket对象
socket_client = socket.socket()
# 连接到服务端
socket_client.connect(("localhost", 8888))
while True:
# 发送消息
msg = input("请输入要给服务端发送的消息:")
if msg == 'exit': # 判断结束通信的条件
break
socket_client.send(msg.encode("UTF-8"))
# 接收返回消息
recv_data = socket_client.recv(1024) # 1024是缓冲区的大小,一般1024即可。 同样recv方法是阻塞的
print(f"服务端回复的消息是: {recv_data.decode('UTF-8')}")
# 关闭链接
socket_client.close()
视频学习链接:https://www.bilibili.com/video/BV1qW4y1a7fU?p=160&vd_source=1a462675092f937f148277eae6265dff
十一、正则表达式
正则表达式
正则表达式,又称规则表达式,是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。
简单来说,正则表达式就是使用字符串定义规则,并通过规则去验证字符串是否匹配。
比如说如果需要验证一个字符串是否是符合条件的电子邮箱地址,只需要配置好正则规则,即可匹配任意邮箱。
比如通过正则规则: (w-]+w-]+)@w-]+.w-]+)+$) 即可匹配一个字符串是否是标准邮箱格式
正则基础方法
Python正则表达式使用re模块并基于re模块中三个基础方法来做正则匹配,分别是: match、search、findall 三个基础方法
(1)re.match
从被匹配字符串开头进行匹配,匹配成功返回匹配对象(包含匹配的信息),若开头匹配不成功(即使后面部分可以匹配成功也直接返回空)则直接返回空。
找到第一个匹配字符串就直接返回符合下标 (左闭右开),后面就停止继续匹配了。
(2)re.search
搜索整个字符串找出匹配的,从前向后找到第一个后就停止,不会再继续向后匹配了。若整个字符串都没有匹配成功则返回空None
(3) re.findall
匹配整个字符串,找出全部匹配项,找不到则返回空list:[]
总结如下:
- re.match:从头开始匹配,匹配第一个命中项
- re.search:全局匹配,匹配第一个命中项
- re.findall:全局匹配,匹配全部命中项
正则元字符匹配
以下是正则表达式的字符匹配规则:
字符串前面带上r的标记,表示字符串中转义字符无效,就是普通字符的意思
代码如下示例:
import re
s1 = "python1245s fbuvna *&9-!? shic38097d"
# (1) 找出所有的数字字符:\d 匹配数字0-9
result1 = re.findall(r"\d", s1) # 字符串前面带上r的标记,表示字符串中转义字符无效,就是普通字符的意思
print(result1)
# (2)找出所有的英文字符:[] 自定义匹配规则
result2 = re.findall(r"[a-zA-Z]", s1)
print(result2)
# (3)找出所有的特殊字符:\W 数字字母下划线除外都为特殊字符
result3 = re.findall(r"\W", s1)
print(result3)
# (4)匹配账号,只能由字母和数字组成,长度限制6到10位
s2 = "python13"
result4 = re.findall(r"^[a-zA-Z0-9]{6,10}$", s2) # ^匹配字符串开头,$匹配字符串结尾,{6,10}匹配字符串长度
print(result4) # 匹配成功
s3 = "@python13"
result5 = re.findall(r"^[a-zA-Z0-9]{6,10}$", s3) # ^匹配字符串开头,$匹配字符串结尾,{6,10}匹配字符串长度
print(result5) # 匹配失败
# (5)匹配QQ号,要求纯数字,长度为5-7,第一位不为0
s4 = "123456"
result6 = re.findall(r"^[1-9][0-9]{4,6}$", s4) # 若需要判断整个字符串就需要加字符串开头^和字符串结尾$(判断子串就不需要加^、$)
print(result6)
s5 = "012345"
result7 = re.findall(r"^[1-9][0-9]{4,6}$", s5)
print(result7)
# (6)匹配邮箱地址,只允qq、163、gmail这三种邮箱地址
"""
邮箱格式如下:
abc.efg.daw@gq.com.cn.eu.qq.aa.cc
abc@gq.com
{内容),{内容),{内容),内容),{内容),内容).内容).[内容)@内容)(内容).{内容)
"""
s6 = "31245@qq.com"
result8 = re.match(r"^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$", s6) # match从头开始匹配第一个符合条件的
print(result8)