python - 类与对象 - 构造函数、重写、super函数、多态

构造函数

在Python里,__init__ 函数是一个特殊的方法,也被叫做构造函数,它主要用在类的对象创建过程中对对象进行初始化操作。这也意味着,类在实例化时也支持个性化定制,只需要在定义类的同时定义构造函数即可。

通俗类比

想象我们有一家蛋糕店,每一款蛋糕都有自己固定的配方,比如草莓蛋糕,需要用到面粉、鸡蛋、奶油和草莓等材料。当顾客下单要一个草莓蛋糕时,店员就得按照这个固定配方去准备材料并制作蛋糕。

在Python的类和对象的世界里,这个蛋糕店就像是一个类,每一个蛋糕就是类创建出来的对象,而 __init__ 函数就相当于这个蛋糕的配方,它规定了创建对象时需要哪些“材料”(也就是参数),并把这些“材料”放到对象里,让对象有自己的初始状态。

class Cake:
    def __init__(self, flour, eggs, cream, strawberries):
        # 这里的self代表创建出来的蛋糕对象本身
        self.flour = flour
        self.eggs = eggs
        self.cream = cream
        self.strawberries = strawberries

    def describe(self):
        print(f"这个蛋糕用了 {self.flour} 克面粉,{self.eggs} 个鸡蛋,{self.cream} 克奶油和 {self.strawberries} 颗草莓。")

# 创建一个草莓蛋糕对象
strawberry_cake = Cake(200, 3, 150, 5)
# 调用对象的describe方法来描述蛋糕
strawberry_cake.describe()
  1. class Cake 定义了一个名为 Cake 的类,它就像蛋糕店,规定了蛋糕的制作规则。
    • def __init__(self, flour, eggs, cream, strawberries)Cake 类的 __init__ 函数。这里的 self 代表将来创建出来的蛋糕对象本身,而 floureggscreamstrawberries 就是创建蛋糕所需的“材料”(参数)。
    • 在函数内部,self.flour = flour 等语句把传入的参数值赋给对象的属性,这样对象就有了自己的初始状态。
  2. strawberry_cake = Cake(200, 3, 150, 5) 这行代码创建了一个 Cake 类的对象 strawberry_cake,并按照 __init__ 函数的要求传入了相应的参数,就像按照配方准备材料制作蛋糕一样。
  3. strawberry_cake.describe() 调用了对象的 describe 方法,输出蛋糕的信息。

重写

如果对父类中的属性或方法不满意的话,可以在子类中写一个同名的去覆盖掉它,这就叫子类对父类的重写。这样,既不需要改动原本父类,又可以拓展子类的新特性。
打个比方说,我们有个汽车工厂,生产过基础款汽车。现在,我们造出了新的升级款,在基础款的基础上改变了鸣笛的声音,真是一个了不起的升级。。

# 定义父类:基础款汽车
class BasicCar:
    def __init__(self):
        print("基础款汽车初始化完成")

    # 定义父类的鸣笛方法
    def honk(self):
        print("普通的鸣笛声音:嘀嘀")

# 定义子类:升级版汽车
class UpgradedCar(BasicCar):
    def __init__(self):
        # 直接通过父类名调用初始化方法,即未绑定的父类方法
        BasicCar.__init__(self)
        print("升级版汽车额外初始化完成")

    # 重写父类的鸣笛方法
    def honk(self):
        print("升级版的鸣笛声音:嘟嘟嘟")

# 创建基础款汽车对象
basic = BasicCar()
# 调用基础款汽车的鸣笛方法
basic.honk()


# 创建升级版汽车对象
upgraded = UpgradedCar()
# 调用升级版汽车的鸣笛方法
upgraded.honk()

ps:未绑定的父类方法就是,父类里定义了的方法但没有和具体的对象关联起来。可以把它想象成一个还没放到具体物品上执行的操作步骤。在 Python 里,我们可以手动把一个对象传递给这个未绑定的方法,让它去操作这个对象。

钻石继承

钻石继承也叫菱形继承,因继承关系连接形似菱形而得名。最简单的钻石继承结构就是一个父类、两个子类,两个子类又共同被新一个子类继承。

想象一下,有一个基础“生物”类,其类下由“怪兽”和“人类”两个子类继承;再然后,有一个“兽人”类,同时继承“怪兽”和“人类”这两个类。这就构建了一个最简单的钻石继承:

# 定义最顶层的基类:生物
class Creature:
    def __init__(self):
        print("Creature 初始化")

    def action(self):
        print("生物的通用行为")

# 定义从 Creature 派生的人类类
class Human(Creature):
    def __init__(self):
        Creature.__init__(self)
        print("Human 初始化")

    def use_weapon(self):
        print("人类使用武器")

# 定义从 Creature 派生的怪物类
class Monster(Creature):
    def __init__(self):
        Creature.__init__(self)
        print("Monster 初始化")

    def attack_style(self):
        print("怪物野蛮攻击")

# 定义兽人类,继承自 Human 和 Monster
class Orc(Human, Monster):
    def __init__(self):
        Human.__init__(self)
        Monster.__init__(self)
        print("Orc 初始化")

# 创建一个兽人对象
orc = Orc()

>>>Creature 初始化
Human 初始化
Creature 初始化
Monster 初始化
Orc 初始化

从输出我们可以看到,很明显的问题:Creature 的初始化方法被调用了两次。在 Human中1次, 在Monster 中一次。显然,调用是重复冗余了的。那么如何避免这个问题?

super函数

在多继承中,super() 会按照方法解析顺序(MRO)来调用父类的方法。MRO 是 Python 为每个类计算出的一个线性顺序,规定了在查找方法时的搜索顺序,这样就可以避免查找进程的重复或混乱。

用上面兽人的例子改写,就得到:

 # 定义最顶层的基类:生物
class Creature:
    def __init__(self):
        print("Creature 初始化")

    def action(self):
        print("生物的通用行为")

# 定义从 Creature 派生的人类类
class Human(Creature):
    def __init__(self):
        super().__init__()
        print("Human 初始化")

    def use_weapon(self):
        print("人类使用武器")

# 定义从 Creature 派生的怪物类
class Monster(Creature):
    def __init__(self):
        super().__init__()
        print("Monster 初始化")

    def attack_style(self):
        print("怪物野蛮攻击")

# 定义兽人类,继承自 Human 和 Monster
class Orc(Human, Monster):
    def __init__(self):
        super().__init__()
        print("Orc 初始化")

# 创建一个兽人对象
orc = Orc()

>>>Creature 初始化
Monster 初始化
Human 初始化
Orc 初始化

这样输出就没有重复了。
同时,在一些场景下,如有必要,super() 函数可以接受两个参数:super(type, object_or_type)。

  • type:指定从哪个类开始查找父类方法。
  • object_or_type:指定要查找方法的对象或类。

多态

多态是指同一个运算符、函数、对象等在不同场景下能展现出不同的作用。也就是说,使用相同的接口,却可以根据不同的对象执行不同的操作。

运算符多态

运算符在不同的数据类型上可以有不同的行为。例如,+ 运算符:

  • 当用于数字类型(如整数、浮点数)时,它执行加法运算:
num1 = 5
num2 = 3
result = num1 + num2
print(result)  # 输出: 8
  • 当用于字符串类型时,它执行字符串拼接操作:
str1 = "Hello"
str2 = " World"
result = str1 + str2
print(result)  # 输出: Hello World

内置函数多态

一些内置函数可以处理不同类型的对象,并根据对象类型执行不同的操作。以 len() 函数为例:

  • 当传入字符串时,它返回字符串中的字符个数:
string = "Python"
print(len(string))  # 输出: 6
  • 当传入列表时,它返回列表中的元素个数:
my_list = [1, 2, 3, 4, 5]
print(len(my_list))  # 输出: 5
  • 当传入字典时,它返回字典中键的个数:
my_dict = {'a': 1, 'b': 2, 'c': 3}
print(len(my_dict))  # 输出: 3

类继承与多态

在类的继承体系中,多态通常通过方法重写来实现。子类可以重写父类的方法,从而在调用相同方法时,根据对象的实际类型执行不同的操作。

class Animal:
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        print("汪汪汪")

class Cat(Animal):
    def make_sound(self):
        print("喵喵喵")

# 创建不同的动物对象
dog = Dog()
cat = Cat()

# 调用相同的方法,根据对象类型执行不同操作
dog.make_sound()  # 输出: 汪汪汪
cat.make_sound()  # 输出: 喵喵喵

自定义函数实现多态接口

要让自定义函数实现多态接口,可以通过以下几种方式:

基于继承和方法重写

定义一个基类,其中包含一个方法,然后让不同的子类重写这个方法。自定义函数接收基类对象作为参数,在调用该方法时,会根据对象的实际类型执行相应的操作。

class Shape:
    def area(self):
        pass

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

    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        import math
        return math.pi * self.radius ** 2

def calculate_area(shape):
    return shape.area()

# 创建不同的形状对象
rect = Rectangle(5, 4)
circle = Circle(3)

# 调用自定义函数,根据对象类型计算不同形状的面积
print(calculate_area(rect))  # 输出: 20
print(calculate_area(circle))  # 输出: 约 28.27

基于鸭子类型(Duck Typing)

鸭子类型是一种编程风格,它不关注对象的具体类型,而是关注对象是否具有特定的方法或属性。只要对象具有所需的方法,就可以被传递给函数进行处理。

class Person:
    def speak(self):
        print("我会说话")

class Robot:
    def speak(self):
        print("我会发出电子声音")

def let_it_speak(obj):
    obj.speak()

# 创建不同的对象
person = Person()
robot = Robot()

# 调用自定义函数,根据对象的方法执行不同操作
let_it_speak(person)  # 输出: 我会说话
let_it_speak(robot)  # 输出: 我会发出电子声音

通过上述方式,自定义函数可以实现多态接口,能够处理不同类型的对象,提高代码的灵活性和可扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值