一、封装的概念
封装
- 隐藏:属性和实现细节,不允许外部直接访问
- 暴露:公开方法,实现对内部信息的操作与访问
封装的作用
- 限制安全的访问和操作,提高数据安全性
- 可进行数据检查,从而有利于保证对象信息的完整性
二、封装的实现
- 保护属性:_属性名,仅供类体内部使用
- 私有属性:__属性名
- 被视作类名_属性名
class Account:
"""账户"""
# 普通属性
bank = 'BOC'
# 保护属性(内部属性)
_username = 'zhizhi'
# 私有属性
__password = '888'
# 通过类名访问属性
print(Account.bank) # 打印BOC
print(Account._username) # 打印zhizhi
print(Account.__password) # 报错AttributeError
print(Account.__dict__)
print(Account._Account__password) # 打印888
封装的实现:暴露
- 提供数据访问功能(getter)
- 计算属性,该属性并不真正的存储任何状态,值是通过某种算法计算得到的,当程序对该属性进行赋值时,被赋的值通常存储到实例变量中
- 语法:使用@property装饰器
- 调用:实例.方法名
class Account:
"""账户"""
# 普通属性
bank = 'BOC'
# 保护属性(内部属性)
_username = 'zhizhi'
# 私有属性
__password = '888'
@property
def password(self):
return self.__password
# 实例化
obj = Account()
# 访问实例的私有属性
print(obj.password)
修改
- 提供数据操作功能(setter)
- 语法:使用@计算属性名.setter装饰器
- 调用:实例.方法名
class Account:
"""账户"""
# 普通属性
bank = 'BOC'
# 保护属性(内部属性)
_username = 'zhizhi'
# 私有属性
__password = '888'
@property
def password(self):
return self.__password
@password.setter
def password(self, value):
# 增加数据的校验
if len(value) >= 8:
self.__password = value
else:
print("密码长度最少有8位!")
# 实例化
obj = Account()
# 访问实例的私有属性
print(obj.password) # 888
# 修改私有属性(满足校验条件)
obj.password = 'zhizhi666'
print(obj.password) # zhizhi666
# 修改私有属性(不满足校验条件)
obj.password = '1123'
print(obj.password) #密码长度最少有8位!
# zhizhi666
三、python继承
1、继承的概念
继承(Inheritance)
- 复用父类的公开属性和方法
- 拓展出新的属性和方法
2、继承的实现
- 语法:class类名(父类列表)
- 默认父类是object
- python支持多继承
class Human:
"""人类"""
# 类属性
message = '这是Human类的属性'
# 构造方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def live(self):
print('住在地球上')
class Student(Human):
# 子类实例方法
def study(self):
print('正在学习')
# 实例化子类对象
stu = Student('zhizhi', 18)
# 访问属性,获得父类的类属性
print(stu.message) # 这是Human类的属性
print(stu.name, stu.age)
# 访问实例方法
stu.live()
stu.study()
3、类型检查
- isinstance(实例, 类名)
- 检查对象是否是某个类及其派生类的实例
- issubclass(类名1,类名2)
- 检查类名1是否是类名2的子类
class Human:
pass
class Student(Human):
pass
class Teacher(Human):
pass
# 检查实例和类
stu = Student()
print(isinstance(stu, Human)) # True
# 检查子类和父类
print(issubclass(Student, Human)) # True
print(issubclass(Student, Teacher)) # False
四、多态
1、多态的概念
python是弱类型语言,对于弱类型语言来说,变量没有声明类型,因此,同一个变量可以在不同的时间代表不同的对象,当同一个变量调用同一个方法时,可以呈现出多种行为
- 多态(Polymorphism)
- 同名方法呈现多种行为
2、多态的表现
运算符的多态表现
- +号
- 加法:数字+数字
- 拼接:字符串+字符串
- 合并:列表+列表
print(1+1) # 2
print("hello"+"world") # hello world
print([1,2]+[3]) # [1, 2,3]
函数的多态表现
- len()函数
- 可以接收字符串
- 可以接收列表
print(len("zhizhi"))
print(len([1,2,3]))
方法的多态表现
- 同名变量调用同名方法呈现多种行为
class China:
def speak(self):
print('汉语')
class Usa:
def speak(self):
print('English')
# 实例化对象
cn = China()
us = Usa()
# 遍历
for x in (cn, us):
x.speak()
# 汉语
# English
3、多态与继承
- 方法重写(override):子类的方法名称与父类的相同,子类优先级更高
- 重写构造方法
- super().init()
- 父类名.init(self)
class Human:
"""人类"""
# 类属性
message = '这是Human类的属性'
# 构造方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def live(self):
print('住在地球上')
class Student(Human):
# 重写父类构造方法
def __init__(self, name, age, school):
# 访问父类的构造方法
# super().__init__(name, age)
# super(Student, self).__init__(name, age)
Human.__init__(self, name, age)
self.school = school
# 重写父类实例方法
def live(self):
print(f'住在{self.school}')
# 实例化子类对象
stu = Student('zhizhi', 12, 'gelanfenduo')
print(stu.school)
stu.live()
四、模块与包
(一)模块
1、项目目录结构
程序结构
- 组成:
- package包
- module模块
- function方法
2、模块定义
- 包含python定义和语句的文件
- .py文件
- 作为脚本运行
3、文件引用
模块导入:模块是在代码量变得相当⼤了之后,为了将需要重复使⽤的有组织的代码放在
⼀起,这部分代码可以被其他程序引⽤,从⽽使⽤该模块⾥的函数等功能,引
⽤的过程叫做导⼊(import)
- 在python中,⼀个⽂件(以“.py”为后缀名的⽂件)就叫做⼀个模块
- 导⼊模块的写法:
- ⽅法⼀:import modul1[,module2…[,moduleN]]
- ⽅法⼆:from module import <name[,name2,….[,nameN]]>
- import模块名
- from <模块名> import <方法名|变量|类>
- from <模块名> import *
- 注意:
- 同一个模块写多次,只被导入一次
- import应该放在代码的顶端
- 使⽤关键词“import + 模块名称” 导⼊某⼀个模块
- 注意:
模块分类
- 系统内置模块
- sys、time、json模块等等;
- 第三⽅的开源模块
- 通过pip install进⾏安装,有开源的代码
- ⾃定义模块
- ⾃⼰写的模块,对某段逻辑或某些函数进⾏封装后供其他函数调⽤。
如何使用模块
1、系统内置模块
- Python安装好之后⾃带的⼀些⾮常有⽤的模块 (sys,os,time,json模块
等)
import sys
print("调⽤了sys模块")
for i in sys.argv:
print(i)
2、第三⽅开源模块
- 是通过包管理⼯具pip完成的。必须先知道该库的名称,可以在官⽹或者pypi
上搜索,⽐如MySQL驱动程序,Web框架Flask,科学计算Numpy等 - NumPy(Numerical Python) 是 Python 语⾔的⼀个扩展程序库,⽀持⼤量的维
度数组与矩阵运算,此外也针对数组运算提供⼤量的数学函数库 - pip install numpy
3、⾃定义模块
- ⾃定义模块是⾃⼰写的模块,对某段逻辑或某些函数进⾏封装后供其他函数调⽤。
- 模块由变量,函数,或类组成
举例: - 创建⼀个模块 baidu.py
- 创建⼀个调⽤模块 index.py
- 注意:⾃定义模块的名字⼀定不能和系统内置的模块重名,否则将不能再导⼊系统的内置模块。
- 例如,⾃定义了⼀个sys.py模块后,那系统的sys模块就不能使⽤
4、导⼊模块
- import 模块名 引⼊⼀个完整的模块
- from <模块名> import <⽅法 | 变量 | 类> 引⼊模块中的⼀个或多个指定部分
- from <模块名> import * 导⼊模块⾥⾯的所有的函数,变量
区别: - import 导⼊模块,每次使⽤模块中的函数,都要是定是哪个模块
- from…import * 导⼊模块,每次使⽤模块中的函数,直接使⽤函数就可以了(因为已经知道该函数是那
个模块中的了)
5、常用方法
- dir() 找出当前模块定义的对象
- dir(sys). 找出参数模块定义的对象
作用域
- 搜索路径(print(sys.path())
- 当你导⼊⼀个模块,Python 解析器对模块位置的搜索顺序是:
- 1、当前⽬录
- 2、如果不在当前⽬录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个⽬录
- 3、如果都找不到,Python会察看默认路径。UNIX下,默认路径⼀般为/usr/local/lib/python/
- 模块搜索路径存储在 system 模块的 sys.path 变量中。变量⾥包含当前⽬录,PYTHONPATH和
由安装过程决定的默认⽬录。
使⽤模块的好处
- 提⾼代码的可维护性
- ⼀个模块编写完毕之后,其他模块直接调⽤,不⽤再从零开始写代码了,节约
了⼯作时间; - 避免函数名称和变量名称重复,在不同的模块中可以存在相同名字的函数名和
变量名(不要和系统内置的模块名称重复)
五、错误与异常
什么是异常
错误 与 异常的区别?
- 错误与异常都是在程序编译和运⾏时出现的错误
- 异常可以被开发⼈员捕捉和处理
- 错误⼀般是系统错误,⼀般不需要开发⼈员处理(也⽆法处理),⽐如内存溢出
什么是异常?
- 异常即是⼀个事件,该事件会在程序执⾏过程中发⽣,影响了程序的正常执⾏。
- 有些是由于拼写、配置、选项等等各种引起的程序错误,有些是由于程序功能处理逻辑不完善引起的漏洞,这些统称为程序中的异常
常见的异常类型
- 除零异常、名称异常、索引异常、键异常、值异常、属性异常等等
语法错误与定位
异常处理流程:
- 检测到错误->引发异常->捕获异常操作
异常解决⽅案
- 如果是拼写、配置等引起的错误,根据出错信息进⾏排查错误出现的位置进⾏解决
- 如果是程序设计不完善引起的漏洞,根据漏洞的情况进⾏设计处理漏洞的逻辑
异常捕获、异常处理
# 相除
# def div(a, b):
# if b != 0:
# return a / b
# else:
# print('分母不能为0')
# return 0
def div(a, b):
return a / b
f = open('data.txt')
try:
print(div(1, 1))
list1 = [1, 2, 3]
print(list1[3])
f.readlines()
except Exception as e:
print(e)
print('这里有个异常')
finally: # finally最终都会被执行,无论有异常还是没有异常的情况
print('finally')
f.close()
def set_age(num):
if num <= 0 or num > 200:
raise ValueError(f'值错误:{num}')
else:
print(f'设置的年龄为:{num}')
set_age(-1)
⾃定义异常
语法
raise [Exception [, args [, traceback]]]
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class MyException(Exception):
def __init__(self, msg):
print(f'这是一个异常:{msg}')
def set_age(num):
if num <= 0 or num > 200:
raise MyException(f'值错误:{num}')
else:
print(f'设置的年龄为:{num}')
set_age(-10)
六、debug调试与分析
调试的概念与技术体系
- 程序调试是将编制的程序投⼊实际运⾏前,⽤⼿⼯或编译程序等⽅法进⾏测
试,修正【语法错误和逻辑错误】的过程。 - 语法错误:编写的python语法不正确,程序编译失败。
- 逻辑错误:代码本⾝能够正常执⾏,但是执⾏完成的结果不符合预期结果。
- 什么是bug:bug是计算机领域专业术语(⼩昆⾍; ⾍⼦),计算机⾥叫漏洞,隐藏在程序中的⼀些未被发现的缺陷或问题统称为bug(漏洞)
程序调试
- 1、语法错误
- 类型错误,语法错误,缩进错误,索引错误,键错误
- 2、逻辑错误
- 业务逻辑错误,并不会报错
调试方法
- 1、对应位置使⽤
print
或者logging
打印⽇志信息 - 2、启动断点模式debug调试
程序运⾏时堆栈分析
七、类型注解
用法
https://docs.python.org/zh-cn/3/library/typing.html
def greeting(name: str) -> str:
return 'Hello ' + name.split(',')[1]
print(greeting('python,java'))
类型提示的好处
- 1、增强代码可读性
- 2、ide 中代码提示
- 3、静态代码检查
IDE 中代码中提示功能
为类型起别名
from typing import List
Vector = List[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
print(scale(1.1, [1.2, -4.2, 5.4]))
设置提示类型
自定义类型
class Student:
name: str
age: int
def get_stu(name: str)->Student:
return Student()
get_stu(). --> 有相应的提示
静态代码检查功能
安装 mypy
pip install mypy
实例
from typing import List
a: List[int] = []
a= [1,2,'1']
运行
mypy demo.py
类型注解总结
- 1、增强代码可读性
- 2、ide 中代码提示
- 3、静态代码检查
八、数据类 dataclass
dataclass 介绍
-
dataclass 优势
- 可读性强
- 操作灵活
- 轻量
-
应用场景
- 创建对象
- 完美融合平台开发 ORM 框架
- 场景:如果创建一只猫,信息包括猫的名字、体重、颜色。同时打印这个对象的时候,希望能打印出一个字符串(包含猫的各种信息)应该如何编写代码
- 问题:
- 数据修改不方便
- 代码冗余
- 解决方案:
- 使用自定义类实现数据类
class Cat:
name: str
weight: int
color: str
def __init__(self, name, weight, color):
self.name = name
self.weight = weight
self.color = color
def __str__(self):
return f'猫猫姓名{self.name},体重{self.weight},颜色{self.color}'
def __repr__(self):
return f'===>>>猫猫姓名{self.name},体重{self.weight},颜色{self.color}'
if __name__ == '__main__':
cat = Cat("原味鸡", 20, '黑色')
print(cat)
数据类更优雅的实现方案
- 使用 dataclass 创建数据类
- 实例化的时候自动生成构造函数
from dataclasses import dataclass
@dataclass
class Cat:
name: str
color: str
weight: int
if __name__ == '__main__':
cat = Cat('原味鸡', '黑色', 20)
print(cat)
field 的使用
# 错误写法,执行报错
@dataclass
class Cat:
name: str
color: str
weight: int
children: list=[1,2,3]
# 正确写法,可变类型必须使用field
from dataclasses import dataclass, field
@dataclass
class Cat:
name: str
color: str = field(default='black')
# weight: int =2
weight: int = field(default=2)
# 可变参数list,dict,需要通过default_factory来指定类型或者默认值
children: list = field(default_factory=list)
if __name__ == '__main__':
cat = Cat('原味鸡')
print(cat)
field 常用参数
参数名 | 参数功能 |
---|---|
default | 字段的默认值 |
default_factory | 定义可变量参数的默认值,default 和 default_factory 不能同时存在 |
init | 如果为 true(默认值),该字段作为参数包含在生成的 init() 方法中。 |
repr | 如果为 true(默认值),该字段包含在生成的 repr() 方法返回的字符串中。 |
field default 参数
- 字段的默认值
field init 参数
- 如果为 True(默认值),该字段作为参数包含在生成的 init() 方法中。
- 如果为 False,该字段不会包含 init() 方法参数中。
@dataclass
class Cat:
name: str
color: str = field(default='black')
# init=False 在实例化对象的时候,不需要设置这个值,需要给一个默认值
weight: int = field(default=5, init=False)
# 可变参数list,dict,需要通过default_factory来指定类型或者默认值
children: list = field(default_factory=list)
"""
如果为 True(默认值),该字段作为参数包含在生成的 init() 方法中。
如果为 False,该字段不会包含 init() 方法参数中。
体现在:在实例化的时候,是否需要传递这个参数
"""
if __name__ == '__main__':
cat = Cat(name='原味鸡', color='black', children=[1, 2, 3])
print(cat)
field repr 参数
- 如果为 True(默认值),该字段包含在生成的 repr() 方法返回的字符串中。
- 如果为 False,该字段不会包含在生成的 repr() 方法返回的字符串中。
@dataclass
class Cat:
name: str
color: str = field(default='black')
# repr=False 打印的时候,就不会包括这个字段
weight: int = field(default=5, repr=False)
children: list = field(default_factory=list)
if __name__ == '__main__':
cat = Cat('原味鸡', 'black', 10, children=[1, 2, 3])
print(cat)
常用的方法
- asdict() 转化实例对象为字典格式
from dataclasses import dataclass, field, asdict
@dataclass
class Cat:
name: str
color: str = field(default='black')
# repr=False 打印的时候,就不会包括这个字段
weight: int = field(default=5, repr=False)
children: list = field(default_factory=list)
# asdict() 直接将实例对象转成 字典格式
if __name__ == '__main__':
cat = Cat('原味鸡', '黑色', 20, [1, 2, 3])
print(asdict(cat))
总结
- 可读性强
- 操作灵活
- 轻量
九、内置装饰器
内置类装饰器
- 不用实例化、直接调用
- 提升代码的可读性
内置装饰器 含义
classmethod 类方法
staticmethod 静态方法
普通方法
定义:
- 第一个参数为self,代表 实例本身
调用:
- 要有实例化的过程,通过 实例对象.方法名 调用
# 1、定义
class MethodsDemo:
param_a = 0 # 类变量
def normal_demo(self): # 定义一个类方法,第一个参数必须为self
"""
普通方法
:return:
"""
print('这是一个普通方法', self.param_a)
# 2、调用
md = MethodsDemo()
md.normal_demo()
类方法
定义:
- 使用 @classmethod 装饰器,第一个参数为类本身,所以通常使用cls命名做区分(非强制)
- 在类内可以直接使用类方法或类变量,无法直接使用实例变量或方法
调用: - 无需实例化,直接通过 类.方法名 调用,也可以通过 实例.方法名 调用
class MethodsDemo:
param_a = 0 # 类变量
# 定义一个类方法,第一个参数必须为self
@classmethod
def classmethod_demo(cls):
"""
类方法,第一个参数需要改为cls
:return:
"""
print('这是一个类方法', cls.param_a)
# 2、类的调用,无需实例化,直接调用
MethodsDemo.classmethod_demo()
静态方法
定义:
- 使用 @staticmethod 装饰器,没有和类本身有关的参数
- 无法直接使用任何类变量、类方法或者实例方法、实例变量
调用:
- 无需实例化,直接通过 类.方法名 调用,也可以通过 实例.方法名 调用
"""静态方法"""
class StaticMethod:
@classmethod
def class_method(cls):
print('这是一个类方法')
def demo_method(self):
print("这是一个普通方法")
# 定义: 1、要使用staticmethod装饰器 2、这个方法是没有self、cls
@staticmethod
def static_demo(param1):
print('这是一个静态方法')
# 2、调用:类.方法名
StaticMethod.static_demo('zhizhi')
demo = StaticMethod()
demo.static_demo('zhizhi')
普通方法、类方法、静态方法
名称 | 定义 | 调用 | 关键字 | 使用场景 |
---|---|---|---|---|
普通方法 | 至少需要一个参数self | 实例名.方法名() | 无 | 方法内部涉及到实例对象属性的操作 |
类方法 | 至少需要一个cls参数 | 类名.方法名() 或者实例名.方法名() | @classmethod | 如果需要对类属性,即静态变量进行限制性操作 |
静态方法 | 无默认参数 | 类名.方法名() 或者实例名.方法名() | @staticmethod | 无需类或实例参与 |
实际案例
- 右边的代码实现的需求是格式化输出时间
- 如果现在需求变更,输入 年、月、日 没法保证格式统一,可能是json,可能是其他格式的字符串,在不修改构造函数的前提下,如何更改代码
class DateFormat:
def __init__(self, year= 0, month=0, day=0):
self.year = year
self.month = month
self.day = day
def out_date(self):
return f'输入的时间为{self.year}年,{self.month}月,{self.day}日'
@classmethod
def json_format(cls, json_data):
"""
输入一个字典格式的数据信息,返回(2023, 6, 3)
:param json_data:
:return:
"""
year, month, day = json_data["year"], json_data["month"], json_data["day"]
return cls(year, month, day)
# def json_format(json_data):
# year, month, day = json_data["year"], json_data["month"], json_data["day"]
# return year, month, day
json_data = {"year": 2023, "month": 6, "day": 27}
# 使用json格式化,生成想要的的日期格式,返回DateFormat实例
# year, month, day = 2023, 6, 27
demo = DateFormat.json_format(json_data)
print(demo.out_date())
静态方法实际案例
- 此方法没有任何和实例、类相关的部分,可以作为一个独立函数使用
- 某些场景下,从业务逻辑来说又属于类的一部分
class Game:
"""
staticmethod使用场景
方法所有涉及到的逻辑都没有使用实例方法或者实例变量的时候
"""
def __init__(self, first_hero, second_hero):
self.first_hero = first_hero
self.second_hero = second_hero
# fight有和实例变量交互的部分,所以需要定义一个普通方法
def fight(self):
print(f'本轮比赛开始,由{self.first_hero}VS{self.second_hero}')
# start没有和类或实例交互的部分,那么可以使用staticmethod
@staticmethod
def start():
print('游戏开始')
Game.start()
game1 = Game('Bob', 'Tom')
game2 = Game('Mike', 'Herry')
game1.fight()
game2.fight()
- 例子:简单工厂方法
# staic使用场景
class HeroFactory:
"""
staticmethod使用场景
方法所有涉及到的逻辑都没有使用实例方法或者实例变量的时候
伪代码
"""
@staticmethod
def create_hero(hero):
if hero == 'ez':
return EZ()
elif hero == 'jinx':
return JINX()
elif hero == 'timo':
return TIMO()
else:
raise Exception('此英雄不在此工厂')