1.封装装饰器的写法
""" 书写封装代码的时候 关于装饰器的细节 1.通常情况下 我们先写@property 然后再写@xxx.setter(不然的话xxx会找不到报错) 2.setter装饰器前边的名字是根据@property装饰器对应的函数名命名的 """ class Test: @property def name(self): return "get_name" @name.setter def name(self,name): self.__name = name print(name) pass t1=Test() print(t1.name) t1.name="test" print(t1.name)
2.对象的创建过程
创建对象:
obj = 类名()对象创建分为2 大阶段:分配空间 → 初始化,对应两个魔法方法:
__new__:创建、分配内存,生成空对象
__init__:给空对象初始化属性顺序:先
__new__→ 再__init__分步详解
第一步:调用
__new__(cls, *args, **kwargs)
第一个参数
cls:当前类作用:开辟一块内存空间,创建并返回一个空的实例对象
返回值:必须返回创建出来的对象
super().__new__(cls)如果
__new__没有返回对象,那么__init__不会执行!
__new__是真正造对象的工厂方法。第二步:调用
__init__(self, *args, **kwargs)
self就是__new__返回过来的那个空对象作用:给对象添加实例属性,做初始化赋值
__init__没有返回值,只能 return None
""" 对象的创建过程 创建对象对比 java写法:类名 对象名 = new 类名() 会通过构造方法创建对象 python写法:对象名 = 类名() Python创建对象的过程 1.访问__new__函数 创建对象 __new__也是魔法函数 每个类默认自带的(继承自object) 这个函数只负责创建对象,最终由祖宗类object来执行,因为在Python中,只有object有权限访问底层c语言的代码 在堆内存开辟空间 创建对象 此时创建没有任何属性的对象 返回回来 传递给__init__函数 2.__init__函数接收__new__函数传入的对象 对此对象添加属性 并且赋予用户传入的值 3.完成对象创建 得到一个有具体属性和属性值的对象 """ class Person: def __new__(cls, *args, **kwargs): print("__new__函数执行了") return super().__new__(cls) def __init__(self, name, age): print("__init__函数执行了") self.name = name self.age = age pass p1=Person("赵四",20) 3. 继承概念引入
""" 编写两个类 狗狗类 属性:名字 亲密值 健康值 品种 方法:介绍狗狗信息 企鹅类 属性:名字 亲密值 健康值 性别 方法:介绍企鹅信息 当前模块中编写了两个类 这两类中有相同的属性 和 相似度非常高的方法 这些如果每次都单独写一遍 是非常啰嗦的 冗余的代码 所以 我们应该考虑 只书写一遍 然后重复利用 使用继承优化 """ class Dog: def __init__(self, name, love, health, strain): self.name = name self.love = love self.health = health self.strain = strain def print_info(self): print(f"狗狗的名字是{self.name}") print(f"狗狗的亲密值是{self.love}") print(f"狗狗的健康值是{self.health}") print(f"狗狗的品种是{self.strain}") pass class Penguin: def __init__(self, name, love, health, sex): self.name = name self.love = love self.health = health self.sex = sex def print_info(self): print(f"企鹅的名字是的{self.name}") print(f"企鹅的亲密值是的{self.love}") print(f"企鹅的健康值是的{self.health}") print(f"企鹅的性别是的{self.sex}") 4.使用继承优化宠物系统
""" 继承的核心思想:子类与父类是is-a的关系 即子类是父类 (属于描述 也属于约束) 表示子类必须属于父类描述的这一大范围 才可以使用继承 使用继承优化宠物系统 宠物类 将各个重复类中 重复的 共同的信息 编写到父类中、 子类 :书写各个子类独有的信息 狗狗类 属性:名字 亲密值 健康值 品种 方法:介绍狗狗信息 企鹅类 属性:名字 亲密值 健康值 性别 方法:介绍企鹅信息 创建子类对象 并不会创建父类对象(注意!!!) """ class Pet: def __int__(self, name, love, health): self.name = name # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 self.love = love # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 self.health = health # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 def print_info(self): print(f"宠物的名字是{self.name}") print(f"宠物的亲密值是{self.love}") print(f"宠物的健康值是{self.health}") pass class Dog(Pet): def __init__(self,strain,name,love,health): super().__int__(name,love,health) self.strain = strain pass class Penguin(Pet): def __init__(self, sex , name, love, health): self.sex = sex super().__int__(name,love,health) # Java 中的写法 super(name,health,love) pass d1 = Dog("金毛", "大黄" , 100 , 100) d1.print_info() print("---------------------------------------------------------") p1 = Penguin("雌性" , "大白" , 100 , 100) p1.print_info()4.1 什么是继承
继承:让一个类(子类 / 派生类)去获取另一个类(父类 / 基类)的属性和方法,实现代码复用,不用重复写相同代码。
父类:被继承的类 子类:继承别人的类
语法:
class 父类名: # 父类代码 class 子类名(父类名): # 子类代码
5. 方法重写
如果子类的方法名和父类一样,子类优先执行自己的方法,覆盖父类。
class Animal: def eat(self): print("动物吃东西") class Dog(Animal): # 重写父类eat def eat(self): print("狗狗吃骨头") dog = Dog() dog.eat() # 输出:狗狗吃骨头""" 之前通过子类调用父类中的 print_info()方法 只能打印所有宠物共有的三个信息 所以我们可以在子类中再编写新的方法 打印独有的信息 并且将父类中的打印方法 复用 实现组合的效果 现在我们在子类中编写了新的方法 实现了组合效果 但是每个子类都有一个不同的名字 这样随着后续宠物类的增多 将会导致程序结构混乱 我们应该使用一个统一的名字 不管任何宠物打印信息 都使用一个 程序结构更加统一 方便调用 """ class Pet: def __init__(self, name, love, health): self.name = name # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 self.love = love # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 self.health = health # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 def print_info(self): print(f"宠物的名字是{self.name}") print(f"宠物的亲密值是{self.love}") print(f"宠物的健康值是{self.health}") pass class Dog(Pet): def __init__(self,strain,name,love,health): super().__init__(name,love,health) self.strain = strain def print_dog(self): super().print_info() print(f"狗狗的品种是{self.strain}") pass class Penguin(Pet): def __init__(self, sex , name, love, health): self.sex = sex super().__init__(name,love,health) # Java 中的写法 super(name,health,love) def print_penguin(self): print(f"企鹅的性别是:{self.sex}") super().print_info() pass d1 = Dog("金毛", "大黄" , 100 , 100) d1.print_dog() d1.print_info() print("---------------------------------------------------------") p1 = Penguin("雌性" , "大白" , 100 , 100) p1.print_penguin()""" 方法重写 : 子类的方法声明与父类一致 Method Override 子类方法覆盖父类方法 单继承下的MRO机制: 调用某个方法 先从本类(子类)中查找 找到则直接调用 找不到则去直接父类中查找 后续重复这个操作 直到找到为止 """ class Pet: def __init__(self, name, love, health): self.name = name # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 self.love = love # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 self.health = health # 这里的self 指向某个具体的子类对象 当前使用的哪个子类对象 就指向谁 def print_info(self): print(f"宠物的名字是{self.name}") print(f"宠物的亲密值是{self.love}") print(f"宠物的健康值是{self.health}") pass class Dog(Pet): def __init__(self,strain,name,love,health): super().__init__(name,love,health) self.strain = strain def print_info(self): super().print_info() print(f"狗狗的品种是{self.strain}") pass class Penguin(Pet): def __init__(self, sex , name, love, health): self.sex = sex super().__init__(name,love,health) # Java 中的写法 super(name,health,love) def print_info(self): print(f"企鹅的性别是:{self.sex}") super().print_info() pass d1 = Dog("金毛", "大黄" , 100 , 100) d1.print_info() print("---------------------------------------------------------") p1 = Penguin("雌性" , "大白" , 100 , 100) p1.print_info()6. 单继承下的MRO(Method Resolution Order)机制
Method Resolution Order : 方法查找顺序
单继承下的MRO,一层一层往上找
class A: # def test(self): # print("A 类中的test函数")' # def __str__(self): # return "A 类中重写的__str__()函数" pass class B(A): # def test(self): # print("B 类中的test函数") # def __str__(self): # return "B 类中重写的__str__()函数" pass class C(B): # def test(self): # print("C 类中的test函数") # def __str__(self): # return "C 类中重写的__str__()函数" pass c1 = C() print(C.__mro__) # (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>) # c1.test() print(c1.__str__())7.多继承下的MRO机制
多继承:一个子类继承多个父类
多继承下的MRO,Python 会从左往右找再往上,找到就停止
⚠️ 开发中尽量少用复杂多继承,容易出现代码混乱。
""" Python是支持多继承的 一个类直接继承多个父类 (Java是单根继承) 多个类之间使用逗号分割即可 在多继承场景下 方法的MRO机制 如果存在多个并列的父类 那么方法的查找顺序 则从左到右 找到则直接调用 否则继续向上查找 分析object类存在的意义: 面向对象 也可以理解为 所有类对象都面向object 属于此类的子类对象 所有的类默认继承此类 此类表示对象 所以在此类中规定了任何一个对象 应该具备的行为 比如 __del__ __new__ __init__ 等等 """ class Person: def __str__(self): return "Person类中的__str__()函数" pass class YellowPerson(Person): def __str__(self): return "YellowPerson类中的__str__()函数" pass class Chinese(Person): def __str__(self): return "Chinese类中的__str__()函数" pass class Student(YellowPerson,Chinese): def __str__(self): return "Student类中的__str__()函数" pass s1 = Student() print(Student.__mro__) print(s1)
8.super () 调用父类方法
重写之后,如果还想保留父类原来的逻辑,用
super()class Animal: def eat(self): print("动物吃东西") class Dog(Animal): def eat(self): super().eat() # 调用父类的eat方法 print("狗狗吃骨头") dog = Dog() dog.eat() #输出: #动物吃东西 #狗狗吃骨头
super () 最常用于
__init__构造函数!
重点:继承时构造方法__init__
⚠️ 如果子类自己写了
__init__,不会自动调用父类的init,必须手动用 super 调用。
class Animal: def __init__(self, name): self.name = name class Dog(Animal): def __init__(self, name, age): # 调用父类构造,把name传给父类 super().__init__(name) self.age = age dog = Dog("旺财", 3) print(dog.name) print(dog.age)9.issubclass () 判断继承关系
print(issubclass(Dog, Animal)) #True Dog是Animal子类
10.多态
10.1 概念
多态:同一个接口(方法名),传入不同的对象,执行不一样的代码。 前提条件(Python 实现多态一般要满足):
要有继承关系
子类重写父类的方法
Python 是鸭子类型,就算没有继承也能实现多态
# 父类 class Animal: def speak(self): pass # 子类1 class Dog(Animal): def speak(self): print("汪汪汪") # 子类2 class Cat(Animal): def speak(self): print("喵喵喵") # 定义一个统一的函数,接收动物对象 def make_sound(animal): animal.speak() # 同一个方法名speak # 传入不同对象,产生不同效果 dog = Dog() cat = Cat() make_sound(dog) make_sound(cat)输出:
汪汪汪 喵喵喵
👉
make_sound函数根本不用关心你是 Dog 还是 Cat,只要对象有speak()方法就行。这就是多态。
10.2多态的好处
1.代码灵活,易扩展:以后新增动物,不用改
make_sound函数代码class Duck(Animal): def speak(self): print("嘎嘎嘎") duck = Duck() make_sound(duck)直接新增子类,原有函数完全不用修改 →开闭原则(对扩展开放,对修改关闭)
降低耦合:调用方和具体的类解绑
10.3 Python 鸭子类型(Python 特色多态,不需要继承!)
“鸭子测试”:看起来像鸭子,走路像鸭子,叫起来像鸭子,那它就是鸭子。 Python 不强制要求继承同一个父类,只要对象拥有同名方法,就可以实现多态。
class Dog: def speak(self): print("汪汪汪") class Cat: def speak(self): print("喵喵喵") class Person: def speak(self): print("你好呀") def make_sound(animal): animal.speak() make_sound(Dog()) make_sound(Cat()) make_sound(Person())三个类没有任何继承关系,但是都有speak(),就可以传入同一个函数。
Java/C++ 必须继承父类才能做多态;Python 不需要,这就是鸭子类型。
10.4 抽象父类(规范子类必须重写方法)
有时候你希望:子类必须重写父类的 speak 方法,不然报错。 这时就要用到
abc模块,创建抽象基类。
from abc import ABC, abstractmethod class Animal(ABC): # 抽象方法:没有实现,强制子类必须重写 @abstractmethod def speak(self): pass class Dog(Animal): def speak(self): print("汪汪汪") # 如果子类没有重写speak,实例化的时候就报错! # class Cat(Animal): # pass # cat = Cat() # TypeError dog = Dog() dog.speak()抽象类不能直接创建对象:
Animal()会报错。
@abstractmethod表示抽象方法 即没有方法体的方法
抽象类作用:制定一套标准、接口,约束子类必须实现某些方法,让多态更规范。
11 多态案例
from abc import ABC, abstractmethod # 支付抽象父类 class Pay(ABC): @abstractmethod def pay_money(self,money): pass # 微信支付 class WeChatPay(Pay): def pay_money(self, money): print(f"微信支付:{money}元") # 支付宝支付 class AliPay(Pay): def pay_money(self, money): print(f"支付宝支付:{money}元") # 统一收银台函数(多态入口) def checkout(pay_obj, money): pay_obj.pay_money(money) checkout(WeChatPay(), 100) checkout(AliPay(), 200)后面新增银联支付,只需要新建一个子类,不用修改
checkout函数。