面向对象的三大特征:封装,继承,多态
一、封装补充
property装饰器
装饰器:不修改调用方式和源代码基础上,修改或者增加源代码功能
@property 这个内置装饰器,在方法上面装饰就自动加入到property()函数里面
class People:
def __init__(self, name, age, height, face):
self.name = name
self.age = age
self.height = height
# __代表隐藏属性,外部不能直接范围
self.__face = face
# 我们要统一一个属性调用的规范,我们注意到属性名好像和初始的名字不一样
# 我们为了进一步和属性一致
@property # 这里就是区分出属性和方法
def face(self):
# 代表getface,以后看到@property+return就是代表getface
return self.__face
# 后面的装饰就要用到@property的函数名+setter或者delter进行装饰
# setter setface的意思,是固定的语法,不能换名字
@face.setter
# 这里的名字必须和@property装饰的函数一致,不能是其他的
# 修改数值,肯定需要数值进行覆盖修改
def face(self, face):
self.__face = face
@face.deleter # 删除属性
# 这里的名字必须和@property装饰的函数一致,不能是其他的
def face(self):
# 代码块部分就是代表整个方法
del self.__face
ziliang = People("梓良", 18, 180, "帅")
print(ziliang.face)
ziliang.face = "丑"
print(ziliang.face)
# 通过动作形式自动分配到对应的方法运行
del ziliang.face
print(ziliang.face) # 这里会报错,因为删除了
我们不难发现,这样操作仿佛和公共属性没有区别
但是
使用@property装饰器和直接定义公共属性之间确实有一定的区别,主要体现在以下几个方面:
1.访问控制:使用@property装饰器可以实现对属性的访问控制,通过定义getter和setter方法可以对属性进行额外
的逻辑处理。而直接定义的公共属性在外部可以直接访问和修改,无法实现访问控制。
2.属性计算:通过@property装饰器可以实现属性值的计算,getter方法可以根据需要进行计算或逻辑判断。而直
接定义的公共属性的值是固定的,无法实现属性值的动态计算。
3.属性验证:@property装饰器的setter方法可以对属性值进行验证,确保符合特定条件。直接定义的公共属性无
法实现对属性值的验证。
4.属性更新:使用@property装饰器可以在setter方法中实现属性值的更新逻辑,例如更新其他相关属性或执行其
他操作。直接定义的公共属性赋值时无法触发额外的更新逻辑。
5.代码规范性:@property装饰器可以使代码更加规范和易读,将属性的访问、设置和删除操作统一在一个接口
中。这有助于提高代码的可维护性和可读性。
6.因此,虽然@property装饰器和公共属性都可以用来定义类的属性,但@property装饰器提供了更多的功能和
灵活性,可以实现更多属性相关的控制和逻辑处理,而不仅仅是简单的属性存取。
二、继承
1.定义
其实和生物学上的继承一样的道理,子类继承父类的全部属性和方法,除隐藏属性不能继承。单继承就是单亲基础,父类自我繁殖了一个子类
子类copy父类代码的意思,但是子类也可以基因突变,也可以自我重构父类的方法和数学,变成和父类不一样
继承可以解决类与类之间代码冗余
记住python创建的所有类都有一个祖宗:object基类
在Python中,类的继承通过在类定义时在类名后面的括号中指定父类来实现。
如果一个类没有指定父类,则默认继承自object类,
即class ClassName:相当于class ClassName(object):。
在python3都是自动默认继承基类,所以可以省略不写
2.单继承
一个子类继承一个父类
隐藏属性,隐藏方法不能继承
class GrandFather:
def __init__(self):
self.money = 1000000
# 代码私房钱
self.__privateMoney = 100000
def house(self):
print(f"北京四合院一套")
def setInfo(self):
# f-String 格式化输出
print(f"爷爷的遗产:{self.money}")
# 虽然隐藏属性是不能继承的,但是通过公开的方法去获取隐藏属性是可以的
# 隐藏方法:__方法名
# 有些方法你不想直接给外面,你可以隐藏起来
def __setPrivateMoney(self):
return self.__privateMoney
def func(self):
# 通过公开方法调用隐藏方法
# 函数调用代表:执行函数的代码
return self.__setPrivateMoney()
class Father(GrandFather):#指定父类是GrandFather
def __init__(self):
super(Father, self).__init__()#确保父类的初始化操作也被执行
#通过super()函数调用父类的方法时,
#并不需要实例化父类对象,而是通过特殊的代理对象来实现方法的调用和方法解析。
#这种机制使得Python的多重继承更加灵活和方便,同时避免了创建多余的父类实例。
self.__privateMoney = 10000
class Son(Father):
# 第一个初始化是son的属性
def __init__(self):
# 继承父类
# 初始化父类Father的属性
super(Son, self).__init__()
self.__privateMoney = -1000
def setInfo(self):
# 这里需要传入你son 的对象
# 这里如果你调用是父类或者祖宗类,因为不是从父类创建或者祖宗类创建的对象,父类的self是没有的
# 你需要手动把你的对象传入进去
# 函数+参数传递,基础课的传递
Father.setInfo(self)
def setHouse(self):
Father.house(self)
son1 = Son()
son1.setInfo()
son1.setHouse()
print(son1.func())
补充:super关键字的作用
①作用
super()
是一个内置函数,用于调用父类的方法。在Python中,当子类继承父类时,可以使用 super()
来调用父类的方法,以便在子类中扩展或重写这些方法而不破坏继承关系。
通过 super()
可以实现以下几点作用:
- 在子类中调用父类的方法:通过
super().method_name()
可以在子类中调用父类的方法。 - 支持多重继承:
super()
确保方法按照继承顺序(MRO,Method Resolution Order)依次调用,避免了钻石继承等多重继承带来的问题。 - 简化代码:使用
super()
可以使代码更加清晰和简洁,避免直接指定父类名称。
下面是一个简单的示例,演示了在子类中使用 super()
调用父类方法的情况:
class Parent:
def show(self):
print("Parent method")
class Child(Parent):
def show(self):
super().show() # 调用父类的方法
print("Child method")
child = Child()
child.show()
在这个示例中,子类 Child
中的 show()
方法通过 super().show()
调用了父类 Parent
中的 show()
方法,从而保留了父类方法的功能并在其基础上进行了扩展。
②参数
在 super()
函数中,参数用于指定子类和实例,以便在多重继承的情况下正确地调用父类的方法。super()
函数的语法如下:
super([type[, object-or-type]])
其中参数的作用如下:
type
:用于指定当前类的类型。如果在实例方法中使用super()
,通常可以省略这个参数,Python会自动获取当前方法所在的类。如果在类方法或静态方法中使用super()
,则需要显式指定当前类的类型。object-or-type
:用于指定当前实例或类。如果在实例方法中使用super()
,通常可以省略这个参数,Python会自动获取当前方法所在的实例。如果在类方法中使用super()
,则需要传入当前类的类型。
在绝大多数情况下,我们可以在实例方法中使用 super()
而无需显式传入参数,因为Python会自动获取当前类和实例。例如:
class Parent:
def __init__(self):
print("Parent init")
class Child(Parent):
def __init__(self):
super().__init() # 自动获取当前类和实例
print("Child init")
child = Child()
在这个例子中,super().__init()
中的 super()
自动获取了当前类 Child
和实例 child
,并调用了父类 Parent
的 __init__
方法。
如果需要在类方法或静态方法中使用 super()
,则需要显式传入当前类的类型和实例。例如:
class MyClass:
@classmethod
def do_something(cls):
super(MyClass, cls).do_something_static()
@staticmethod
def do_something_static():
print("Doing something static")
MyClass.do_something()
在这个例子中,在类方法 do_something
中,我们显式传入了当前类 MyClass
和当前类的类型 cls
,以确保正确地调用父类的方法。
3.重写父类中的属性与方法
class Father:
def __init__(self, height, eye,age):
self.height = height
self.eye = eye
# 年龄是不能继承
self.__age = age
def Money(self):
print("老爸工资1w")
# 括号里面是你想要继承的父类
# 子类可以修改父类的某些属性
class Son(Father):
def __init__(self, height, eye, name):
# 这里需要用到一个super的函数进行初始化父类的属性,代表你
# 拿到了属性可以进行覆盖修改
# 如果没有super,就无法修改里面的数值
# 没有super只有只读权限,没有修改权限,用了super就变成
#超级管理员,你可以修改上面的属性数值了
super(Son, self).__init__(height, eye,age)
#调用super(son, self).__init__(height, eye, age)时,
#传入age参数是因为Father类的__init__方法需要height、
#eye和age这三个参数来正确初始化父类的属性。即使子类不直
#接访问父类的属性,但是在构造子类对象时,需要确保父类的
#初始化工作完成,以保证整个继承链的正确性。
self.name = name
def Money(self):
print("你们的工资1.5k, 老爸发的")
father1 = Father("170", "黑色")
son1 = Son("180", "美瞳白色", "xxx1")
print(father1.height, father1.eye)
print(son1.height, son1.eye)
father1.Money()
son1.Money()
4.多层继承
多层继承就是还有爷爷
class GrandFather:
def __init__(self):
self.money = 1000000
# 代码私房钱
self.__privateMoney = 100000
def house(self):
print(f"北京四合院一套")
def setInfo(self):
# f-String 格式化输出
print(f"爷爷的遗产:{self.money}")
# 虽然隐藏属性是不能继承的,但是通过公开的方法去获取隐藏属性是可以的
# 隐藏方法:__方法名
# 有些方法你不想直接给外面,你可以隐藏起来
def __setPrivateMoney(self):
return self.__privateMoney
def func(self):
# 通过公开方法调用隐藏方法
# 函数调用代表:执行函数的代码
self.__setPrivateMoney()
class Father(GrandFather):
def __init__(self):
super(Father, self).__init__()
self.__privateMoney = 10000
class Son(Father):
# 第一个初始化是son的属性
def __init__(self):
# 继承父类
# 初始化父类Father的属性
super(Son, self).__init__()
self.__privateMoney = -1000
def setInfo(self):
# 这里需要传入你son 的对象
# 这里如果你调用是父类或者祖宗类,因为不是从父类创建或
#者祖宗类创建的对象,父类的self是没有的
# 你需要手动把你的对象传入进去
# 函数+参数传递,基础课的传递
Father.setInfo(self)
def setHouse(self):
Father.house(self)
son1 = Son()
son1.setInfo()
son1.setHouse()
5.多继承
就是一个儿子有很多个老爸
吕布
# 多继承
class DingYuan:
def __init__(self):
self.fatherName = "丁原义父"
def setOffices(self):
print("升官为主簿")
class DongZhuo:
def __init__(self):
self.fatherName = "董卓义父"
def setOffices(self):
print("送赤兔马")
class WangYun:
def __init__(self):
self.fatherName = "王允义父"
def setOffice(self):
print("送貂蝉")
#
class LvBu( DingYuan, WangYun , DongZhuo):
def __init__(self):
super(LvBu, self).__init__()
def setOffices(self):
WangYun.setOffice(self)
DongZhuo.setOffices(self)
DingYuan.setOffices(self)
lvbu = LvBu()
lvbu.setOffices()
# mro就是看继承的顺序
print(LvBu.__mro__)
print(DongZhuo.__mro__)
#在Python中,类的继承顺序是按照从左到右的顺序来决定的。
#这种继承方式称为广度优先,即首先查找当前类,
#然后按照从左到右的顺序逐级向上查找父类,直到找到为止。
# 只看父类顺序
print(LvBu.__bases__)
# 同一个属性名遵守就近原则
print(lvbu.fatherName)
三、多态
一个对象的多种状态
不同的对象,调用同一个方法,表现出不同形式
多态的实现遵守两个原则
1.必须要有类的继承 2.子类必须重写父类的方法
class Money:
def ticke(self):
print("车票单价")
class Car(Money):
def ticke(self):
print("票价是200元")
class Bus(Money):
def ticke(self):
print("票价150元")
class Train(Money):
def ticke(self):
print("票价120元")
c = Car()
b = Bus()
t = Train()
c.ticke()
b.ticke()
t.ticke()
也就是把方法类化