O que é?

Duck typing é um estilo de codificação de linguagens dinamicamente tipadas onde o tipo de uma variável não importa, contanto que seu comportamento seja o desejado. O nome "tipagem de pato" vem da expressão "se anda como pato, nada como um pato e faz quack como um pato, então provavelmente é um pato".

Quando criamos uma função ou um método em Python o tipo do parâmetro não é a parte importante, mas o que realmente importa é se o mesmo vai possuir os métodos e atributos esperados:

def realizar_operacao(operacao, **kwargs):
    operacao.realizar(**kwargs)

A função realizar_operacao é um exemplo de como duck typing é aplicado: ela recebe como parâmetro o objeto operacao e executa o método realiza sem se importar com o tipo do objeto.

É interessante notar que realizar_operacao utiliza a variável mágica **kwargs, que recebe qualquer número de parâmetros nominais após passado o objeto referente à operação, não importando quais sejam.

Essa função pode ser implementada para realizar uma transferência, como vemos a seguir:

transferencia = Transferencia()
realizar_operacao(
    transferencia,
    conta_origem='14725-8',
    conta_destino='85296-3',
    valor=10000
)

A função também pode ser implementada para realizar um depósito simples:

deposito = Deposito()
realiza_operacao(
    deposito,
    conta_destino='85296-3',
    valor=10000
)

Melhor pedir perdão que permissão

Quando estamos trabalhando com tipagem estática, o comum é utilizar uma interface como um contrato, definindo os atributos e métodos obrigatórios, e esperar que o objeto sendo recebido seja do tipo de uma classe que implemente esta interface, garantindo que o objeto que estamos recebendo tenha o comportamento esperado.

Observe o exemplo em Java a seguir:

public interface Pagamento {

    void realizar();
}

public class Deposito implements Pagamento {

    @Override
    public void realizar() {
        System.out.println("Deposito::realizar");
    }
}


public class Transferencia implements Pagamento {

    @Override
    public void realizar() {
        System.out.println("Transferencia::realizar");
    }
}

Veja que são criadas a interface de Pagamento, que define o contrato das classes que devem ter o método realizar, e duas classes que a implementam.

public class Billing {

    public void realizarPagamento(Pagamento operacao) {
        operacao.realizar();
    }
}

Criamos a classe Billing que recebe um objeto que implementa a interface Pagamento e executa o método realizar deste objeto.

public class Main {
    public static void main(String args[]) {
        Billing billing = new Billing();
        Deposito deposito = new Deposito();
        Transferencia transferencia = new Transferencia();
        billing.realizarPagamento(deposito);
        billing.realizarPagamento(transferencia);
    }
}

Por fim é implementado um objeto da classe Billing que realiza uma transferência e um pagamento.

Esse tipo de abordagem evita erros, como executar um método no objeto recebido que não existe. Como Python não possui suporte a interfaces, a abordagem para prevenir problemas como executar um método que não existe em um objeto segue outro tipo de prática: a EAFP.

EAFP (easier to ask for forgiveness than permissione), do inglês "Melhor pedir perdão que permissão", é um estilo de codificação que envolve assumir que os comportamentos desejados em um determinado objeto existem e qualquer problema relacionado é tratado como uma exceção, assim o código se mantém limpo, conciso e legível.

A seguir temos o exemplo prévio traduzido em Python:

class Deposito:
    def realizar(self):
        print("Deposito::realizar")

class Transferencia:
    def realizar(self):
        print("Transferencia::realizar")

Temos as classes das operações praticamente idênticas ao exemplo em Java:

class Billing:
    def realizar_pagamento(self, operacao):
        try:
            operacao.realizar()
        except AttributeError:
            print('a operação não pôde ser realizada')

O método realizar_pagamento da classe Billing recebe a operação como parâmetro, porém não espera um tipo específico, mas sim executa dentro de uma declaração try/except: se o método não existir, uma exceção do tipo AttributeError será disparada e capturada pelo except, então uma mensagem será impressa informando que há um problema.

def main():
    billing = Billing()
    deposito = Deposito()
    transferencia = Transferencia()
    billing.realizar_pagamento(deposito)
    billing.realizar_pagamento(transferencia)


     if __name__ == "__main__":
    main()

Por fim, implementamos as classes em nossa função main da mesma forma que no exemplo em Java.

Conclusão

Python é uma linguagem singular com toda uma forma de programar e uma cultura interna, o jeito "pythônico", de como escrever o código e implementar boas práticas. É interessante ressaltar que essa cultura de boas práticas agrega uma importância fundamental à legibilidade do código.

Vale citar que tanto a comunidade quanto o próprio time de desenvolvimento seguem a filosofia deixada por um de seus criadores: o Zen do Python, que pode ser acessado ao digitar o comando import this em um terminal interativo do Python:

The Zen of Python, by Tim Peters

    Bonito é melhor que feio.
    Explícito é melhor que implícito.
    Simples é melhor que complexo.
    Complexo é melhor que complicado.
    Linear é melhor do que aninhado.
    Disperso é melhor que denso.
    Legibilidade conta.
    Casos especiais não são especiais o bastante para quebrar as regras.
    Ainda que praticidade vença a pureza.
    Erros nunca devem passar silenciosamente.
    A menos que sejam explicitamente silenciados.
    Diante da ambiguidade, recuse a tentação de adivinhar.
    Deveria haver um -- e de preferência só um -- modo óbvio para fazer algo.
    Embora esse modo possa não ser óbvio a princípio a menos que você seja
    holandês.
    Agora é melhor que nunca.
    Embora nunca frequentemente seja melhor que já.
    Se a implementação é difícil de explicar, é uma má ideia.
    Se a implementação é fácil de explicar, pode ser uma boa ideia.
    Namespaces são uma grande ideia -- vamos ter mais dessas!