面向对象之对象的三大特性
一、封装
封装是一种将数据和相关方法组合成一个单独的实体的机制。它将数据(属性)和操作数据的方法(方法)封装在一个对象中,并对外部代码隐藏了内部的实现细节。通过封装,对象可以提供一个公共接口,使得外部代码可以通过该接口访问和操作对象的数据,而不
需要了解其内部的具体实现。
二、继承
继承是新建类的一种方式,新建出来的类称作子类,被继承的类叫父类
它允许新类(子类)继承现有类(父类)的属性和方法,并且可以在新类中添加新的属性和方法,或者重写父类的方法来定制子类的行为。
继承促进了代码的重用和扩展(),并且提供了层次化和组织化的代码结构。解决了类与类之间的代码冗余问题
1、为什么要继承?
类解决什么问题:解决的是对象与对象之间的代码冗余问题
继承解决什么问题:解决的是类与类之间的代码冗余问题
2、子类的继承可以有多个父类
class Parent1(object): pass class Parent2: pass # 子类 class Sub1(Parent1): pass # 多继承, 括号里面可以写多个类 class Sub2(Parent1, Parent2): pass
3、查看一个类继承了哪些父类,.__bases__方法
print(Sub1.__bases__) # (<class '__main__.Parent1'>,) print(Sub2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>) print(Parent1.__bases__) # (<class 'object'>,)
注:python3中新定义的父类默认继承的是object类,所以父类也是一个子类
4、继承小案例
class People: # 定义一个父类 school = 'SH' # 严格依赖继承 def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender class Student(People): def __init__(self, name, age, gender, course): """这个是指名道姓的调用方法,不依赖于继承""" People.__init__(self, name, age, gender) # name, age, gender 继承父类的属性 self.course = course # 选课 def choose_course(self): pass class teacher(People): def __init__(self, name, age, gender, level): People.__init__(self, name, age, gender) self.level = level # 教室职称 stu = Student('kevin', '19', 'male', 'python') teacher1 = teacher('王刚', 40, 'male', '高级教师') print(stu.name) # kevin print(teacher1.level) # 高级教师
5、
6、多
菱形查找分:经典类和新式类
经典类:按照深度优先查询
新式类:按照的广度优先查询
非菱形查找
class G(object): def test(self): print('from G') class E(G): def test(self): print('from E') class F(G): def test(self): print('from F') class B(E): def test(self): print('from B') class C(F): def test(self): print('from C') class D(G): def test(self): print('from D') class A(B,C,D): # def test(self): # print('from A') pass obj = A() obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object # 可依次注释上述类中的方法test来进行验证
⚠️:
1. 广度优先查找:从B开始到E回头,C到F,即查找到根部的上一级,最后一个分支查找到根部G
2. 如果D分支没有继承G,会在F查找完后查找G,形成菱形查找
class ParentClass: def __init__(self): self.parent_attr = "I am the parent class" def some_method(self): print("This is the parent class method") class ChildClass(ParentClass): def __init__(self): super(ParentClass, self).__init__() # python2 的写法,python3 兼容调构造方法 self.child_attr = "I am the child class" def some_method(self): super().some_method() # python2 的写法不支持 print("This is the child class method") # 创建子类对象 child = ChildClass() # 访问子类和父类的属性 # print(child.child_attr) # 输出: I am the child class # print(child.parent_attr) # 输出: I am the parent class # 调用子类和父类的方法 child.some_method() # 输出: # This is the parent class method # This is the child class method
如果不用super()方法来实现派生与方法重用:
指名道姓的调用某一个类的函数(不依赖于继承)
派生:比父类多的功能,派生方法。
相同的方法:重写
class ChildClass(ParentClass): def __init__(self): ParentClass.__init__(self) self.child_attr = "I am the child class" def some_method(self): ParentClass.some_method(self) print("This is the child class method")
mro列表
mro
(Method Resolution Order)是指在多重继承中确定方法调用顺序的算法。Python中的每个类都有一个mro列表,它决定了方法解析的顺序。MRO列表可以通过
__mro__
属性来访问,它是一个元组,按照方法解析顺序列出了类及其超类。MRO列表遵循C3线性化算法,它考虑了类的继承关系和方法重写,以确保方法的解析顺序是一致且合理的。
class A: def some_method(self): print("Method from class A") class B(A): # def some_method(self): # print("Method from class B") pass class C(A): def some_method(self): print("Method from class C") class D(B, C): # def some_method(self): # print("Method from class D") pass # 创建类D的实例 d = D() # 调用some_method()方法 d.some_method() # 打印MRO列表 print(D.__mro__)
输出结果
# Method from class C # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
从结果可以看出:查找遵循了广度优先原则
三、多态
一种事物的多种形态,比如动物类会发出叫声,但是不同的动物叫声是不一样的
1、概念
允许不同的对象通过相同的接口进行交互,而无需关注对象的具体类型。多态使得我们可以编写更加灵活和通用的代码,提高代码的可重用性和可扩展性。
多态的实现依赖于继承和方法重写的机制。当多个类继承自同一个父类,并且这些子类都实现了父类的方法,那么这些子类对象可以被视为父类对象的多态形式。
多态性的关键在于子类对象可以被赋值给父类对象的变量,然后通过父类的接口来调用方法。在编译时,编译器会根据变量的声明类型选择适当的方法。而在运行时,实际上调用的是子类对象的方法。
多态实现了面向对象编程中的一个重要原则:针对接口编程,而不是针对实现编程。
class Animal: def make_sound(self): pass class Dog(Animal): def make_sound(self): print("Woof!") class Cat(Animal): def make_sound(self): print("Meow!") def make_animal_sound(animal): animal.make_sound() # 创建不同的子类对象 dog = Dog() cat = Cat() # 通过父类接口调用方法 make_animal_sound(dog) # 输出: Woof! make_animal_sound(cat) # 输出: Meow!
⚠️:
1. 在此案例中Animal为父类,要求子类对象dog和cat有制造声音的功能。
2. dog和cat通过同一个接口传入,输出不同的结果
3. 此处的Animal 父类没有强制要求子类实现制造声音的功能,是一种思想,可以删掉Animal实现相同的效果
2、父类强制子类拥有相同的属性或方法
抽象类和抽象方法,用到abc模块,是abstract 抽象的英文缩写
class Animal(metaclass=abc.ABCMeta): 先变成抽象类,@abc.abstractmethod变成抽象方法
# 强制子类有某种属性方法 import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def speak(self): pass @abc.abstractmethod def run(self): pass class Cat(Animal): # def speak(self): def test(self): print('miaomiao') class Dog(Animal): def speak(self): print('wangwang!') cat = Cat() print(cat.test()) # TypeError: Can't instantiate abstract class Cat with abstract methods run, speak
注:用上抽象类以后,子类没有遵循限制,实现相关的功能就会报错
1. 类传入 ‘metaclass=abc.ABCMeta’成为抽象类, 方法使用@abc.abstractmethod装饰器,成为抽象方法。
2. 抽象类本身不能被实例化和直接调用,它主要用于作为其他类的基类(父类)。
3. 抽象类的主要目的是为了定义一组通用的属性和方法,供子类继承和实现。
3、鸭子类型
抽象类的强制限制,这种方式是python不推荐的,python推荐的是鸭子类型
鸭子类型是一种思想,它关注对象的行为而不是对象的类型。
根据鸭子类型的理念,如果一个对象具有与鸭子相似的行为特征,那么它可以被视为鸭子。
class Duck: def quack(self): print("Quack!") def fly(self): print("Flying!") class Robot: # 机器人 def quack(self): print("Beep!") def fly(self): print("Unable to fly!") def make_quack_and_fly(obj): # quack 嘎嘎叫声 obj.quack() obj.fly() duck = Duck() robot = Robot() make_quack_and_fly(duck) # 输出: Quack! Flying! make_quack_and_fly(robot) # 输出: Beep! Unable to fly!
注:
定义了两个类,实例化一个鸭子和机器人,通过统一的函数接口调用方法(quack、fly),得到不同的结果
4、组合
一个对象拥有的属性或者方法,该属性或方法的被另外一个对象调用
案例1: 学生和选课
class People: def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender class Course: def __init__(self, course_name, course_price, course_period): self.course_name = course_name self.course_price = course_price self.course_period = course_period # 实例化课程 python = Course("python", 10000, '6mon') linux = Course("linux", 20000, '5mon') class Student(People): def __init__(self, name, age, gender, course=None): if course is None: course = [] super(Student, self).__init__(name, age, gender) self.courses = course # def choose_course(self): # pass stu = Student('kevin', '19', 'male') stu.courses.append(python) # stu.courses ====> [python对象] stu.courses.append(linux) # stu.courses ====> [python对象, linux对象] # print(stu.courses) print(stu.courses[0].course_name) print(stu.courses[0].course_price)
案例2:car汽车对象使用了另一个类Engine中的函数功能,也是一种组合
class Engine: def start(self): print("Engine started.") def stop(self): print("Engine stopped.") class Car: def __init__(self): self.engine = Engine() # Car类包含Engine类对象 def start(self): print("Car starting.") self.engine.start() def stop(self): print("Car stopping.") self.engine.stop() car = Car() car.start() car.stop() # 输出 Car starting. Engine started. Car stopping. Engine stopped.
案例3:商品和订单的组合关系
class Product: def __init__(self, name, price): self.name = name self.price = price def info(self): print(f"Product: {self.name}, Price: ${self.price}") class Order: def __init__(self, order_id): self.order_id = order_id self.products = [] # 添加商品 def add_product(self, product): # 接收一个实例对象(包括对象的所有属性和方法) self.products.append(product) # 展示商品 def display(self): print(f"商品订单: {self.order_id}") print("商品展示:") for product in self.products: # 实例对象列表,每次循环都是一个实例 product.info() # 商品实例调用商品对象的内部info方法,打印商品名和价格信息 print("-----------") # 创建产品,实例化过程 product1 = Product("Phone", 999) product2 = Product("Laptop", 1499) product3 = Product("Headphones", 199) # 创建订单,实例化过程 order = Order("ORD-123") order.add_product(product1) # 把实例传进去 order.add_product(product2) order.add_product(product3) # 显示订单信息,order是一个实例对象 order.display()