面向对象之对象的三大特性

一、封装 封装是一种将数据和相关方法组合成一个单独的实体的机制。它将数据(属性)和操作数据的方法(方法)封装在一个对象中,并对外部代码隐藏了内部的实现细节。通

一、封装

封装是一种将数据和相关方法组合成一个单独的实体的机制。它将数据(属性)和操作数据的方法(方法)封装在一个对象中,并对外部代码隐藏了内部的实现细节。通过封装,对象可以提供一个公共接口,使得外部代码可以通过该接口访问和操作对象的数据,而不

需要了解其内部的具体实现。

封装之后:可以使用对象.属性的方法,不需要记所有的方法和属性。 掩藏属性  

二、继承

继承是新建类的一种方式,新建出来的类称作子类,被继承的类叫父类

它允许新类(子类)继承现有类(父类)的属性和方法,并且可以在新类中添加新的属性和方法,或者重写父类的方法来定制子类的行为。

继承促进了代码的重用和扩展(),并且提供了层次化和组织化的代码结构。解决了类与类之间的代码冗余问题

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、多继承下的属性查找

多继承下的属性查找分为:菱形查找和非菱形查找

菱形查找分:经典类和新式类
经典类:按照深度优先查询
新式类:按照的广度优先查询

非菱形查找

注:在python3 中都是新式类

所以,多继承下的属性查找,如果属性找不到,就按照广度优先查询

菱形查找:

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,形成菱形查找

7、super()内置函数与mro列表

super()是一个内置函数,用于调用父类的方法。它提供了一种方便的方式来访问父类的属性和方法,从而实现方法重写和继承(派生)。

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()

  

 

您可能有感兴趣的文章
面向对象编程思想 OOP

什么是面向对象、对象是什么、什么是类、如何如何使用对象、类和对象的关系

面向对象的概述

java面向对象:面向对象的思想和概述

C# 基础知识系列