~ Being an account of the adventures (fictional & otherwise) of Jay Loomis, esq.

Duck Typing and Python

I recently learned about the term duck typing, as it pertains to Python (and, I gather, to some other programming languages as well). If you’re not familiar, the idea is to evaluate an object’s suitability for use by the methods it exposes rather than by its type. The name comes from the adage (paraphrased): if it quacks like a duck, walks like a duck, and swims like a duck, it’s a duck.

When you write a function or method in Python, the convention is that you should use duck typing. Rather than calling type(x) to validate a parameter, you should test the functionality of the object. Obviously, duck typing is for complex classes, not for Python’s built-in types. If, for example, you need a string, checking to see whether a parameter is a string is still the right call in most cases.

But, enough talk, time for examples. Take a look at the following two classes:

Both of these classes have a method called speak. The Human class defines a pseudo-constructor that takes a nationality, while no pseudo-constructor is defined for the Dog class. Note that the signatures of the speak methods of the two classes are identical.

The first function, talk_to uses traditional static type checking. It will only work with an object of type Human.

The second function, flex_talk_to does not use a static type check. Instead, it tries the speak method of the object passed to it, and raises a type error if the object doesn’t have such a method. Note that AttributeError is the exception that Python raises whenever you try to access an attribute that an object doesn’t have (a reminder that a method is really just an attribute that happens to be a function). In this example, we catch the attribute error and raise a type error, because the catch is serving the purpose of a type check.

The simplest case is pi. Either function will raise a type error, because an integer is not type Human, and it does not have a speak method.

Both jacques and hildy will work fine with either function. And spot will work with flex_talk_to, but will result in a type error is passed to talk_to.

As you’ve no doubt worked out, the flex_talk_to function is using duck typing. As long as the object that you pass to it has a speak method, it behaves as though the object is the type that we wanted.

Now imagine that you have an object with a speak method that does something other than print out a message. You can no doubt see that duck typing can potentially cause problems. Of course, how likely are those problems to arise? If you’re documenting your functions properly, it should be clear what the requirements for them are—you should never be passing objects to functions on faith alone. In any case, I’m not here to discuss the relative merits of static type checking versus duck type checking. That’s a philosophical issue you’ll have to come to terms with on your own.

Duck typing seems to be in keeping with Python’s philosophies and quirks to me. Rather than questioning the practice, I want to say a few words about how duck typing exemplifies Python’s take on some common programming concepts. Duck typing is the dynamic type checking solution to the problem that is solved by interfaces (or protocols, depending on what language you’re using) in statically-typed languages. An interface is the signature of an object’s functionality that ensures that the object can interact with other objects that consume and rely on that same functionality. An interface is a formal way of saying that you don’t care what object you’re dealing with, or how it does things under the covers, as long as it interacts with other objects in predictable ways. Duck typing is far less formal, perhaps frighteningly so.

A lot of Python’s philosophy seems at first to me like a leap of faith. As a language, it doesn’t clutter up its syntax with formalities. It relies instead on the notion that programmers are going to act in good faith and use their own code properly most of the time. For someone like me, who was trained in an older school of programming, the lack of formality and static definition can cause some intellectual vertigo: the language feels chaotic and unfixed. But I think that my uneasiness is largely unfounded. Not only are most of the potentially vague or ambiguous bits of Python’s syntax typically clear in practice, that vagary may encourage programmers to clarify with helpful comments, which are more concise and human-readable than interface definitions and the like. Also, no amount of formal definition will ever substitute for complete documentation and description of your functions and objects. The preference for duck typing is just another way that Python seems to trust reality over formality. It encourages you not to try to ward yourself against every improbable pitfall, but to write flexible, readable code with proper documentation. And that’s a good thing in my book.