Python学习之面向对象

一、什么是面向对象编程(OOP)?

面向对象编程(Object-Oriented Programming,OOP)是一种程序设计范式,它以对象为中心,将数据和操作数据的方法(函数)组合到一个单元中,这个单元就是对象。每个对象都是类的一个实例,而类则定义了对象的属性(数据)和方法(代码)。换句话说,面向对象编程将现实世界中的实体抽象为程序中的对象,这些对象可以相互交互、传递消息,并且可以继承和扩展。

‼️几个核心的概念

  • 类(Class):定义了对象的模板,包括数据和方法。
  • 对象(Object):类的实例,具有特定的属性和方法。
  • 封装(Encapsulation):将数据(属性)和操作数据的方法(函数)封装到对象中,使得对象的内部细节对外部不可见。
  • 继承(Inheritance):允许一个类(子类)继承另一个类(父类)的属性和方法,并且可以添加自己的特定属性和方法。
  • 多态(Polymorphism):允许不同类的对象对同一个方法做出不同的响应,提高代码的灵活性和可重用性。

🌰一个简单的例子

假设我们要模拟一个动物园中的动物。我们可以使用定义一个 Animal(动物)类,并创建具体的动物对象。这就是面向对象编程的两个概念了,类、对象。

# 定义 Animal (动物)类
class Animal:
    # 初始化方法
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 定义方法
    def make_sound(self):
        pass

# 定义具体的动物类: 猫(Cat) 和 狗(Dog)
class Cat(Animal):
    def make_sound(self):
        return "Meow"
    
class Dog(Animal):
    def make_sound(self):
        return "Woof"
    

# 创建动物对象
cat1 = Cat("Kitty", 3)
dog1 = Dog("Buddy", 5)

# 调用动物对象的方法
print(cat1.name, "says:", cat1.make_sound()) # 输出:Kitty says: Menow
print(dog1.name, "says:", dog1.make_sound()) # 输出:Buddy says: Woof

上面的代码,我们一步步来说:

第一步:首先定义了一个 Animal(动物)类,它有两个属性 name(名字)和 age(年龄)。在 __init__ 构造函数中,我们初始化了这两个属性。

第二步:Animal 类有一个 make_sound(发出声音)方法,但在基类中我们只定义了方法的签名,没有具体的实现。这是因为我们无法确定所有动物的叫声,具体的叫声会在子类中实现。

第三步:然后我们定义了两个具体的动物类 Cat(猫)和 Dog(狗),它们都继承自 Animal 类。这意味着 Cat 和 Dog 类会继承 Animal 类的属性和方法,并且可以根据需要添加自己的属性和方法。

第四步:在 Cat 类和 Dog 类中,我们重写了 make_sound 方法,分别返回了猫和狗的叫声

第五步:最后,我们创建了两个具体的动物对象 cat1 和 dog1,分别是一只名叫 "Kitty" 的猫和一只名叫 "Buddy" 的狗。

第六步:我们调用了这两个动物对象的 make_sound 方法,并打印出它们的名字和叫声。

上面总共讲了6步,6步里面涉及到了:类(Class)、对象(Object)、封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)。这些概念,具体怎么看呢?往下看:

  • 类:Animal、Cat、Dog 分别是类,它们定义了对象的模板,包括属性和方法。
  • 对象:cat1 和 dog1 是 Cat 和 Dog 类的对象,它们是具体的实例,具有特定的属性和方法。
  • 封装:每个动物对象封装了自己的属性 name 和 age,并且调用了自己的 make_sound 方法。
  • 继承:Cat 和 Dog 类继承了 Animal 类的属性和方法,避免了重复定义。
  • 多态:make_sound 方法在不同的子类中表现出不同的行为,根据对象的类型返回不同的叫声。

二、类和对象

让我们以一个仍然用上面的例子来说明什么是类和对象。我们创建了一个"动物"(Animal)类,然后创建该类的对象,比如"狗"(Dog)和"猫"(Cat)。

1. 类的定义和创建

我们可以定义一个"动物"(Animal)类,该类具有一些基本属性(如名称和年龄),以及一些行为(如发出声音)。

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def make_sound(self):
        pass  # 此处留空,因为这是一个抽象的动作,每种动物发出的声音不同,后续会在子类中实现

在这个类中,我们定义了一个构造函数 __init__(),它接收 name 和 age 作为参数,并将它们分配给对象的属性。我们还定义了一个 make_sound() 方法,但目前它什么也不做,因为每种动物发出的声音都不同,所以我们会在子类中覆盖这个方法。

🌾 类的定义 class
class Animal:   # 通过 class + 类名
  pass  

类的命名方法为Pascal命名法,即首字母大写。

🌾 类的构造函数

类的构造函数,主要作用是定义实例对象的属性。

1、必须命名为 __init__ 前后必须有两个下划线。
2、括号内可以放任意数量的参数,但是第一个永远是被占用的,用于表示自身,约定俗称为self。
3、它能把属性绑定在实列的对象上。

🌾 定义类的方法(函数)
定义类的方法与创建普通函数差不多。有两点区别:

1、需要创建在class里面,前面需要有缩进,来表示属于该类的方法。
2、和 __init__ 一样,第一个参数被占用,用于表示对象自身,约定俗成为 self 。

🌾 类的继承

在子类名称后的括号中写上父类的名称,此时父类的方法就会继承到子类中。

 

2. 对象的创建和使用

现在,让我们创建具体的动物对象,比如一个狗对象和一个猫对象,并使用它们的属性和方法。

class Dog(Animal):
    def make_sound(self):
        return "Woof!"

class Cat(Animal):
    def make_sound(self):
        return "Meow!"

# 创建狗对象和猫对象
my_dog = Dog("Buddy", 3)
my_cat = Cat("Whiskers", 5)

# 访问对象的属性
print(f"My dog's name is {my_dog.name} and it is {my_dog.age} years old.")
print(f"My cat's name is {my_cat.name} and it is {my_cat.age} years old.")

# 调用对象的方法
print(f"My dog says: {my_dog.make_sound()}")
print(f"My cat says: {my_cat.make_sound()}")

3. 类和对象的关系

在这个例子中,Animal 是一个基类(父类),它定义了动物的基本属性和方法。然后,Dog 和 Cat 是 Animal 类的子类(也可以称为派生类),它们继承了 Animal 类的属性和方法,并且还可以定义自己独特的属性和方法。

当我们创建 my_dog 和 my_cat 时,实际上是在内存中实例化了两个对象,每个对象都有自己的 name 和 age 属性,并且可以调用 make_sound() 方法。

🔊:类是对对象的抽象,它定义了对象的属性和方法。 对象是类的实例,它具体化了类的定义,并可以执行类中定义的操作。

类和对象之间的关系就像是模具和制造出的产品之间的关系:模具定义了产品的形状和特性,而产品则是模具的具体实例。 

4. 属性和方法

🌾 实例属性和类属性

实例属性:实例属性是指属于特定实例的属性。它们在创建实例时被赋予,并且每个实例都可以有不同的值。通常在类的 __init__ 方法中初始化。

class Car:
    def __init__(self, make, model ,year):
        # 实例属性
        self.make = make
        self.model = model
        self.year = year

在这里,make, model, 和 year 就是 Car 类的实例属性。

类属性:属于类本身的属性,被所有类的实例共享。可以直接在类定义中设置。

class Car:
    num_cars = 0  #类属性
    
    # 初始化方法
    def __init__(self, make, model ,year):
        # 实例属性
        self.make = make
        self.model = model
        self.year = year

        # 每创建一个实例,类属性num_cars 加一
        Car.num_cars += 1  

在这里,num_cars 就是 Car 类的类属性,它被所有 Car 类的实例共享。

🌾 实例方法和类方法

实例方法:操作实例属性的方法,第一个参数通常是 self,代表对象本身。可以访问实例属性,并且可以改变实例的状态。

class Car:
    #初始化方法
    def __init__(self, brand):
        self.brand = brand
        self.speed = 0
    #实例方法
    def accelerate(self, amount):
        self.speed += amount
    def brake(self, amount):
        self.speed -= amount

my_car = Car('ToyoTa')
print(my_car.speed) # 输出:0

my_car.accelerate(50)
print(my_car.speed) # 输出:50

my_car.brake(20)
print(my_car.speed) # 输出:30

accelerate 、brake 就是 Car 类的实例方法,用于打印汽车信息。

类方法:操作类属性的方法,使用 @classmethod 装饰器定义,第一个参数通常是 cls,代表类本身。可以访问类属性,并且可以在整个类上执行操作。

class Dog:
    # 属性
    num_of_dogs = 0
    # 初始化方法
    def __init__(self, name):
        self.name = name
        Dog.num_of_dogs += 1
    # 类方法
    @classmethod
    def get_num_of_dogs(cls):
        return cls.num_of_dogs

dog1 = Dog("Buddy")
dog2 = Dog("Max")
print(Dog.get_num_of_dogs()) # 输出:2

get_num_of_dogs 就是 Dog 类的类方法,用于显示狗总数。

🌾 静态方法

静态方法:不操作实例属性或类属性的方法,使用 @staticmethod 装饰器定义。它们与类无关,不传递 self 或 cls 参数。可以在类中定义,但是不能访问 self 或 cls。

class MyMath:
    @staticmethod
    def add(x, y):
        return x + y
    
result = MyMath.add(5,10)
print(result) # 输出:15

add 就是 Math 类的静态方法,提供关于类的一般信息。

💡 @classmethod 和 @staticmethod 区别

@classmethod 和 @staticmethod 是 Python 中用于定义类方法和静态方法的装饰器,它们有以下区别:

参数传递

@classmethod 装饰的方法会自动将类作为第一个参数传递给方法(通常使用 cls 表示),可以通过该参数访问类的属性和方法。
@staticmethod 装饰的方法不会自动传递类或实例,它与普通函数类似,不依赖于类或实例的特定上下文。

访问类和实例

@classmethod 装饰的方法可以访问类的属性和方法,也可以通过类调用和通过实例调用。
@staticmethod 装饰的方法不能直接访问类的属性和方法,也不能访问实例的属性和方法,它与类和实例无关。

继承和多态

@classmethod 装饰的方法在子类中可以被继承和重写,子类调用时会自动传递子类作为第一个参数。
@staticmethod 装饰的方法不会自动传递类信息,子类无法继承和重写静态方法,它是与类和实例无关的普通函数。

选择使用 @classmethod 还是 @staticmethod 取决于方法的使用场景和需求

如果方法需要访问类的属性和方法,或者需要在子类中进行多态调用,通常使用 @classmethod。
如果方法与类和实例无关,只是类中的工具函数或者通用函数,可以使用 @staticmethod。

三、封装 (Encapsulation)

​封装是指将数据(属性)和操作数据的方法(方法)捆绑在一起的机制。在封装中,对象的内部细节被隐藏起来,只有特定的方法才能访问和操作这些细节。这有助于确保数据的安全性和代码的可维护性。

1. 如何实现封装?

封装可以通过访问控制和访问修饰符来实现。主要有两种访问修饰符:公有属性和方法、私有属性和方法。

🌾 公有属性和方法

可以被类的外部访问。在 Python 中,默认情况下,类的所有属性和方法都是公有的。

class Person:
    def __init__(self, name, age):
        self.name = name #公有属性 
        self.age = age   #公有属性

    def get_name(self):
        return self.name #公有方法
    
# 使用公有属性 和 公有方法
person_1 = Person('Python', 30) 
print(person_1.name) #Python
print(person_1.age) #30
print(person_1.get_name()) #Python

在这个例子中,Person类有一个公有方法 get_name(),那么其他类或代码可以通过调用这个方法来获取对象的名称。同样,有一个公有属性 age,那么其他类或代码可以直接访问和修改这个属性。所以,我们的实例对象就可以访问公共的属性和方法。

🌾 私有属性和方法

只能在类的内部访问,外部无法直接访问。在 Python 中,可以在属性名或方法名前加上双下划线 ‘__’ 来定义私有属性和方法

class Person:
    #初始化方法
    def __init__(self,name,age):
        self.__name = name #私有属性
        self.__age = age #私有属性
    #实例方法
    def __display_info(self):
        return f'Name:{self.__name}, Age: {self.__age}' #私有方法

# 外部无法直接访问私有属性 和 方法
person_1 = Person('Python', 25)
print(person_1.__name) # ❌错误:这会引发错误,因为__name是私有属性
print(person_1.__age)  # ❌错误:这会引发错误,因为__age是私有属性
print(person_1.__display_info()) # ❌错误:这会引发错误,因为__display_info()是私有方法

在上面的例子中:person1对象就不能访问__name私有属性和__display_info()私有方法。

尽管外部无法直接访问私有属性和方法,但我们仍然可以通过公有方法来间接访问和操作它们。这种间接访问的方式使得我们可以控制对象的状态和行为,确保数据的一致性和安全性。

class Person:
    def __init__(self, name, age):
        self.__name = name   # 私有属性
        self.__age = age     # 私有属性

    def get_name(self):
        return self.__name   # 公有方法

    def set_name(self, new_name):
        self.__name = new_name   # 公有方法,用于修改私有属性

    def display_info(self):
        return f"Name: {self.__name}, Age: {self.__age}"   # 公有方法

# 通过公有方法访问和修改私有属性
person1 = Person("Python2.0", 30)
print(person1.get_name())  # 输出: Python2.0
person1.set_name("Python3.0")
print(person1.display_info())  # 输出: Name: Python3.0, Age: 30

那么,封装通过隐藏对象的内部细节、定义清晰的接口,提高了代码的安全性、可维护性和复用性,这就使得我们的程序更加健壮和易于开发与维护。

四、继承(Inheritance)

继承就是允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。子类可以继承父类的特性,并且可以在此基础上添加自己的新特性。这种机制允许代码的重用和层次化的设计。继承,就是字面上意思,继承。

1. 继承的类型

🌾 单继承

单继承是指一个子类只能继承一个父类的属性和方法。这是最简单和最常见的继承类型。

class ParentClass:

    def parent_method(self):
        print("Parent method")

class ChildClass(ParentClass):
    def child_method(self):
        print("Child method")

# 子类继承父类的方法
child = ChildClass()
child.parent_method()  # 输出:Parent methods
child.child_method()   # 输出:Child methods
🌾 多继承

多继承是指一个子类可以同时继承多个父类的属性和方法。这使得子类可以具有多个父类的特性,但也可能引发一些复杂性和歧义。

class ParentClass1:
    def method1(self):
        print("Method 1 from ParentClass1")

class ParentClass2:
    def method2(self):
        print("Method 2 from ParentClass2")

class ChildClass(ParentClass1, ParentClass2):
    def child_method(self):
        print("Child method")

# 子类继承多个父类的方法
child = ChildClass()
child.method1()  # 输出: Method 1 from ParentClass1
child.method2()  # 输出: Method 2 from ParentClass2
child.child_method()  # 输出: Child method
🌾 继承顺序

在多继承的情况下,Python 使用 C3 线性化算法来确定方法解析顺序(Method Resolution Order, MRO)。MRO 定义了类的方法解析顺序,确保在继承链中查找方法时,按照一定的顺序进行搜索。

可以通过调用类的 mro() 方法来查看方法的解析顺序:

print(ChildClass.mro())
# 输出: [<class '__main__.ChildClass'>, <class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>, <class 'object'>]

2. 继承的实际应用

🌾 创建子类

通过继承可以创建子类,实现代码的重用和层次化的设计。子类可以继承父类的方法和属性,并且可以添加自己的方法和属性。

# 定义父类
class Animal:
    def speak(self):
        pass
# 定义 Dog 子类
class Dog(Animal):
    def speak(self):
        return "Woof!"

# 定义 Cat 子类
class Cat(Animal):
    def speak(self):
        return "Meow!"

# 创建实例对象
dog = Dog()
print(dog.speak())  # 输出: Woof!

cat = Cat()
print(cat.speak())  # 输出: Meow!
🌾 覆盖父类方法

子类可以覆盖父类的方法,即在子类中重新定义与父类同名的方法。这样做可以根据子类的需求修改方法的实现。

# 父类
class Bird:
    def speak(self):
        return 'Chirp!'

#子类
class Parrot(Bird):
    # 子类重写父类方法
    def speak(self):
        return 'Polly wants a cracker!'
    
parrot = Parrot()
print(parrot.speak()) # 输出:Polly wants a cracker!
🌾 调用父类方法(super() 函数)

子类中可以通过 super() 函数调用父类的方法。这在子类需要扩展父类方法的功能时非常有用。

# 父类
class Rectangle:
    # 初始化方法
    def __init__(self, length, width):
        self.length = length
        self.width = width

    # 实例方法
    def area(self):
        return self.length * self.width

# 子类
class Square(Rectangle):
    def __init__(self, side_length):
        super().__init__(side_length, side_length)  # 调用父类的 __init__() 方法

# 实例对象
square = Square(5)
print(square.area())  # 输出: 25

在上面的例子中,Square 类继承自 Rectangle 类,并通过 super() 函数调用了 Rectangle 类的 __init__() 方法,实现了对正方形的初始化。 

五、多态(Polymorphism)

多态是指允许对象在不同的情况下表现出不同的行为。简单地说,多态性意味着相同的方法调用可能会有不同的实现方式,具体取决于调用该方法的对象的类型或类的实现。

1. 多态的实现方式

🌾 方法重写(Method Overriding)

方法重写是实现多态的一种方式,它允许子类覆盖(重写)父类的方法,以便在子类中实现特定的行为。当子类重新定义了与父类同名的方法时,调用这个方法时会执行子类的实现。通过一个例子展示方法重写:

class Animal:
    def speak(self):
        raise NotImplementedError("Subclass must implement abstract method")

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

# 多态性的体现
def animal_sound(animal):
    return animal.speak()

dog = Dog()
cat = Cat()

print(animal_sound(dog))  # 输出: Woof!
print(animal_sound(cat))  # 输出: Meow!
🌾 方法重载(Method Overloading)

方法重载是一种在同一个类中,方法名称相同但参数列表不同的技术。但是,在Python中,并没有像其他编程语言那样直接支持方法重载的特性,不过,可以通过一些技巧来模拟。

比如:使用默认参数值或者 *args 和 **kwargs 参数来实现类似方法重载的效果。

class Calculator:
    def add(self, a, b):
        return a + b

    def add(self, a, b, c):
        return a + b + c

calc = Calculator()
print(calc.add(2, 3))    # 输出: TypeError: add() missing 1 required positional argument: 'c'
print(calc.add(2, 3, 4))  # 输出: 9

2. 多态的实际应用

🌾 多态的实例

多态在实际应用中非常多。

class Shape:
    def draw(self):
        pass

class Circle(Shape):
    def draw(self):
        return "Drawing Circle"

class Square(Shape):
    def draw(self):
        return "Drawing Square"

# 多态性的体现
def draw_shape(shape):
    return shape.draw()

circle = Circle()
square = Square()

print(draw_shape(circle))  # 输出: Drawing Circle
print(draw_shape(square))  # 输出: Drawing Square

例子,展示了在图形绘制中,不同的图形对象可以有不同的 draw 方法实现,但可以使用相同的方式进行绘制。Shape 是一个基类,Circle 和 Square 是它的子类,它们都有自己的 draw 方法实现。在调用 draw_shape 函数时,根据传入的参数不同,会执行不同子类的 draw 方法。

🌾 使用抽象基类(Abstract Base Classes)

abc 模块,可以通过抽象基类定义抽象方法,从而强制子类实现这些方法,实现接口的规范化。

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def draw(self):
        pass

class Circle(Shape):
    def draw(self):
        return "Drawing Circle"

class Square(Shape):
    def draw(self):
        return "Drawing Square"

# 多态性的体现
def draw_shape(shape):
    return shape.draw()

# 抽象基类确保子类实现了抽象方法
# rectangle = Shape()  # 会引发错误,因为 Shape 是抽象基类,不能实例化

circle = Circle()
square = Square()

print(draw_shape(circle))  # 输出: Drawing Circle
print(draw_shape(square))  # 输出: Drawing Square

在这个例子中,Shape 是一个抽象基类,定义了一个抽象方法 draw,所有继承自 Shape 的子类必须实现 draw 方法。这样可以确保所有的子类都有相同的接口,实现了多态的规范化。

通过上面的介绍,我们知道了多态使得代码更加灵活、可扩展、简洁。这也就是为什么面向对象那么受欢迎的原因了。

六、类模块化

类也可以被存储在称为模块的独立文件中,使用时再将模块导入到主程序中。

1. 创建模块

类名应采用驼峰命名法,即类名中的每个单词的首字母都大写,且不使用下划线,模块名一般都采用类名的小写格式,并且在单词之间加下划线。使用该类的程序都必须采用更加具体的名称。

# car.py 文件
"""文档描述"""


# 创建一个 Car 类 class Car: --snip--

当然,也可以在一个模块中存储多个类,在一个模块中,可使用两个空行来分隔类。

# car.py 文件
"""文档描述"""

# 定义父类Car 
class Car:
    --snip--


# 定义子类Buttery
class Buttery:
    --snip--


# 定义子类ElectricCar
class ElectricCar(Car):
    --snip--

每个模块的开头应当包含一个文档字符串,对其中的类可用于做什么进行简要的描述。

2. 导入整个模块

可以使用import语句导入模块,进而在当前程序中使用模块中的代码。

import car

要访问car.py中的类,需要采用句点表示法,即“模块名称.类名称()”的格式。

my_beetle = car.Car("volkswagen","beetle",2019)
my_tesla = car.ElectricCar("tesla","roadster",2019)

3. 导入模块中的特定类

导入模块中的特定类可以采用如下语法:

from car import Car

如果需要导入多个类,可以用逗号分隔。

from car import Car,ElectricCar

此时可以直接访问导入的类。

my_beetle = Car("volkswagen","beetle",2019)
my_tesla = ElectricCar("tesla","roadster",2019)

如果导入的类名字太长,可以使用关键词as指定别名。

from car import ElectricCar as EC

4. 导入模块中的所有类

使用星号(*)运算符可以导入模块中的所有类,但不建议这么使用,此处列出仅做了解。

from car import *

由于导入了每个类,因此在访问时可以直接使用名称来调用每个类而无需使用句点表示法。但是如果被导入的模块中有类与当前项目中的类重名,会直接覆盖掉当前项目中的类,引发难以诊断的错误。 需要从一个模块中导入很多类时,最好直接导入整个模块,并采用句点表示法来访问类,这样做可以避免引发名称冲突,而且可以清楚地看出在程序的哪些地方使用了导入的模块。

5. 在一个模块中导入另一个模块

有时需要将类分散到多个模块中,避免模块太大或同一个模块中存储不相关的类。将类分散到多个模块中存储时,可能会出现一个模块中的类依赖于另一个模块中的类的情况,这时就需要在这个模块中导入另一个模块。 比如说只在car.py中存储Car类

# car.py

"""一个可用于表示汽车的类"""

class Car:
    --snip--

在electric_car.py中存储Buttery类和ElectricCar类,而ElectricCar类需要访问其父类Car类,因此需要在electric_car.py中先导入Car类。如果忘记这项操作,Python将会在我们试图创建ElectricCar实例时报错。

#electric_car.py

"""一组可用于表示电动汽车的类"""

from car import Car

class Buttery:
    --snip--

class ElectricCar(Car):
    --snip--

下面演示在项目中分别从这两个模块导入类,并根据需要创建汽车。

from car import Car
from electric_car import ElctricCar

my_beetle = Car("volkswagen","beetle",2019)
my_tesla = ElectricCar("tesla","roadster",2019)
print(my_beetle.get_descriptive_name())
print(my_tesla.get_descriptive_name())
2019 volkswagen beetle
2019 tesla roadster

七. 实例展示

最后,我们通过两篇文章的学习,现在,我们通过实例代码来回顾一下所有的内容。

1. 创建一个简单的封装类

示例: 定义一个 Car 类,封装汽车的属性和方法。

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def __str__(self):
        return f"汽车信息:\n  品牌:{self.make}\n  型号:{self.model}\n  年份:{self.year}"

    def start(self):
        print(f"{self.make} {self.model} 已启动!")

car = Car("Toyota", "Camry", 2024)
print(car)
car.start()

输出如下:

汽车信息:
  品牌:Toyota
  型号:Camry
  年份:2024

Toyota Camry 已启动!

2. 实现一个继承的示例

示例: 定义一个 ElectricCar 类,继承 Car 类并添加续航里程属性和方法。

# 定义Car类
class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def __str__(self):
        return f"汽车信息:\n  品牌:{self.make}\n  型号:{self.model}\n  年份:{self.year}"

    def start(self):
        print(f"{self.make} {self.model} 已启动!")

# 继承Car
class ElectricCar(Car):
    def __init__(self, make, model, year, range):
        super().__init__(make, model, year)
        self.range = range

    def __str__(self):
        return f"{super().__str__()}\n  续航里程:{self.range}公里"

electric_car = ElectricCar("Tesla", "Model 3", 2024, 600)
print(electric_car)

输出如下:

汽车信息:
  品牌:Tesla
  型号:Model 3
  年份:2024
  续航里程:600公里

3. 演示多态的用例

示例: 定义一个 Vehicle 类,包含 drive() 方法,并定义 Car 和 Bicycle 两个子类,分别实现不同的 drive() 方法。

class Vehicle:
    def drive(self):
        raise NotImplementedError
# 多态重写
class Car(Vehicle):
    def drive(self):
        print("驾驶汽车...")

class Bicycle(Vehicle):
    def drive(self):
        print("骑自行车...")

car = Car()
bicycle = Bicycle()

vehicles = [car, bicycle]

for vehicle in vehicles:
    vehicle.drive()

输出如下:

驾驶汽车...
骑自行车... 

八、总结

类、对象、封装、继承和多态的重要性总结如下表:

概念描述重要性
一种抽象数据类型,用于描述一组具有相同属性和方法的对象。 提高代码的组织性和可维护性,提高代码的复用性。
对象 类的实例,包含了类所定义的所有属性和方法。 提高代码的组织性和可维护性,提高代码的复用性。
封装 将对象的属性和方法隐藏在类中,对外只提供接口进行访问。 提高代码的安全性,提高代码的可维护性。
继承 允许子类继承父类的属性和方法的一种机制。 提高代码的复用性,提高代码的可扩展性。
多态 同一个方法可以对不同类型的对象执行不同的操作。 提高代码的灵活性,提高代码的可扩展性。

一些概念需要你记住的:

  • 使用 class 关键字定义类。
  • 使用 __init__ 方法初始化对象的属性。
  • 使用 self 关键字引用当前对象。
  • 使用 super() 函数调用父类的方法。
  • 使用 isinstance() 函数检查对象的类型。

posted on 2024-12-03 19:48  梁飞宇  阅读(47)  评论(0)    收藏  举报