现在,我全职使用python ,对它的工作方式(或某些人的python禅)有更深入的了解。 每当我发现自己的日常传奇时,都会让我大吃一惊的是鸭子打字。 老实说,它不是python独有的功能,因为几乎每种动态语言都具有这种行为。 但是,很好,我喜欢Python。
如果某些读者不知道它是什么,鸭子类型是类型系统的功能,其中类的语义由其对某种消息(方法或属性)的响应能力决定。 典型的例子(以及名称背后的原因)是鸭子测试 : 如果它看起来像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那可能就是鸭子。
class Duck:
def quack():
print('Quack!')
class Goose:
def quack():
print('Quack')
Goose().quack()
>> Quack!
Duck().quack()
>> Quack!
好的,但是有什么收获呢? 这里没有新内容。 是的,没有新内容。 我试图展示的是这种行为对系统体系结构的影响。 我主张这是一个很好的暗示。 让我解释。
想象一下一个名为Car的类:
class Car:
def __init__(self, engine):
self.engine = engine
def run():
self.engine.turn_on()
这是依赖注入的经典示例。 我的汽车类接收一个引擎实例,并在run方法中使用它,在该方法中调用turn_on方法。 请注意,我的汽车不依赖于引擎的任何具体实现。 而且我不会导入任何其他类型! 仅使用响应Turn_on消息的依赖项注入实例。 我可以说我的班级Car取决于接口。 但是我不必声明它。 这是一个自动界面!
在没有鸭子类型的语言中,我可能必须声明一个名为IEngine的显式接口,实现(例如EngineV1 ),并显式定义Car参数为IEngine的实现。
interface IEngine {
void turnOn();
}
public class EngineV1 implements IEngine {
public void turnOn() {
// do something here
}
}
public class Car {
public Car(IEngine engine) {
this.engine = engine;
}
public void run() {
this.engine.turnOn();
}
}
啊! 多少代码。
因此,您可以看到效果是相同的。 在这两种情况下,我的班级Car都取决于接口。 但是在第一种情况下,代码更少,我不需要显式实现接口。 如果定义方法turn_on,则已经实现。
弱点
我在这里看到两个问题。
- 胖界面
首先是鼓励膨胀 。 由于我们未明确定义API,因此这可能导致接口中包含过多的粒度方法。
2.未命名的依赖项
由于我们不知道类所依赖的接口的名称,因此我们没有自动依赖树。 因此,所有依赖项注入框架都必须以某种方式解决依赖项解析。
例如, Injector lib有义务实现Keys的概念,以允许自动解析依赖关系树。 用喷射器的术语来说,键不过是命名/标识类实现的接口的一种方法。
结论
我不认为鸭子的输入是故意设计的,以具有这种行为。 我什至根本不认为鸭子类型是故意用某种语言设计的,而是它是那些类型系统的动态特性的副作用。 但是有趣的是,它可以为系统设计和体系结构带来简洁性和低耦合性。
From: https://hackernoon.com/python-duck-typing-or-automatic-interfaces-73988ec9037f