面向对象编程
1. 面向对象
🥝 面向对象
面向对象:一种抽象化的编程思想→化简代码、逻辑
Conclusion:面向对象就是将编程当作一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事。
面向对象三大特性
- 封装:将属性和方法写到类里面的操作.
- 封装可以为属性和方法添加私有权限
- 继承
- 子类默认继承父类的所有属性和方法
- 子类可以重写父类属性和方法
- 多态
- 传入不同的对象,产生不同的结果
🥝 经典类和新式类
经典类:不由任意内置类型派生出的类(Python2.0版本默认是经典类)
class 类名:code
新式类(Python3.0默认)
class 类名(object):code
在Python中,所有类默认继承object类,object类是顶级类(基类),其他子类叫做派生类。
🥝 类和对象的关系
在面向对象编程过程中,有两个重要组成部分:类和对象。
类和对象的关系:用类去创建(or实例化)一个对象。
类是对一系列具有相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物。
- 特征即是属性
- 行为即是方法
对象是类创建出来的真实存在的事物。对象又名实例。
开发中,先有类,再有对象。
🥝 类和对象的语法
定义类的语法:
class 类名():代码
类名要满足标识符命名规则,同时遵循大驼峰命名习惯。
对象的语法:
对象名=类名()
🥝 面向对象实现方法
# 定义类
"""
class 类名():代码
"""
class Washer():def wash(self):print('能洗衣')# 创建对象
# 对象名=类名()
haier=Washer()# 验证成果
# 打印haier对象、使用wash功能
print(haier) #<__main__.Washer object at 0x000001F9017434C8>
haier.wash() #能洗衣
🥝 类里面的self
self指的是调用该函数的对象。
# 定义类
"""
class 类名():代码
"""
class Washer():def wash(self):print('能洗衣') #能洗衣print(self) #<__main__.Washer object at 0x000001E3E5073508># 创建对象
# 对象名=类名()
haier=Washer()# 验证成果
# 打印haier对象、使用wash功能
print(haier) #<__main__.Washer object at 0x000001E3E5073508>
haier.wash() #能洗衣 <__main__.Washer object at 0x000001E3E5073508>
🥝 一个类创建多个对象
class Washer():def wash(self):print('能洗衣') #能洗衣print(self) """<__main__.Washer object at 0x000001F2A45932C8> <__main__.Washer object at 0x000001F2A4593808>"""# 创建多个对象
haier=Washer()
haier1=Washer()
# 验证成果
print(haier) #<__main__.Washer object at 0x000001F2A45932C8>
print(haier1)#<__main__.Washer object at 0x000001F2A4593808>
haier.wash() #能洗衣 <__main__.Washer object at 0x000001F2A45932C8>
haier1.wash()#能洗衣 <__main__.Washer object at 0x000001F2A4593808>
🥝 类外面添加对象属性
属性即是特征,比如∶洗衣机的宽度、高度、重量…
对象属性既可以在类外面添加和获取,也能在类里面添加和获取。
类外面添加对象属性的语法:
对象名.属性名=值
类外面添加对象属性:
haier.width=500
haier.height=800
🥝 类外面获取对象属性
类外面获取对象属性的语法:
对象名.属性名
类外面获取对象属性:
print(f'haier洗衣机的宽度是{haier.width}')
print(f'haier洗衣机的高度是{haier.height}')
🥝 类里面获取对象属性
类里面获取对象属性的语法:
self.属性名
类里面获取对象属性:
class Washer():def print_info(self):# 在类里面获取实例属性print(f'haier洗衣机的宽度是{self.width}')print(f'haier洗衣机的高度是{self.height}')# 创建多个对象
haier=Washer()# 添加实例属性
haier.width=500
haier.height=800# 对象调用实例方法
haier.print_info()
"""
haier洗衣机的宽度是500
haier洗衣机的高度是800
"""
🥝 小例子
"""
1. 应用需求
需求主线:0-3min:生的; 3-5min:半生; 5-8min:熟的; ≥8min:烤糊
添加调料:用户按意愿添加调料。
2. 步骤分析
涉及事物:地瓜🍠,涉及的类:地瓜类
定义类地瓜的属性: 被烤的时间; 地瓜的状态; 添加的调料地瓜的方法: 被烤(用户根据意愿设定每次烤地瓜的时间; 判断地瓜被烤的总时间是在那个区间,修改地瓜状态);添加调料(用户根据意愿设定添加的调料; 将用户添加的调料存储)
现实对象信息
"""# 定义地瓜类:初始化、被烤和添加调料、显示信息的str
class SweetPotato():def __init__(self):self.cook_time = 0 # 被烤的时间self.cook_state = '生的' # 地瓜状态self.condiments = [] # 调料列表为空def cook(self, time):self.cook_time += timeif 0 <= self.cook_time < 3:self.cook_state = '生的'elif 3 <= self.cook_time < 5:self.cook_state = '半生'elif 5 <= self.cook_time < 8:self.cook_state = '熟了'elif self.cook_time >= 8:self.cook_state = '烤糊'def add_condiments(self, condiment):self.condiments.append(condiment)def __str__(self):return f'这个地瓜烤了{self.cook_time}min,状态是{self.cook_state},添加的调料有{self.condiments}'# 创建对象并调用对应的实例方法
Potato1 = SweetPotato()
print(Potato1)
# 这个地瓜烤了0min,状态是生的,添加的调料有[]
Potato1.cook(2)
print(Potato1)
# 这个地瓜烤了2min,状态是生的,添加的调料有
Potato1.add_condiments('辣椒')
Potato1.cook(1)
print(Potato1)
# 这个地瓜烤了3min,状态是半生,添加的调料有['辣椒']
Potato1.add_condiments('酱油')
Potato1.cook(3)
print(Potato1)
# 这个地瓜烤了6min,状态是熟了,添加的调料有['辣椒', '酱油']
"""
1. 要求: 小于房子面积的家具搬进房子里面
2. 定义类房子类属性: 房子地理位置; 房子占地面积; 房子剩余面积; 房子内家具列表方法: 容纳家具; 显示房屋信息家具类: 家具名称, 家具占地面积
3. 创建对象并调用相关方法
"""
class Home():def __init__(self,address,area):self.address = addressself.area = areaself.free_area =areaself.furniture=[]def __str__(self):return f'房子坐落于{self.address},房子面积是{self.area},剩余面积{self.free_area},家具有{self.furniture}'def add_furniture(self,item):if self.free_area >= item.area:self.furniture.append(item.name)self.free_area -= item.areaelse:print('家具太大,剩余面积不足,无法容纳')class Furniture():def __init__(self,name,area):# 家具名字self.name = name# 家具占地面积self.area = areabed = Furniture('双人床',6)
home=Home('beijing',1200)
print(home)
home.add_furniture(bed)
print(home)
sofa=Furniture('沙发',10)
home.add_furniture(sofa)
print(home)
gallery = Furniture('画廊',1900)
home.add_furniture(gallery)
print(home)"""
房子坐落于beijing,房子面积是1200,剩余面积1200,家具有[]
房子坐落于beijing,房子面积是1200,剩余面积1194,家具有['双人床']
房子坐落于beijing,房子面积是1200,剩余面积1184,家具有['双人床', '沙发']
家具太大,剩余面积不足,无法容纳
房子坐落于beijing,房子面积是1200,剩余面积1184,家具有['双人床', '沙发']
"""
2. 魔法方法
在Python中,__xx__()
的函数叫做magic methods(魔法方法,也叫dunder methods),指的是具有特殊功能的函数。
__init__()
方法的作用:初始化对象。
init魔法方法:
class Washer():def __init__(self):self.width=500self.height=800def print_info(self):print(f'haier洗衣机的宽度是{self.width},高度是{self.height}')haier=Washer()
haier.print_info()
"""
haier洗衣机的宽度是500,高度是800
"""
__init__()
方法,在创建一个对象时默认被调用,不需要手动调用。
__init__(self)
中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
🥝 带参数的init
传参(参数传递)
多个对象的初始化属性:
class Washer():def __init__(self,width,height):self.width=widthself.height=heightdef print_info(self):print(f'洗衣机的宽度是{self.width},高度是{self.height}')haier=Washer(10,20)
haier.print_info()
"""
洗衣机的宽度是10,高度是20
"""
haier1=Washer(30,50)
haier1.print_info()
"""
洗衣机的宽度是30,高度是50
"""
TypeError:
__init__()
missing 2 required positional arguments: ‘width’ and ‘height’
🥝 魔法方法str
当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__
方法,那么就会打印从在这个方法中return的数据.
默认打印对象的内存地址:
class Washer():def __init__(self,width,height):self.width=widthself.height=heighthaier=Washer(10,20)
print(haier)
# 默认打印对象的内存地址
# 即:<__main__.Washer object at 0x000001C6F01F3A48>
# 一般我们自定义的类,__str__方法的回传值是默认的字串,
# 比如说:<__main__.Washer object at 0x000001C6F01F3A48>
# 用以说明namespace,class name和位置
魔法方法str:
class Washer():def __init__(self,width,height):self.width=widthself.height=heightdef __str__(self):return '说明书'haier=Washer(10,20)
print(haier) # 说明书
overrides method in object 覆盖对象中的方法
在python中自己写的数据类型使用print无法输出每个元素 - SegmentFault 思否
🥝 魔法方法del
当删除对象时,python解释器也会默认调用__del__()
方法。
class Washer():def __init__(self,width,height):self.width=widthself.height=heightdef __del__(self):print('对象已经删除')haier=Washer(10,20) # 对象已经删除
3. 继承 封装
🥝了解继承
Python面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法。
继承的好处:提高代码的重用率,节省了代码量。
继承的特点:
- 子类默认拥有父类的所有属性和方法 //不继承给子类的属性和方法需要添加私有权限
- 子类能重写父类同名方法和属性
- 子类能调用父类同名属性和属性 //super()快速调用父类方法
🥝 继承
# 父类A
class A(object):def __init__(self):self.num = 1def info_print(self):print(self.num)
# 子类B
class B(A):passresult =B()
result.info_print() #1
🥝 单继承
煎饼果子师父把果子技术传授给徒弟。
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'def make_cake(self):print(f'运用{self.skill}制作果子')# 徒弟类Prentice
class Prentice(Master):passresult =Prentice()
print(result.skill) # [煎饼果子配方]
result.make_cake() # 运用[煎饼果子配方]制作果子
🥝 多继承
当一个类有多个父类的时候,默认使用第一个父类的同名属性和方法。
徒弟从师父和学校学果子技术。
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'def make_cake(self):print(f'运用{self.skill}制作果子')# 学校类School
class School(object):def __init__(self):self.skill='[黑马果子]'def make_cake(self):print(f'运用{self.skill}制作果子')# 徒弟类Prentice
class Prentice1(Master,School):pass
class Prentice2(School,Master):passresult1 =Prentice1()
print(result1.skill) # [煎饼果子配方]
result1.make_cake() # 运用[煎饼果子配方]制作果子
result2 =Prentice2()
print(result2.skill) # [黑马果子]
result2.make_cake() # 运用[黑马果子]制作果子
🥝 子类重写父类同名属性和方法
子类重写父类同名属性和方法后,子类和父类拥有同名属性和方法,子类创建对象调用属性和方法的时候,调用到的是子类里面的同名属性和方法。
徒弟在掌握师父和学校果子技术后,自创一果子技术。
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'def make_cake(self):print(f'运用{self.skill}制作果子')# 学校类School
class School(object):def __init__(self):self.skill='[黑马果子]'def make_cake(self):print(f'运用{self.skill}制作果子')# 徒弟类Prentice
class Prentice(School,Master):def __init__(self):self.skill = '[自创果子术]'def make_cake(self):print(f'运用{self.skill}制作果子')result = Prentice()
print(result.skill) # [自创果子术]
result.make_cake() # 运用[自创果子术]制作果子
🥝 mro顺序
类名.__mro__
mro:方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序。
用来看类的层级关系
mro采用C3 线性化算法,遵循以下规则:
- 子类永远在父类前面
- 如果有多个父类,会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'def make_cake(self):print(f'运用{self.skill}制作果子')# 学校类School
class School(object):def __init__(self):self.skill='[黑马果子]'def make_cake(self):print(f'运用{self.skill}制作果子')# 徒弟类Prentice
class Prentice(School,Master):def __init__(self):self.skill = '[自创果子术]'def make_cake(self):print(f'运用{self.skill}制作果子')result = Prentice()
print(result.skill) # [自创果子术]
result.make_cake() # 运用[自创果子术]制作果子print(Prentice.__mro__)
类继承的顺序:
(<class '__main__.Prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)
🥝 子类调用父类的同名方法和属性
顾客也希望吃徒弟用师父和学校技术做的果子。
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'def make_cake(self):print(f'运用{self.skill}制作果子')# 学校类School
class School(object):def __init__(self):self.skill='[黑马果子]'def make_cake(self):print(f'运用{self.skill}制作果子')# 徒弟类Prentice
class Prentice(School,Master):def __init__(self):self.skill = '[自创果子术]'def make_cake(self):# 先调用自己子类的初始化,if先调用父类的属性和方法,父类会覆盖子类属性。self.__init__()print(f'运用{self.skill}制作果子')# 调用父类方法# 为保证调用到的也是父类的属性,必须在调用前调用父类的初始化def make_master_cake(self):Master.__init__(self)Master.make_cake(self)def make_school_cake(self):School.__init__(self)School.make_cake(self)result = Prentice()
print(result.skill) # [自创果子术]
result.make_cake() # 运用[自创果子术]制作果子
result.make_master_cake() # 运用[煎饼果子配方]制作果子
result.make_school_cake() # 运用[黑马果子]制作果子
🥝 多层继承
师父把某果子技能给徒弟,徒弟把所有果子技能给徒孙。
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'def make_cake(self):print(f'运用{self.skill}制作果子')# 学校类School
class School(object):def __init__(self):self.skill='[黑马果子]'def make_cake(self):print(f'运用{self.skill}制作果子')# 徒弟类Prentice
class Prentice(School,Master):def __init__(self):self.skill = '[自创果子术]'def make_cake(self):self.__init__()print(f'运用{self.skill}制作果子')def make_master_cake(self):Master.__init__(self)Master.make_cake(self)def make_school_cake(self):School.__init__(self)School.make_cake(self)# 徒孙类
class Tusun(Prentice):passresult =Tusun()
result.make_cake() # 运用[自创果子术]制作果子
result.make_master_cake() # 运用[煎饼果子配方]制作果子
result.make_school_cake() # 运用[黑马果子]制作果子
🥝 super()调用父类方法
使用super()可以自动查找父类。调用顺序遵循__mro__
类属性的顺序。比较适合单继承使用。
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'def make_cake(self):print(f'运用{self.skill}制作果子')# 法二super(Master,self).__init__()super(Master,self).make_cake()# 学校类School
class School(object):def __init__(self):self.skill='[黑马果子]'def make_cake(self):print(f'运用{self.skill}制作果子')# 徒弟类Prentice
class Prentice(Master,School):def __init__(self):self.skill = '[自创果子术]'def make_cake(self):self.__init__()print(f'运用{self.skill}制作果子')def make_master_cake(self):Master.__init__(self)Master.make_cake(self)def make_school_cake(self):School.__init__(self)School.make_cake(self)# 一次性调用父类的同名属性和方法def make_old_cake(self):'''# 法一:代码冗余;父类类名如果变化,这里代码需要频繁修改Master.__init__(self)Master.make_cake(self)School.__init__(self)School.make_cake(self)'''# 法二:super()super().__init__()super().make_cake()'''也可写作:super(当前类名,self).函数() //不过麻烦super(Prentice,self).__init__()super(Prentice,self).make_cake()'''result=Prentice()
result.make_old_cake()
'''
运用[煎饼果子配方]制作果子
运用[黑马果子]制作果子
'''
🥝 定义私有属性和方法
在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。
设置私有权限的方法:在属性名和方法前面加上两个下划线__
。
师父授之以渔,不受之以鱼。把果子技术给徒弟,不给钱。
私有属性语法:
class 类名():def __init(self):self.__属性名 = 值 # 私有属性def __函数名(self):代码
定义私有属性和方法:
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'self.bill=0 # 公有权限self.__money=200 # 私有属性__# 定义私有方法__def __info_print(self):print(self.skill)print(self.__money)def make_cake(self):print(f'运用{self.skill}制作果子')# 法二super(Master,self).__init__()super(Master,self).make_cake()# 徒弟类Prentice
class Prentice(Master):passresult=Prentice()
print(result.bill) # 0
#print(result.__money)
# 'Prentice' object has no attribute '__money'#result.__info_print()
# 'Prentice' object has no attribute '__info_print'
#即便是Master也无法获取私有属性,同样 has no attribute
🥝 获取和修改私有属性值
在Python中,一般定义函数名
get_××
来获取私有属性,定义set_××
来修改私有属性。//get、set只是习惯,非强制。
# 师父类Master
class Master(object):def __init__(self):self.skill ='[煎饼果子配方]'self.__money=200 # 私有属性__# 获取私有属性def get_money(self):return self.__money#修改私有属性def set_money(self):self.__money= 500# 定义私有方法__def __info_print(self):print(self.skill)print(self.__money)def make_cake(self):print(f'运用{self.skill}制作果子')# 法二super(Master,self).__init__()super(Master,self).make_cake()# 徒弟类Prentice
class Prentice(Master):passresult=Prentice()
print(result.get_money()) # 200result.set_money()
print(result.get_money()) # 500
5. 多态 类属性 类方法
🥝 了解多态
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。
定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法可以产生不同的执行结果。
好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化.
实现步骤:
- 定义父类,并提供公共方法
- 定义子类,并重写父类方法
- 传递子类对象给调用者,可以看到不同子类执行效果不同
🥝 多态
# 多态
# 警务人员和警犬(ad,dd)一起工作
class Dog(object):def work(self): # 父类提供的统一方法,哪怕是空方法(pass)passclass ArmyDog(Dog): # 继承Dog类def work(self): # 子类重写父类同名方法print("追击敌人...")class DrugDog(Dog):def work(self):print("追查毒品...")class Person(object):def work_with_dog(self,dog): # 传入不同的对象,执行不同的代码,即不同的work函数dog.work()ad=ArmyDog()
dd=DrugDog()result=Person()
result.work_with_dog(ad) # 追击敌人...
result.work_with_dog(dd) # 追查毒品...
🥝 类属性
类属性:类对象所拥有的属性,它被该类的所有实例对象所拥有。
类属性可以使用类对象和实例对象访问。
记录的某项数据始终保持一致时,则定义类属性。
类属性的优点: 节省内存空间。实例属性要求每个对象为其单独开辟出一份内存空间来记录数据,而类属性为全类所共有,仅占一份内存。
设置类属性、访问类属性、修改类属性
属性只能通过类对象修改,不能通过实例对象修改。如果通过实例对象修改类属性,表示的是创建了一个实例属性。
# 定义类、类属性
class Dog(object):tooth = 10
# 创建对象
black = Dog()
white = Dog()
# 访问类属性
print(Dog.tooth) #10
print(black.tooth) #10
print(white.tooth) #10
# 修改类属性
# 类属性只能通过类对象修改,不能通过实例对象修改。
Dog.tooth = 12
print(Dog.tooth) #12
print(black.tooth) #12
print(white.tooth) #12
# 如果通过实例对象修改类属性,表示的是创建了一个实例属性。
black.tooth = 13
print(Dog.tooth) #12
print(black.tooth) #13
print(white.tooth) #12
# 类属性没有被修改成功,如果改成功了牙齿数应该都是13
🥝 类方法
类方法特点:需要用装饰器 @classmethod
来标识其为类方法。对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数。
当方法中需要使用类对象(如访问私有类属性等)时,定义类方法。
类方法一般和类属性配合使用
@classmethod
def ××():代码
class Dog(object):__tooth = 10 # 当方法中需要使用类对象(如访问私有类属性等)时,定义类方法。@classmethod # 用装饰器@classmethod来标识其为类方法def get_tooth(cls): # 对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数return cls.__toothblack = Dog()
result = black.get_tooth()
print(result) #10
🥝 静态方法
静态方法特点:
- 需要通过装饰器
@staticmethod
来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)
取消不需要的参数传递,有利于减少不必要的内存占用和性能消耗
2.静态方法也能通过实例对象和类对象去访问
@staticmethod
def ××():代码
class Dog(object):@staticmethoddef info_print():print("这是一个静态对象")
# 创建对象
black=Dog()
# 调用静态方法
# 通过实例对象访问
black.info_print() # 这是一个静态对象
# 通过类对象访问
Dog.info_print() # 这是一个静态对象
6. 异常
异常:当检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误❌的提示。
🥝了解异常
unnormal
#程序调试及异常处理
"""
try-except, except 除……之外
就是用一个try except然后它才可以继续执行下面的内容,如果不这样会报错中止程序运行
"""
##-----ZeroDivisionError 除数不为0 ---
def division(x,y):try:return x/yexcept ZeroDivisionError:print('除数不可为0')print(division(2,0))
#----try except else 个人认为用不用else没啥影响,用反而麻烦
def division(x,y):try:ans = x/yexcept ZeroDivisionError:print('除数不可为0')else:return ansprint(division(2,9))
##-----TypeError 数据类型不符 ---
def division(x,y):try:return x/yexcept TypeError:print('TypeError')print(division('a',0))#-----------
def division(x,y):try:return x/yexcept Exception: #Exception一般错误皆可使用print('Exception')print(division('a',0))"""
常见的异常对象:
1. AttributeError 对象没有这个属性
2. Exception 一般错误皆可使用,通用错误发生
3. FileNotFoundError 找不到打开的文档
4. IOError 在输入或输出时发生错误
5. IndexError 索引超出范围区间 MemoryError 需求存储空间超出范围
6. KeyError 在映射中没有这个键 ValueError 输入无效参数
7. TypeError 数据类型错误 SyntaxError 语法错误
8. NameError 对象名未申明
9. ZeroDivisionError 除数为0
10. SystemError 直译器的系统错误
"""
# ----------try except finally-----
def division(x,y):try:return x/yexcept ZeroDivisionError:print('Error')finally:print('阶段任务完成---')print(division(4,2))
print(division(5,0))
print(division(3,1))
以r方式打开一个不存在的文件:
open('test.txt','r')
# FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'
异常的语法:
try:可能发生错误的代码
except:如果出现异常执行的代码
else:没有异常执行的代码
finally:无论是否异常都要执行的代码
一般try下方只放一行尝试执行的代码。
尝试以r
模式打开文件,如果文件不存在,则以w
方式打开.
try:f=open('test.txt','r')
except:f=open('test.txt','w')
🥝了解异常类型
如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常。
×××Error(异常类型):***(异常信息)
# NameError
# print(num) # NameError: name 'num' is not defined# ZeroDivisionError
print(1/0) # ZeroDivisionError: division by zero
🥝 捕获异常
捕获指定异常类型
try:print(num) # NameError
except NameError:print('有错误') # 有错误try:print(1/0) # ZeroDivisionError
except NameError:print('有错误') # ZeroDivisionError: division by zero
捕获多个指定异常
当捕获多个异常时,可以把要捕获的异常类型的名字,放到except后,并使用元组的方式进行书写。
try:print(1/0) # ZeroDivisionError
except (NameError,ZeroDivisionError):print('有错误') # 有错误
捕获异常描述信息
try:print(1/0) # ZeroDivisionError
except (NameError,ZeroDivisionError) as result:print(result) # division by zero
捕获所有异常
Exception是所有重写异常类的父类。
try:print(1/0) # ZeroDivisionError
except Exception as result:print(result) # division by zero
🥝 异常的else
try:print(1) #1
except Exception as result:print(result) # division by zero
else:print('else后是没有异常时执行的代码') #else后是没有异常时执行的代码
🥝 异常的finally
finally表示的是无论是否异常都要执行的代码,例如关闭文件。
try:f=open('test.txt','r')
except Exception as result:f=open('test.txt','w')
else:print('没有异常')
finally:f.close()
🥝 异常的传递
命令提示符运行py文件
文件资源管理器中找到文件路径,输入cmd,回车
python3 文件名
文件名可以输一部分,然后用tab自动补全。
异常传递
从外层的try-except传到内层的try-except
需求︰
1.尝试只读方式打开test.txt文件,如果文件存在则读取文件内容,文件不存在则提示用户即可。
⒉读取内容要求︰尝试循环读取内容,读取过程中如果检测到用户意外终止程序,则except捕获异常并提示用户。
test.txt如下所示:
asads
sdsd
asadsds
aas
ass
as
import time
try:f=open('test.txt')try:while True:content = f.readline()if len(content) == 0:breaktime.sleep(2)print(content)except:print('意外终止了读取数据')# 如果再读取文件过程中,产生了异常那,那么就会捕捉到# 比如 按下ctrl+c(cmd里)finally:f.close()print('关闭文件')
except:print('该文件不存在')
🥝 自定义异常
自定义异常作用:用来将不满足程序逻辑的情况进行反馈。
在Python中,抛出自定义异常的语法为raise 异常类对象
。
# 自定义异常类
class 异常类类名(Exception):代码# 设置抛出异常的描述信息def __str__(self):return ...
# 抛出异常
raise 异常类名
# 捕获异常
except Exception...
需求︰密码长度不足,则报异常(用户输入密码,如果输入的长度不足3位,则报错,即抛出自定义异常,并捕获该异常)。
# 自定义异常类,继承Exception
class ShortInputError(Exception):def __init__(self,length,min_len):self.length = lengthself.min_len =min_len# 设置抛出异常的描述信息def __str__(self):return f'你输入的长度是{self.length},要求不少于{self.min_len}个字符'def main():try:password=input('请输入密码:')if len(password)<3:raise ShortInputError(len(password),3)except Exception as result:print(result)else:print('密码已经输入完成')main()
"""
请输入密码:1
你输入的长度是1,要求不少于3个字符请输入密码:1a3
密码已经输入完成
"""
7. 模块和包
🥝了解模块
Python模块(Module),是一个Python 文件,以.py 结尾,包含了Python对象定义和Python语句。模块能定义函数,类和变量,模块里也能包含可执行的代码。
在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。
模块的作用:降低工作量,常用功能封装成某个模块,使用时只需要调用模块的功能,就可以完成某些需求。
🥝 导入模块
import 模块名
from 模块名 import 功能名
(可以多个功能用逗号隔开)from 模块名 import *
(*指代所有功能)
as定义别名:(只能用别名,原来的名字不能用,会报错)
import 模块名 as 别名
(模块定义别名)from 模块名 import 功能名 as 别名
(功能定义别名)
可以import 模块名,模块名...
但不建议这样写,推荐用多行import来导入模块。更多的是出于操作便利性的原因,与代码本身无关。
# 导入模块
import 模块名
# 调用功能
模块名.功能名()
import math
print(math.sqrt(9)) #3.0#from math import sqrt
#print(sqrt(9)) #3.0#from math import *
#print(sqrt(9)) #3.0
"""
import time as tt# 等待2s然后执行打印hello
tt.sleep(2)
print('hello')
"""from time import sleep as sl
sl(2)
print('hello')
🥝 制作模块
自定义模块名必须要符合标识符命名规则。
步骤:
- 定义模块
- 测试模块
- 调用模块
新建Python文件(my_module.py),命名并定义功能函数:
# 定义模块(模块名满足标识符规则)
def test(a,b):print(a+b)# 测试模块
# test(1,5) #6
# 测试信息保留且不被其他文件调用执行,只在当前文件中调用
# print(__name__) # __main__
# 如果上面这句不被注释,而被其他文件调用,结果就不是__main__了,而是my_module
# __name__是系统变量,是模块的标识符,值是:如果是自身模块是__main__,否则是当前模块的名字
if __name__ == '__main__':test(1,5) #6
除my_module.py什么名字都可:
import my_module
# 调用模块
my_module.test(3,4) # 7
🥝 模块的定位顺序
由近及远
当导入一个模块,Python解析器对模块位置的搜索顺序是∶
1.当前目录
2.如果不在当前目录,Python则搜索在shell变量PYTHONPATH下的每个目录。(解释器目录)
3.如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/pythonl
模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
自己的文件名(当前目录)不要和已有模块名(在解释器目录)重复,否则导致模块功能无法使用。//优先用当前目录
使用from 模块名 import功能
的时候,如果功能名字重复,调用到的是最后定义或导入的功能。
# 使用 from 模块名 import 功能的时候,如果功能名字重复,调用到的是最后定义或导入的功能
from time import sleep
sleep(2) # 隔2s运行结束
def sleep():print('自定义的sleep')
# sleep(2) # TypeError: sleep() takes 0 positional arguments but 1 was given
# 使用 import 模块名 不用担心功能名重复
名字重复的严重性
import time
print(time) #<module 'time' (built-in)>
# 变量和模块名相同,如果变量在后,会覆盖模块功能
time = 1
print(time) #1
# Q:为什么变量也能覆盖模块?
# A:在Python语言中,数据是通过引用传递的
🥝 all列表
如果一个模块文件中有__all__
变量,当使用from xxx import *
导入时,只能导入这个列表中的元素。
my_module1.py
__all__=['testA']def testA():print('testA')def testB():print('testB')
from my_module1 import *testA() #testA
#testB() #NameError: name 'testB' is not defined
🥝 了解、制作包
包将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为 __init__.py
文件,那么这个文件夹就称之为包。
[New] →[ Python Package] → 输入包名 → [OK] → 新建功能模块(有联系的模块)。
注意:新建包后,包内部会自动创建__init__.py
文件,这个文件控制着包的导入行为。
新建包内模块
my_module1.py
print(1)
def info_print1():print('my_module1')
my_module2.py
print(2)
def info_print2():print('my_module2')
导入、调用包
# 法一
import 包名.模块名
# 调用
包名.模块名.目标# 法二:在__init__.py文件中添加__all__ =[],控制允许导入的模块或功能列表。
from 包名 import *
# 调用
模块名.目标
import mypackage.my_module1mypackage.my_module1.info_print1()
'''
1
my_module1
'''
from mypackage import *my_module1.info_print1()
# 如果__init__中没有设置all列表,会不能导入任何一个模块,
# NameError: name 'my_module1' is not defined
# 在__init__.py文件中添加__all__ =['my_module1'],上一句正常运行,结果如下,my_module2不能导
'''
1
my_module1
'''
my_module2.info_print2() #NameError: name 'my_module2' is not defined
8. 学员管理系统
🥝 系统需求
使用面向对象编程思想完成学员管理系统的开发,具体如下:
系统要求:学员数据存储在文件中。
系统功能:添加学员、删除学员、修改学员信息、查询学员信息、显示所有学员信息、保存学员信息及退出系统等功能。
🥝 需求分析
角色分析
- 学员
- 管理系统
工作中注意事项
1.为了方便维护代码,一般一个角色一个程序文件;
2.项目要有主程序入口,习惯为main.py
🥝 创建程序文件
student.data是main.py运行后生成的
student.py
# 学员文件
class Student(object):def __init__(self,name,gender,tel):self.name = nameself.gender = genderself.tel = teldef __str__(self):return f'{self.name},{self.gender},{self.tel}'#aa=Student('aa','women',111)
#print(aa) #aa,women,111
managerSystem.py
from student import *
# 管理系统文件
class StudentManager(object):def __init__(self):# 存储数据所用的列表self.student_list = []# 程序入口函数,启动程序后执行的函数def run(self):# 加载学员数据self.load_student()while True:# 显示功能菜单self.show_menu()# 用户输入功能序号menu_num =int(input('输入你要的功能序号:'))# 根据输入的功能序号执行相应功能if menu_num ==1:self.add_student() # 添加学员elif menu_num ==2:self.del_student() # 删除学员elif menu_num ==3:self.modify_student() # 修改学员信息elif menu_num ==4:self.search_student() # 查询学员elif menu_num ==5:self.show_student() # 显示所有学员信息elif menu_num ==6:self.save_student() # 保存学员信息elif menu_num ==7:break # 退出系统# 系统功能函数#---显示功能---@staticmethoddef show_menu():print('请选择功能:')print('1.添加学员')print('2.删除学员')print('3.修改学员信息')print('4.查询学员信息')print('5.显示所有学员信息')print('6.保存学员信息')print('7.退出系统')#---添加学员---def add_student(self):name=input('输入姓名:')gender=input('输入性别:')tel=input('输入电话:')student=Student(name,gender,tel)self.student_list.append(student)#print(self.student_list) #[<student.Student object at 0x0000025D1A532B08>]print(student) #mao,1,123#---删除学员---def del_student(self):del_name=input('输入删除人的姓名:')for i in self.student_list:if i.name ==del_name:self.student_list.remove(i)breakelse:print('查无此人')#print(self.student_list)#---修改学员信息---def modify_student(self):modify_name=input('输入修改信息学员的姓名:')for i in self.student_list:if i.name== modify_name:i.name=input('请输入学员姓名:')i.gender=input('性别:')i.tel=input('电话:')print(f'修改信息成功,姓名:{i.name},性别:{i.gender},电话:{i.tel}')breakelse:print('查无此人')#---查询学员---def search_student(self):search_name=input('查询的学员姓名是:')for i in self.student_list:if search_name==i.name:print(f'所查之人姓名:{i.name},性别:{i.gender},电话:{i.tel}')breakelse:print('查无此人')#---显示所有学员信息---def show_student(self):print('姓名\t性别\t电话')# 表头for i in self.student_list:print(f'{i.name}\t{i.gender}\t{i.tel}')#---保存学员信息---def save_student(self):f=open('student.data','w')new_list=[i.__dict__ for i in self.student_list]#[{'name':'aa','gender':'man','tel':'111'}]print(new_list)f.write(str(new_list))f.close()# ---加载学员数据---def load_student(self):try:f=open('student.data','r')except:f=open('student.data','w')else:data=f.read()new_list=eval(data)# 数据类型转换,此处字典转成列表类型self.student_list=[Student(i['name'],i['gender'],i['tel']) for i in new_list]finally:f.close()
main.py
# 程序入口文件
from managerSystem import *
if __name__=='__main__':student_manager=StudentManager()student_manager.run()
🥝 __dict__
__dict__
作用:搜集类对象或实例对象的属性和方法以及对应的值,从而返回一个字典。
class A(object):a=0def __init__(self):self.b=1aa=A()
#print(A.__dict__)
"""
{'__module__': '__main__', 'a': 0, '__init__': <function A.__init__ at 0x000001F5FB18A798>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
"""
print(aa.__dict__) #{'b': 1}
🥝 补:“
在Lightly平台中二次运行会出现:
SyntaxError: invalid syntax
把student.data
文件删除之后再次运行就正常。
https://0efa42f73e-share.lightly.teamcode.com
Lightly(Q登的)
SyntaxError: invalid syntax的可能原因:
- 不适合的 Python 版本:python2、3版本兼容问题。
- 路径问题。
- 拼写、缩进、语法错误。
- 退出 Python 解释器,返回到命令行安装第三方模块。
- 在 Jupyter Notebook 中,直接使用 pip install 也可能导致 SyntaxError,应使用 ! 调用系统命令。