前言
Python面向对象编程的核心在于封装、继承和多态。封装隐藏细节,继承实现重用,多态提升灵活。本文将深入解析这三大要素,助您掌握面向对象编程的精髓。面向对象基础详解请看上一篇,点这!!!
一、面向对象三要素
继承:
子类继承父类,子类拥有了父类的属性和方法。降低代码的重复性
封装:
将对象的属性和实现细节进行封装,仅对外提供公共的访问方式。提高代码 的安全性;使用私有属性和私有方法实现封装
多态:
不同的对象调用父类相同的方法获取不同的执行结果。多态实现需要:继承、方法重写
二、继承
1.概念
概念:子类继承父类,子类拥有了父类的属性和方法;降低代码的重复性
继承–>派生 子类–>派生类 父类–>基类
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarnings('ignore') import ssl ssl._create_default_https_context = ssl._create_unverified_context
2.简单继承
# 简单继承 class A: def __init__(self): self.money = "一个亿" def car(self): print("一辆黑色的帕拉梅拉") class B(A): pass b = B() print(b.money) # 子类继承了父类,子类对象可以获取父类的属性 b.car() # 子类继承了父类,子类对象可以调用父类的方法
3.单继承
# 单继承:一个子类继承一个父类 class Master: def __init__(self): self.kongfu = "古法煎饼果子配方" def make_cake(self): print(f"使用{self.kongfu}制作出古法煎饼果子") class Cat(Master): pass cat = Cat() print(cat.kongfu) cat.make_cake()
4.多继承
# 多继承:一个子类继承多个父类 class Master: def __init__(self): self.kongfu = "古法煎饼果子配方" self.m_money = "100" def make_cake(self): print(f"使用{self.kongfu}制作出古法煎饼果子") def master_house(self): print("老师傅的茅草屋") class School: def __init__(self): self.kongfu = "现代煎饼果子配方" self.s_money = "1000" def make_cake(self): print(f"使用{self.kongfu}制作出现代煎饼果子") def school_house(self): print("学校的宿舍") class Cat(School,Master): pass cat = Cat() print(cat.kongfu) cat.make_cake() cat.master_house() cat.school_house() print(cat.s_money) # print(cat.m_money) # 多继承的继承顺序 __mro__ # 语法:子类.__mro__ print(Cat.__mro__) - 多继承可以继承多个父类,也继承了所有父类的属性和方法 - 注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性**mro**的顺序来查找) - 多个父类中,不重名的方法,不会有任何影响。
5.单继承的多层继承
# 多继承:一个子类继承多个父类 # 单继承的多层继承: class GrandFather: def __init__(self): self.money = "一个亿" def house(self): print("一栋中式装修风格的大别墅") class Father(GrandFather): def __init__(self): self.money = "两个亿" def house(self): print("一栋欧式装修风格的大别墅") # 获取grandfather的money属性 def get_grand_money(self): # 第一种方式:类名.__init__(self) # 第二种方式:super().__init__() super().__init__() print(f"获取父类的money值为:{self.money}") # 调用grandfather的house方法 def get_grand_house(self): # 第一种方式:类名.方法名() # 第二种方式:super().方法名() super().house() class Son(Father): def __init__(self): self.money = "10" def house(self): print("学校的宿舍") # 子类获取父类的属性 def get_father_money(self): # 第一种方式:类名.__init__(self) # 第二种方式:super().__init__() super().__init__() print(f"获取父类的money值为:{self.money}") # 子类调用父类的方法 def get_father_house(self): # 第一种方式:类名.方法名() # 第二种方式:super().方法名() super().house() son = Son() print(son.money) son.house() son.get_father_money() son.get_father_house() son.get_grand_money() son.get_grand_house()
6.子类重写父类方法
# 子类重写父类同名方法 class Master: def __init__(self): self.kongfu = "古法煎饼果子配方" def make_cake(self): print(f"使用{self.kongfu}制作出古法煎饼果子") class School: def __init__(self): self.kongfu = "现代煎饼果子配方" def make_cake(self): print(f"使用{self.kongfu}制作出现代煎饼果子") class Cat(School,Master): # 在子类中定义与父类相同名字的属性和方法 def __init__(self): self.kongfu = "猫式煎饼果子配方" def make_cake(self): print(f"使用{self.kongfu}制作出猫式煎饼果子") # 如果子类和父类的方法名和属性名相同,则默认使用子类的 cat = Cat() print(cat.kongfu) # 子类与父类有相同名字的属性,默认使用子类的属性 cat.make_cake() # 子类与父类有相同名字的方法,默认调用子类的方法 # 多继承的继承顺序 __mro__ # 语法:子类.__mro__ print(Cat.__mro__)
7.子类获取父类的属性和方法
# 子类调用父类同名属性和方法 class Master: def __init__(self): self.kongfu = "古法煎饼果子配方" def make_cake(self): print(f"使用{self.kongfu}制作出古法煎饼果子") class School: def __init__(self): self.kongfu = "现代煎饼果子配方" def make_cake(self): print(f"使用{self.kongfu}制作出现代煎饼果子") class Cat(School,Master): # 在子类中定义与父类相同名字的属性和方法 def __init__(self): self.kongfu = "猫式煎饼果子配方" def make_cake(self): print(f"使用{self.kongfu}制作出猫式煎饼果子") # 调用School类中的同名属性和方法 # super()在多继承中只能获取第一个父类的同名属性和方法 def get_school(self): # 获取父类同名属性:super().__init__() super().__init__() print(f"获取到的配方为{self.kongfu}") # 调用父类同名方法:super().方法名() super().make_cake() # 调用Master类中的同名属性和方法 # 类名.的方式可以获取任意一个父类的同名属性和方法 def get_master(self): # 获取父类同名属性:类名.__init__(self) Master.__init__(self) print(f"获取到的配方为{self.kongfu}") # 调用父类同名方法:类名.方法(self) Master.make_cake(self) cat = Cat() cat.get_school() cat.get_master()
注意:
在不进行方法重写和获取方法之前 如果子类继承了一个父类,且子类中没有init。则初始化父类的init,获取父类的属性 如果子类继承了多个父类,且子类中没有init。则初始化第一个父类的init,获取第一个父类的属性 如果子类继承了多个父类,且子类中有init。则初始化子类本身的init,所有的父类的属性都无法获取
三、封装
1.概念
将对象的属性和实现细节隐藏起来,仅对外提供公共访问方式。提高代码的安全性
使用私有属性和私有方法实现
2.私有属性
2.1定义私有属性
class MyCls: def __init__(self): self.money = 100 # 将属性设置为私有属性 在属性名前__ self.__mimi = "我有一个小秘密" my = MyCls() print(my.__mimi) # 对象获取不到私有属性
2.2 获取私有属性
class MyCls: def __init__(self): self.money = 100 # 将属性设置为私有属性 在属性名前__ self.__mimi = "我有一个小秘密" # 获取私有属性:通过定义公共的实例方法,在实例方法中使用私有属性 # 私有属性只是不能在类外获取,但是可以提供在类内部进行使用;用于处理类内部的操作 def get_mimi(self): print(f"获取到的私房钱为:{self.__mimi}") my = MyCls() my.get_mimi()
2.3 修改私有属性的值
class MyCls: def __init__(self): # 将属性设置为私有属性 在属性名前__ self.__mimi = "我有一个小秘密" # 修改私有属性的值:通过定义公共的实例方法,在实例方法中获取私有属性,重新赋值 def set_mimi(self,str1): self.__mimi = str1 print(f"获取到的秘密为:{self.__mimi}") my = MyCls() my.set_mimi("嘿嘿嘿嘿嘿嘿")
3.私有方法
3.1 定义私有方法
class MyCls: # 将方法设置为私有方法 在方法名前__ def __house(self): print("我有一个大别墅") my = MyCls() my.__house() # 对象无法调用私有方法
3.2 调用私有方法
class MyCls: # 将方法设置为私有方法 在方法名前__ def __house(self): print("我有一个大别墅") # 调用私有方法:通过定义公共的实例方法,在公共的方法中调用私有方法 def get_house(self): # 调用私有方法-->实例方法 self.__house() my = MyCls() my.get_house()
4.总结
关于私有属性和私有方法的总结: 私有权限:在属性和方法名前边加上两个下划线 __ 1. 类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问; 2. 类的私有属性 和 私有方法,都不会被子类继承,子类也无法访问; 3. 私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用。
四、多态
1.概念
不同的对象调用父类相同的方法获取到不同的执行结果
多态的实现需要建立在继承与方法重写的基础上
2.简单案例
class Animal: def bark(self): print("动物叫") class Dog(Animal): def bark(self): print("小狗汪汪叫") class Cat(Animal): def bark(self): print("小猫喵喵叫") dog = Dog() cat = Cat() dog.bark() cat.bark()
五、补充
1.类对象
# 定义一个英雄类 class Hero: # 定义实例属性 def __init__(self,name): self.name = name # 定义实例方法 def kill(self): print(f"{self.name}英雄会杀人") # 实例化一个对象-->实例对象 luban = Hero("鲁班") # 实例对象获取实例属性 print(luban.name) # 实例对象调用实例方法 luban.kill() # 类对象-->类本身-->类名 print(Hero) # 类对象无法获取实例属性 # 类对象无法调用实例方法
2.类属性
# 定义一个英雄类 class Hero: # 类属性:直接在定义类的类名下 属性名 = 属性值 class_name = "英雄类" # 实例化一个对象-->实例对象 luban = Hero("鲁班") # 实例对象获取类属性 print(luban.class_name) # 类对象-->类本身-->类名 print(Hero) # 类对象获取类属性 print(Hero.class_name)
3.类方法
# 定义一个英雄类 class Hero: # 定义类方法,需要借助装饰器 classmethod 实现定义类方法 # 装饰器用法:想要将哪一个方法创建为类方法,在该方法上方 @装饰器 # 创建好的类方法自带参数cls;cls-->类对象本身 @classmethod def run(cls): print("英雄会跑") # 实例化一个对象-->实例对象 luban = Hero("鲁班") # 实例对象调用类方法 luban.run() # 类对象-->类本身-->类名 print(Hero) # 类对象调用类方法 Hero.run()
4.静态方法
# 定义一个英雄类 class Hero: # 定义静态方法,需要借助装饰器 staticmethod 实现定义静态方法 @staticmethod def eat(): print("英雄会吃") # 实例化一个对象-->实例对象 luban = Hero("鲁班") # 实例对象调用静态方法 luban.eat() # 类对象-->类本身-->类名 print(Hero) # 类对象调用静态方法 Hero.eat()
5.属性在方法中的使用
# 定义一个英雄类 class Hero: # 类属性:直接在定义类的类名下 属性名 = 属性值 class_name = "英雄类" # 定义实例属性 def __init__(self,name): self.name = name # 定义实例方法 def kill(self): # 实例对象可以获取实例属性,实例方法中使用实例属性 self.实例属性名 # 实例对象可以获取类属性,实例方法中使用类属性 self.类属性名 print(f"{self.name}英雄属于{self.class_name}") # 定义类方法,需要借助装饰器 classmethod 实现定义类方法 # 装饰器用法:想要将哪一个方法创建为类方法,在该方法上方 @装饰器 # 创建好的类方法自带参数cls;cls-->类对象本身 @classmethod def run(cls): # 类对象可以获取类属性但是不能获取实例属性 # 在类方法中可以使用类属性,不能使用实例属性 # 类方法中使用类属性 cls.类属性名 print(f"英雄会跑,属于{cls.class_name}") # 定义静态方法,需要借助装饰器 staticmethod 实现定义静态方法 # 静态方法中不提供任何对象参数,无法调用实例属性和实例方法 @staticmethod def eat(): print("英雄会吃") # 实例化一个对象-->实例对象 luban = Hero("鲁班") # 实例对象获取实例属性 print(luban.name) # 实例对象调用实例方法 luban.kill() # 实例对象获取类属性 print(luban.class_name) # 实例对象调用类方法 luban.run() # 实例对象调用静态方法 luban.eat() # 类对象-->类本身-->类名 print(Hero) # 类对象无法获取实例属性 # 类对象无法调用实例方法 # 类对象获取类属性 print(Hero.class_name) # 类对象调用类方法 Hero.run() # 类对象调用静态方法 Hero.eat()
6.在方法中修改属性
6.1 在实例方法中修改数据
# 定义一个英雄类 class Hero: # 类属性:直接在定义类的类名下 属性名 = 属性值 class_name = "英雄类" # 定义实例属性 def __init__(self,name): self.name = name # 定义实例方法 def kill(self): # 在实例方法中获取属性 # 实例对象可以获取实例属性,实例方法中使用实例属性 self.实例属性名 # 实例对象可以获取类属性,实例方法中使用类属性 self.类属性名 print(f"{self.name}英雄属于{self.class_name}") # 在实例方法中修改属性的值 # 修改实例属性的值 self.实例属性名 = 新值 self.name = "卤蛋" # 使用实例对象修改类属性的值 == 创建新的实例属性class_name # self.类属性名 = 新值 self.class_name = "超人类" # 实例化一个对象-->实例对象 luban = Hero("鲁班") luban.kill() # "鲁班英雄属于英雄类" print(luban.name) # "卤蛋" print(luban.class_name) # "超人类" print(Hero.class_name) # "英雄类" 如果需要在类外修改**类属性**,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的**实例属性**,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是**实例属性**,除非删除了该实例属性。
6.2 在类方法中修改属性
# 定义一个英雄类 class Hero: # 类属性:直接在定义类的类名下 属性名 = 属性值 class_name = "英雄类" # 定义类方法,需要借助装饰器 classmethod 实现定义类方法 # 装饰器用法:想要将哪一个方法创建为类方法,在该方法上方 @装饰器 # 创建好的类方法自带参数cls;cls-->类对象本身 @classmethod def run(cls): # 类对象可以获取类属性但是不能获取实例属性 # 在类方法中可以使用类属性,不能使用实例属性 # 类方法中使用类属性 cls.类属性名 print(f"英雄会跑,属于{cls.class_name}") # 在类方法中修改类属性 cls.类属性名 = 属性值 cls.class_name = "超人类" Hero.run() # "英雄会跑,属于英雄类" print(Hero.class_name) # "超人类"
7.总结
实例对象:实例属性、实例方法、类属性、类方法、静态方法 类对象:类属性、类方法、静态方法 1. 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法; 2. 实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。 3. 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用 在平时定义类中实例方法最常用 类方法-->修改类属性的值、工厂模式 静态方法-->只是想让功能代码存放在类中,使用静态方法即可