面向对象编程:继承与多态+面试习题

面向对象编程中的继承与多态是实现代码复用、扩展和灵活设计的核心机制:

一、继承(Inheritance)

继承是指一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法,实现代码复用和功能扩展。(儿子继承父亲的财产)

1. 基本继承语法

复制代码
# 父类(基类)
class Animal:
    def __init__(self, name):
        self.name = name
        self.species = "Unknown"
    
    def eat(self):
        return f"{self.name} is eating."
    
    def sleep(self):
        return f"{self.name} is sleeping."

# 子类(派生类)- 继承Animal
class Dog(Animal):
    def __init__(self, name, breed):
        # 调用父类的构造方法
        super().__init__(name)
        self.breed = breed
        self.species = "Canine"
    
    # 子类新增方法
    def bark(self):
        return f"{self.name} is barking: Woof!"

# 子类(派生类)- 继承Animal
class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name)
        self.color = color
        self.species = "Feline"
    
    # 子类新增方法
    def meow(self):
        return f"{self.name} is meowing: Meow!"

2.2 方法重写(Override)

子类可以重新定义父类的方法,实现自己的逻辑:

复制代码
class Bird(Animal):
    def __init__(self, name, wingspan):
        super().__init__(name)
        self.wingspan = wingspan
        self.species = "Aves"
    
    # 重写父类的eat方法
    def eat(self):
        return f"{self.name} is pecking at food."
    
    def fly(self):
        return f"{self.name} is flying with {self.wingspan}cm wingspan."

# 调用重写后的方法
bird = Bird("Tweety", 12)
print(bird.eat())  # Tweety is pecking at food. (重写的方法)
print(bird.sleep())  # Tweety is sleeping. (继承的方法)

2.3 super()函数

用于调用父类的方法,实现父类与子类的协作:

复制代码
class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
    
    def get_info(self):
        return f"{self.year} {self.make} {self.model}"

class Car(Vehicle):
    def __init__(self, make, model, year, fuel_type):
        super().__init__(make, model, year)  # 调用父类构造方法
        self.fuel_type = fuel_type
    
    def get_info(self):
        # 调用父类的get_info方法,然后添加子类特有的信息
        return f"{super().get_info()} ({self.fuel_type})")

car = Car("Toyota", "Camry", 2023, "Gasoline")
print(car.get_info())  # 2023 Toyota Camry (Gasoline)

2.4 继承类型

单继承 :一个子类只有一个父类(Python默认支持)

复制代码
class A: pass
class B(A): pass

多继承 :一个子类有多个父类(用逗号隔开)

复制代码
class A: pass
class B: pass
class C(A, B): pass

多层继承 :子类继承自父类,父类又继承自另一个父类(爷爷、儿子、孙子)

复制代码
class A: pass
class B(A): pass
class C(B): pass

二、多态(Polymorphism)

多态是指不同类的对象对同一方法调用会产生不同的行为,实现"一个接口,多种实现"。

1. 多态的基本实现(每个人都长了一张脸但各有不同)

复制代码
# 定义多个类,都实现了相同的方法名
class Dog:
    def make_sound(self):
        return "Woof!"

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

class Cow:
    def make_sound(self):
        return "Moo!"

class Duck:
    def make_sound(self):
        return "Quack!"

# 多态函数:接收任何实现了make_sound方法的对象
def animal_sound(animal):
    return animal.make_sound()

# 调用同一函数,传入不同对象
dog = Dog()
cat = Cat()
cow = Cow()
duck = Duck()

print(animal_sound(dog))  # Woof!
print(animal_sound(cat))  # Meow!
print(animal_sound(cow))  # Moo!
print(animal_sound(duck))  # Quack!

2. 多态的条件

  • 继承关系:不同类有共同的父类

  • 方法重写:子类重写父类的方法

  • 向上转型:将子类对象赋值给父类类型的变量

    父类定义统一接口

    class Shape:
    def area(self):
    """计算面积的接口"""
    pass

    子类实现不同的面积计算

    def Circle(Shape):
    def init(self, radius):
    self.radius = radius

    复制代码
      def area(self):
          import math
          return math.pi * self.radius ** 2

    class Rectangle(Shape):
    def init(self, width, height):
    self.width = width
    self.height = height

    复制代码
      def area(self):
          return self.width * self.height

    class Triangle(Shape):
    def init(self, base, height):
    self.base = base
    self.height = height

    复制代码
      def area(self):
          return 0.5 * self.base * self.height

    多态函数:接收Shape类型的对象

    def print_area(shape):
    print(f"Area: {shape.area():.2f}")

    创建不同形状的对象

    shapes = [
    Circle(5),
    Rectangle(4, 6),
    Triangle(3, 8)
    ]

    同一接口,不同实现

    for shape in shapes:
    print_area(shape)

    输出:

    Area: 78.54

    Area: 24.00

    Area: 12.00

3. 抽象类与多态

使用抽象类定义接口,强制子类实现特定方法,确保多态的一致性:

复制代码
from abc import ABC, abstractmethod

# 抽象类:定义接口
class Payment(ABC):
    @abstractmethod
    def pay(self, amount):
        """支付接口"""
        pass

# 具体实现类
class CreditCardPayment(Payment):
    def __init__(self, card_number):
        self.card_number = card_number
    
    def pay(self, amount):
        return f"Paid ${amount} using credit card: {self.card_number}"

class PayPalPayment(Payment):
    def __init__(self, email):
        self.email = email
    
    def pay(self, amount):
        return f"Paid ${amount} using PayPal: {self.email}"

class BitcoinPayment(Payment):
    def __init__(self, wallet_address):
        self.wallet_address = wallet_address
    
    def pay(self, amount):
        return f"Paid ${amount} using Bitcoin: {self.wallet_address}"

# 多态函数
def process_payment(payment_method, amount):
    return payment_method.pay(amount)

# 使用不同的支付方式
payments = [
    CreditCardPayment("****1234"),
    PayPalPayment("user@example.com"),
    BitcoinPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")
]

for payment in payments:
    print(process_payment(payment, 100))

# 输出:
# Paid $100 using credit card: ****1234
# Paid $100 using PayPal: user@example.com
# Paid $100 using Bitcoin: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

三、继承与多态的关系

  1. 继承是多态的基础 :多态通常基于继承关系实现,子类重写父类方法

  2. 多态增强了继承的灵活性 :同一接口可以处理不同类型的对象

  3. 共同协作 :继承实现代码复用,多态实现灵活扩展

四、继承与多态的优势

继承的优势

  • 代码复用 :避免重复编写相同的代码

  • 功能扩展 :子类可以添加新功能

  • 层次结构 :建立清晰的类层次关系

多态的优势

  • 灵活性 :同一接口适应不同对象

  • 可扩展性 :新增类不需要修改现有代码

  • 可维护性 :代码结构清晰,易于理解和修改

五、面试习题

题目 1:选择题

在Python中,子类继承父类的语法是:

A. class SubClass inherits ParentClass:

B. class SubClass(ParentClass):

C. class SubClass extends ParentClass:

D. class SubClass -> ParentClass:
答案:B

解释: Python中子类继承父类的正确语法是 `class SubClass(ParentClass):`。

题目 2:选择题

多态的主要特点是:

A. 同一个方法在不同对象中表现出不同的行为

B. 不同的方法在同一个对象中表现出相同的行为

C. 相同的方法在不同对象中表现出相同的行为

D. 不同的方法在不同对象中表现出不同的行为
**答案:A**

**解释:** 多态的主要特点是同一个方法在不同对象中表现出不同的行为。

题目 3:判断题

子类可以重写父类的方法。(对/错)

多态可以通过方法重载实现。(对/错)
**答案:对**

**解释:** 子类可以重写父类的方法。

**答案:错**

**解释:** 在Python中,多态主要通过方法重写实现,而不是方法重载。方法重载是指在同一个类中定义多个同名但参数不同的方法,Python不支持这种方法重载。

编程题

请定义一个父类Animal,包含方法make_sound(),并定义两个子类Dog和Cat,分别重写make_sound()方法,输出"汪汪"和"喵喵"。然后,创建Dog和Cat的实例,调用它们的make_sound()方法,观察输出结果。

题目 4:选择题

在Python中,子类调用父类方法的正确方式是:

A. super().method_name()

B. self.super().method_name()

C. parent().method_name()

D. parent.method_name(self)
**答案:A**

**解释:** 在Python中,子类调用父类方法的正确方式是 `super().method_name()`。

题目 5:编程题

请定义一个父类Vehicle,包含方法describe(),并定义两个子类Car和Bicycle,分别实现describe()方法,输出不同的描述信息。然后创建Car和Bicycle的实例,调用它们的describe()方法。

示例:

Car输出:"这是汽车,四个轮子。"

Bicycle输出:"这是自行车,两个轮子。"

复制代码
class Vehicle:
    def describe(self):
        pass

class Car(Vehicle):
    def describe(self):
        print("这是汽车,四个轮子。")

class Bicycle(Vehicle):
    def describe(self):
        print("这是自行车,两个轮子。")

# 测试
car = Car()
bicycle = Bicycle()

car.describe()  # 输出:这是汽车,四个轮子。
bicycle.describe()  # 输出:这是自行车,两个轮子。

题目 6:选择题

多态性通常通过以下哪种方式实现?

A. 方法重载

B. 方法重写

C. 类的多重继承

D. 函数的嵌套
**答案:B**

**解释:** 多态性通常通过方法重写实现。

题目 7:判断题

在Python中,子类可以访问父类的私有属性。(对/错)
**答案:错**

**解释:** 在Python中,子类不能直接访问父类的私有属性(以双下划线开头的属性),但可以通过其他方式间接访问。

作者提问: 那其他方式是什么?

在Python中,子类不能直接访问父类的私有属性(以双下划线开头的属性),这是因为Python会对私有属性进行 名称修饰 (name mangling),将 __private_attr 重命名为 _类名__private_attr ,以避免命名冲突。

尽管如此,子类仍可以通过以下几种方式间接访问父类的私有属性:

方法一:通过父类的公共方法(推荐)

父类提供公共的getter/setter方法,子类通过调用这些方法访问私有属性。

复制代码
class Parent:
    def __init__(self):
        self.__private_attr = "Private data"
    
    # 公共getter方法
    def get_private_attr(self):
        return self.__private_attr
    
    # 公共setter方法
    def set_private_attr(self, value):
        self.__private_attr = value

class Child(Parent):
    def access_parent_private(self):
        # 通过父类的公共方法访问
        return self.get_private_attr()
    
    def modify_parent_private(self, new_value):
        # 通过父类的公共方法修改
        self.set_private_attr(new_value)

child = Child()
print(child.access_parent_private())  # Private data
child.modify_parent_private("New private data")
print(child.access_parent_private())  # New private data

方法二:通过名称修饰后的属性名直接访问

子类可以使用父类名称修饰后的属性名直接访问私有属性(不推荐,破坏封装性)。

复制代码
class Parent:
    def __init__(self):
        self.__private_attr = "Private data"

class Child(Parent):
    def access_parent_private(self):
        # 使用名称修饰后的属性名:_Parent__private_attr
        return self._Parent__private_attr
    
    def modify_parent_private(self, new_value):
        # 直接修改名称修饰后的属性
        self._Parent__private_attr = new_value

child = Child()
print(child.access_parent_private())  # Private data
child.modify_parent_private("Modified directly")
print(child.access_parent_private())  # Modified directly

方法三:通过super()调用父类的方法

子类可以使用 super() 函数调用父类的方法,间接访问父类的私有属性。

复制代码
class Parent:
    def __init__(self):
        self.__private_attr = "Private data"
    
    def _parent_method(self):
        # 父类的保护方法(单下划线)
        return f"Parent method accessing: {self.__private_attr}"

class Child(Parent):
    def access_through_super(self):
        # 通过super()调用父类的保护方法
        return super()._parent_method()

child = Child()
print(child.access_through_super())  # Parent method accessing: Private data

方法四:通过@property装饰器创建属性访问器

父类使用@property装饰器创建属性访问器,子类可以直接使用这些属性。

复制代码
class Parent:
    def __init__(self):
        self.__private_attr = "Private data"
    
    @property
    def private_attr(self):
        return self.__private_attr
    
    @private_attr.setter
    def private_attr(self, value):
        self.__private_attr = value

class Child(Parent):
    def access_property(self):
        # 直接使用@property定义的属性
        return self.private_attr
    
    def modify_property(self, new_value):
        # 使用@setter定义的属性赋值
        self.private_attr = new_value

child = Child()
print(child.access_property())  # Private data
child.modify_property("New value through property")
print(child.access_property())  # New value through property

题目 8:编程题

请定义一个父类Shape,包含一个方法area(),然后定义两个子类Circle(圆形)和Rectangle(矩形)。在每个子类中重写area()方法,分别计算圆形和矩形的面积。

提示:

圆形面积公式:π * r^2

矩形面积公式:length * width

复制代码
import math

class Shape:
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return math.pi * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, length, width):
        self.length = length
        self.width = width
    
    def area(self):
        return self.length * self.width

# 测试
circle = Circle(5)
rectangle = Rectangle(5, 3)

print(f"圆形的面积为: {circle.area():.2f}")  # 输出:圆形的面积为: 78.54
print(f"矩形的面积为: {rectangle.area()}")  # 输出:矩形的面积为: 15

题目 9:选择题

如果父类中的方法被子类重写,那么子类的实例将调用哪个版本的方法?

A. 父类的方法

B. 子类的方法

C. 依赖于调用顺序

D. Python会抛出错误
**答案:B**

**解释:** 如果父类中的方法被子类重写,那么子类的实例将调用子类的方法。

题目 10:编程题

编写一个Python程序,定义一个父类Shape,并在其中定义一个方法describe(),然后定义两个子类Circle和Square,它们分别重写describe()方法并提供不同的描述。最后,创建一个Shape对象和Circle、Square对象,调用它们的describe()方法。

示例输出:

Shape的describe()方法输出:"这是一个形状。"

Circle的describe()方法输出:"这是一个圆形。"

Square的describe()方法输出:"这是一个正方形。"

复制代码
class Shape:
    def describe(self):
        print("这是一个形状。")

class Circle(Shape):
    def describe(self):
        print("这是一个圆形。")

class Square(Shape):
    def describe(self):
        print("这是一个正方形。")

# 测试
shape = Shape()
circle = Circle()
square = Square()

shape.describe()  # 输出:这是一个形状。
circle.describe()  # 输出:这是一个圆形。
square.describe()  # 输出:这是一个正方形。

题目 11:选择题

以下关于继承的说法,哪个是正确的?

A. 子类不能访问父类的属性和方法

B. 子类可以继承父类的私有方法和属性

C. 子类可以继承父类的公有方法和属性

D. 子类必须重写父类的所有方法
**答案:C**

**解释:** 子类可以继承父类的公有方法和属性。

题目 12:判断题

多态只通过继承和方法重写来实现,而不能通过接口或抽象类来实现。(对/错)
**答案:错**

**解释:** 多态可以通过继承和方法重写来实现,也可以通过接口或抽象类来实现。Python中没有接口的概念,但可以通过抽象基类(ABC)来实现类似的功能。

相关推荐
好好学操作系统17 小时前
flash_attn ImportError undefined symbol:
开发语言·python
GHL28427109017 小时前
通义千问的 Function Call - demo学习
学习·ai·ai编程
知识分享小能手17 小时前
Ubuntu入门学习教程,从入门到精通,Ubuntu 22.04 中的服务器 —— 知识点详解 (22)
服务器·学习·ubuntu
꧁Q༒ོγ꧂17 小时前
算法详解(二)--算法思想基础
java·数据结构·算法
慎独41317 小时前
记忆力革命:学习力的核心与其目脑力的科技探索
科技·学习
꧁Q༒ོγ꧂17 小时前
算法详解(一)--算法系列开篇:什么是算法?
开发语言·c++·算法
橘颂TA17 小时前
【剑斩OFFER】算法的暴力美学——力扣:1047 题:删除字符串中的所有相邻重复项
c++·算法·leetcode·职场和发展·结构于算法
year--17 小时前
虚拟环境安装requirements.txt
python
_Kayo_17 小时前
Node.js 学习笔记6
笔记·学习·node.js