面向对象编程中的继承与多态是实现代码复用、扩展和灵活设计的核心机制:
一、继承(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 = radiusdef area(self): import math return math.pi * self.radius ** 2class Rectangle(Shape):
def init(self, width, height):
self.width = width
self.height = heightdef area(self): return self.width * self.heightclass Triangle(Shape):
def init(self, base, height):
self.base = base
self.height = heightdef 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:选择题
在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)来实现类似的功能。