Python 之 接口和多态引发的血案

事故发生地

在学习 Python 面向对象视频 之 鸭子类型与多态 时看到这样两句话不得其解:

非动态语言必须通过继承和接口实现多态
动态语言不需要实现接口


菜鸟的问题

(非动态语言以 Java 为例,动态语言以 Python 为例)
1. 接口和多态是什么关系?
2. Python 为什么没有接口?没有接口怎么实现接口对应的设计模式
3. Python 为什么没有重载?
4. Python 的多态是什么样的?


接口和多态是什么关系?

在我的理解中,多态具有两种形式,重载和重写:

重载在同一个类中,相同的方法名对应着不同的方法实现,其区别在于他们需要的参数和返回值不同。
重写用于父类和子类间,子类重写父类的方法,只是对应的方法实现不同,其方法名和参数都相同。

这样看来,重写是基于继承的。那么接口呢?好像除了强制要求子类实现某些方法,我不知道还有什么别的作用。
正因如此,接口只是一个规范。在多个类都实现一个接口的时候他的作用就体现出来了,我们不需要知道在某个类中这个方法的具体实现,只需要这个类中有这个方法而我们可以直接调用就可以了。有了这样一个统一的标准,我们就不必担心同样功能的方法被起了各种各样的名字了。
所以,我觉得可以用这么一句话回答这个问题,接口不是实现多态的,而是基于多态的,有了多态接口才能发挥作用


Python 为什么没有接口?没有接口怎么实现接口对应的设计模式

接口的作用就是规范,规范参数类型,返回值等等,从而使相同的调用能够实现不同的效果;并且接口也是为了用来弥补语言自己表达能力的不足,如:Java 只支持单继承,为了使类拥有更多的特性,使用了多实现来弥补。静态的语言必须全部都规定好才能正确使用。
而在 Python 中,变量是没有类型的,不需要事先规定,不管调用者传入什么类型的对象,被调用者就会认为那就是我所需要的对象(鸭子类型),如果在运行时传入的对象不具备需要的属性或方法,程序会直接报错。需要实现这个方法的话,就照实现就是了,返回和形参不限制类型,比如:在创建了类的实例后,动态地给该实例加上一个函数属性。这就是动态类型的优势。
这种风格被称为“鸭子类型”:

在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。
在我想成为鸭子的时候,不是我必须真的是个鸭子,而是我的行为表现的像是个鸭子,那我就是个鸭子

相关概念 - “鸭子测试”:

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
“换言之,不要检查它是不是一个鸭子:检查它像不像一个鸭子地叫,等等。取决于你需要哪个像鸭子的行为的子集来使用语言。”


Python 为什么没有重载?

函数重载主要是为了解决两个问题:
1. 可变参数类型。
2. 可变参数个数。

另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
对于情况 1,函数功能相同,但是参数类型不同,Python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 Python 中很可能是相同的代码,没有必要做成两个不同函数。
对于情况 2 ,函数功能相同,但参数个数不同,Python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,Python 自然就不需要函数重载了。


Python 的多态是什么样的?

多态即多种形态,在运行时确定其状态,在编译阶段无法确定其类型,这就是多态。
1)Python是解释性语言。不进行预编译,因此它就只在运行时确定其状态;
2)Python中变量是弱类型的。在定义时不用指明其类型,它会根据需要在运行时确定变量的类型。


结语:一些粗浅之语,有长进再补充。


Reference:

Java 中的接口有什么作用?
Python 里没有接口,如何写设计模式?
为什么 Python 不支持函数重载?
Python面向对象编程(二)
鸭子类型

### Python接口多态的实现方式及使用方法 #### 1. 多态的基础原理 在 Python 中,多态的核心理念基于动态类型系统鸭子类型[^1]。这意味着只要一个对象能够响应所需的方法调用,无论它的具体类型是什么,都可以认为它是有效的。这种灵活的设计使得 Python多态性更加直观且易于实现。 --- #### 2. 使用继承实现多态 通过继承机制,子类可以覆盖父类中的方法以提供特定的功能实现。当通过父类引用访问这些方法时,实际执行的是子类中定义的行为。 ```python # 定义一个基类 class Animal: def talk(self): raise NotImplementedError("Subclasses must implement this method") # 子类覆写talk方法 class Dog(Animal): def talk(self): return 'Woof!' class Cat(Animal): def talk(self): return 'Meow!' # 测试多态行为 def animal_speak(animal: Animal): print(animal.talk()) dog = Dog() cat = Cat() animal_speak(dog) # 输出 Woof! animal_speak(cat) # 输出 Meow! ``` 在这里,`Animal` 类作为一个抽象基类提供了统一的接口 `talk()`,而具体的实现则由各个子类负责完成。这种方式体现了典型的多态特征[^3]。 --- #### 3. 利用鸭子类型简化设计 由于 Python 支持鸭子类型,因此并不严格要求显式的继承关系也能达到类似的多态效果。只要有相同名称的方法就可以正常工作。 ```python class Laptop: def charge(self): return "Charging laptop..." class Phone: def charge(self): return "Charging phone..." def device_charge(device): return device.charge() laptop = Laptop() phone = Phone() print(device_charge(laptop)) # 输出 Charging laptop... print(device_charge(phone)) # 输出 Charging phone... ``` 此例说明即便 `Laptop` `Phone` 没有任何关联,但因为两者都有名为 `charge` 的方法,所以仍然可以通过通用函数 `device_charge` 来操作它们。 --- #### 4. 接口的隐式表达 Python 并不像一些静态语言那样拥有正式的接口关键字,但是可以通过 ABCs(Abstract Base Classes) 或协议(protocol) 来模拟接口的概念。ABCs 可用于强制某些类必须实现特定的方法才能实例化。 ```python from abc import ABC, abstractmethod class DeviceInterface(ABC): @abstractmethod def connect(self): pass @abstractmethod def disconnect(self): pass class BluetoothDevice(DeviceInterface): def connect(self): return "Bluetooth connected." def disconnect(self): return "Bluetooth disconnected." bluetooth_device = BluetoothDevice() print(bluetooth_device.connect()) # 输出 Bluetooth connected. print(bluetooth_device.disconnect()) # 输出 Bluetooth disconnected. try: interface_instance = DeviceInterface() # 尝试直接实例化会抛出异常 TypeError except Exception as e: print(e) # 输出 Can't instantiate abstract class ... ``` 这里展示了一个简单的设备连接断开的例子,其中 `DeviceInterface` 是一个抽象基类,只有在其所有抽象方法都被具体实现在子类之后才可以正常使用[^4]。 --- #### 5. 协议与数据类的应用 从 Python 3.8 开始引入的数据类(data classes),配合协议的支持,可以让开发者更方便地构建具有固定属性集合的对象,并自动获得诸如比较、字符串表示等功能。同时结合 typing 模块中的 Protocol ,可以进一步强化类型提示的同时保留灵活性。 ```python from dataclasses import dataclass from typing import Protocol class SpeakerProtocol(Protocol): volume_level: int def adjust_volume(self, level_change: int) -> None: ... @dataclass class Radio(SpeakerProtocol): volume_level: int def adjust_volume(self, level_change: int): self.volume_level += level_change print(f'Radio Volume adjusted to {self.volume_level}') radio = Radio(volume_level=50) radio.adjust_volume(-10) # 输出 Radio Volume adjusted to 40 ``` 这段代码片段演示了如何利用协议来描述期望的行为模式而不必关心确切的继承链路[^2]。 --- ### 总结 综上所述,Python 提供了多种途径来达成多态的目标,既可以依赖传统的继承结构也可以充分利用其独特的动态特性如鸭子类型等手段。无论是简单应用还是复杂架构下都能找到适合自己的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值