Python duck typing (or automatic interfaces)

Written by martim00 | Published 2017/09/26
Tech Story Tags: programming | python | duck-typing | python-duck-typing | automatic-interfaces

TLDRvia the TL;DR App

Working with python full-time now I, more often, have insights about its way of doing things (or Zen of python for some people). One thing that blows my mind every time I find out in my daily saga is the duck typing thing. Being honest, it is not a python exclusive feature, since almost every dynamic language presents that behavior. But be nice, I like Python.

If some reader doesn’t know what it is, duck typing is a feature of a type system where the semantics of a class is determined by his ability to respond to some message (method or property). The canonical example (and the reason behind the name) is the duck test: If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.

class Duck:def quack():print('Quack!')

class Goose:def quack():print('Quack')

Goose().quack()>> Quack!

Duck().quack()>> Quack!

Ok, but what is the catch? There is nothing new here. Yes, nothing new. What I’m trying to show is the implications that this behavior has in the architecture of a system. And I’m advocating that this is a good implication. Let me explain.

Imagine a class named Car:

class Car:def __init__(self, engine):self.engine = engine

def run():self.engine.turn_on()

This is a classical example of dependency injection. My class Car receives an instance of an engine and use it in the run method, where it calls the turn_on method. Note that my Car does not depends on any concrete implementation of engine. And I'm not importing any other type! Just using a dependency injected instance of something that responds to a turn_on message. I could say my class Car depends on an interface. But I did not have to declare it. It is an automatic interface!

In a language without duck typing I'll probably have to declare an explicit interface named for example IEngine, have the implementation (for example EngineV1) and explicit define my Car parameter to be an implementation of 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();  
}

}

Ugh! How much code.

So, as you can see the effect is the same. My class Car in both cases depends on an interface. But in the first case there are less code and I don't need to explicit implement the interface. It's already implemented if I define the method turn_on.

The weakness

I can see two problems here.

  1. Fat interface

The first is an incentive to interface bloat. As we don’t explicit define the API this can result in an interface with too much granular methods.

2. Unnamed dependencies

As we don’t know the name of the interfaces that a class depends we don’t have an automatic dependency tree. Because of this, all dependency injection frameworks have to workaround the dependency resolution in some way.

Injector lib, for example, was obligated to implement the concept of Keys to allow dependency tree to be resolved automatically. Keys, in injector terms, are nothing more than a way to name/identify an interface that a class implements.

Conclusion

I don't think that duck typing was intentionally designed to have this behavior. I don't even think that duck typing is intentionally designed in a language at all but instead it is a side-effect of the dynamic nature of those type systems. But it is interesting what it can brings in terms of conciseness and low-coupling to the system design and architecture.


Published by HackerNoon on 2017/09/26