51

Inspired by a great question (and bunch of great answers) from here.

Does the statement "Code against an interface, not an object" have any significance in Python?

I'm looking for answers like the ones in the Original Question but with Python snippets and thoughts.

Community
  • 1
  • 1
Jeffrey Jose
  • 1,992
  • 2
  • 16
  • 21
  • 3
    Just don't think *interface* means the same as *interface* in Java or C#. It is something that can be applied to any object oriented language but maybe in different ways. And maybe you cannot enforce it in the code but the concept itself is still applicable. – Felix Kling Dec 28 '10 at 19:57
  • 2
    No, python doesn't have interfaces. Until recently it doesn't have abstract classes. And even so, neither are necessary. Coding for an interface doesn't make sense in python because you don't have to declare a variable's type before assigning or using it. – Falmarri Dec 28 '10 at 19:59
  • 4
    @Falmarri: In a way you *do* code against an interface if you rely on duck typing. Because you assume that e.g. an object that is passed to your method has method A, B and C and you don't care about anything else. You just cannot enforce this by code. At least, this is how I see it, but maybe this a too philosophical or ideological discussion ;) – Felix Kling Dec 28 '10 at 20:05
  • @Felix: Yes, that's what the language does. But YOU'RE not coding against an interface because there is no interface. Coding against an interface in Java guarantees that objects of a type that implements that interface have those methods. This is not guaranteed in python. – Falmarri Dec 28 '10 at 20:08
  • @Falmarri: I see your point. You are talking more about the practical implications whereas I'm referring to the concept. Anyway, I think both points are valid :) – Felix Kling Dec 28 '10 at 20:13
  • 3
    @Falmarri: Of course it's not guaranteed, and of course there is not Python equivalent to "interface" as "statically checked contract about members". But outside of the type system, on a conventional level, we do use something that could be called "interfaces" - `map` expects a callable and an iterable, not something derived from a hypothetical `FunctionClass` or `BaseIterable`. It's just implicit. –  Dec 28 '10 at 20:13
  • Both abstract base classes and zope.interface can be used to have interfaces in Python. Buuuuuuuuuut, more significantly, everything has an implied interface. Which is why kindalls answer below is correct. Coding against an interface in Python means to use duck typing. – Lennart Regebro Dec 28 '10 at 20:27
  • @Falmarri just because your code did not use some special keyword to indicate that you're describing and interface, does not mean that there is no interface. 'iterable' and 'file-like' and 'callable' are common examples of "interfaces" in Python. – Karl Knechtel Dec 28 '10 at 21:20

4 Answers4

69

"Code against an interface, not an object" doesn't make literal sense in Python because the language doesn't have an interface feature. The rough Python equivalent is "use duck typing." If you want to see if an object is a duck, in other words, you should check to see whether it has a quack() method, or better yet try to quack() and provide appropriate error handling, not test to see if it is an instance of Duck.

Common duck types in Python are files (well, really, file-like objects), mappings (dict-like objects), callables (function-like objects), sequences (list-like objects), and iterables (things you can iterate over, which can be containers or generators).

As an example, Python features that want a file will generally be happy to accept an object that implements the methods of file it needs; it needn't be derived from the file class. To use an object as standard out, for example, the main thing it is going to need is a write() method (and maybe flush() and close(), which needn't actually do anything). Similarly, a callable is any object that has a __call__() method; it needn't be derived from the function type (in fact, you can't derive from the function type).

You should take a similar approach. Check for the methods and attributes you need for what you're going to do with an object. Better yet, document what you expect and assume that whoever is calling your code is not a total doofus. (If they give you an object you can't use, they will certainly figure that out quickly enough from the errors they get.) Test for specific types only when necessary. It is necessary at times, which is why Python gives you type(), isinstance(), and issubclass(), but be careful with them.

Python's duck typing is equivalent to "code against an interface, not an object" in the sense that you're advised not to make your code too reliant on an object's type, but rather to see whether it has the interface you need. The difference is that in Python, "interface" just means an informal bundle of attributes and methods of an object that provide a certain behavior, rather than a language construct specifically named interface.

You can formalize Python "interfaces" to some extent using the abc module, which allows you to declare that a given class is a subclass of a given "abstract base class" (interface) using any criteria you desire, such as "it has attributes color, tail_length, and quack, and quack is callable." But this is still much less strict than static languages having an interface feature.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • 3
    +1 Note though that ideally, you only need `isinstance` with an abstract base class as type. `type` is really evil - it doesn't take subtyping into account! –  Dec 28 '10 at 20:26
  • 4
    It is fair to say that "code against an interface, not an object" is by default in Python unless you *explicitly* use `type()`, `isinstance()`, etc. – jfs Dec 28 '10 at 20:27
  • @delnan, +1 for mentioning `type()` is evil. – Jeffrey Jose Dec 29 '10 at 07:54
  • @kindall: Why do we prefer to avoid the `isinstance()` and prefer to raise an exception to be handled? – JavaSa Jun 23 '15 at 11:04
  • Because there's no reason to reject an object that will do what you need it to do just because of its pedigree. – kindall Jun 23 '15 at 14:34
31

To understand interfaces in Python you have to understand duck-typing. From the very Python glossary:

duck-typing: A programming style which does not look at an object’s type to determine if it has the right interface; instead, the method or attribute is simply called or used (“If it looks like a duck and quacks like a duck, it must be a duck.”) By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). (Note, however, that duck-typing can be complemented with abstract base classes.) Instead, it typically employs hasattr() tests or EAFP programming.

Python encourages coding for interfaces, only they are not enforced but by convention. Concepts like iterables, callables or the file interface are very pervasive in Python - as well the builtins that rely on interfaces like map, filter or reduce.

Paulo Scardine
  • 73,447
  • 11
  • 124
  • 153
  • 7
    EAFP stands for Easier to Ask Forgiveness than Permission. – Paulo Scardine Dec 28 '10 at 20:38
  • 8
    “If it looks like a duck, and quacks like a duck, we have at least to consider the possibility that we have a small aquatic bird of the family anatidae on our hands.” –Douglas Adams – Alex Brasetvik Dec 28 '10 at 21:14
19

An interface means you expect certain methods to be present and standardised across objects; that is the point of an interface or abstract base class, or whatever implementation you wish to consider.

For example (Java), one might have an interface for symmetric encryption like so:

public interface cipher 
{
    public void encrypt(byte[] block, byte[] key); 
    public void decrypt(byte[] block, byte[] key);    
}

Then you can implement it:

public class aes128 implements cipher
{ 
    public void encrypt(byte[] block, byte[] key)
    {
        //...
    }
    public void decrypt(byte[] block, byte[] key)
    {
        //...
    }
}

It is then possible to declare an object like so:

cipher c;

What have we done here? Well, we've created this object c whose type must match that of the interface. c can refer to anything that matches this interface, so the next stage would be:

c = new aes128();

You can now call methods you expect a cipher to have.

That's java. Now here's what you do in python:

class aes128(Object):

    def __init__(self):
        pass

    def encrypt(self, block, key):
        # here I am going to pass, but you really 
        # should check what you were passed, it could be 
        # anything. Don't forget, if you're a frog not a duck
        # not to quack!
        pass

When you want to use this, and you're not sure that the object you've been passed is, just try to use it:

c = aes128()
try:
    c.encrypt(someinput, someoutput)
except:
    print "eh? No encryption method?!"

Here, you're relying on c.encrypt's implementation to raise if it can't handle what it has been passed, if the method exists. Of course, if c is a string type and therefore not of the right type you require, it will also throw automatically, and you will catch (hopefully).

In short, one form of programming is typed such that you have to obey the interface rules, the other is saying you don't even need to write them down, you simply trust that if it didn't error, it worked.

I hope that shows you the practical difference between the two.

  • 1
    That's a nice addition to the more conceptual answers to this question. – kindall Dec 28 '10 at 20:46
  • 2
    Nitpick: in Python, `throw` is spelled `raise`. :) – Karl Knechtel Dec 28 '10 at 21:21
  • @Kark sssssh you can tell I've been writing too much java/c++ recently!! Thanks, edited in. –  Dec 28 '10 at 21:26
  • Thanks a lot for the excellent explanation. The only places I've seen interfaces are in zope and twisted (IAbstractFactory .. et al) in others' code. I've never used any of those in my code myself. Was wondering whether I was not doing it right. – Jeffrey Jose Dec 29 '10 at 07:39
  • Nice answer. This probably means programming against interface can be approximately achieved in python by programming against abstract class. And there is an abstract class module in python. See here(http://stackoverflow.com/questions/13646245/is-it-possible-to-make-abstract-classes-in-python). – liang Jan 02 '14 at 14:51
1

What's the Python version for “Code against an interface, not an object.”?

The proper quote is "program against an interface, not an implementation". The principle holds the same way in Python that it did in Smalltalk, the language it in which it originated.

Does the statement "Code against an interface, not an object." have any significance in Python?

Yes. It has the same significance in Python as it has in the language that this quote originated from (Smalltalk), and in every other language.

Keith Pinson
  • 7,835
  • 7
  • 61
  • 104
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 9
    I don't care for your condescending tone. – martineau Dec 28 '10 at 23:00
  • 5
    @martineau: As you may or may not have deduced from my name, English is not my native language. If there is anything that I may have erroneously phrased in such a way that you perceived it as condescending, I apologize and humbly ask for your help in improving my answer. – Jörg W Mittag Dec 29 '10 at 00:05
  • 2
    Yes, you can do what the quote says in any language. But as other answers correctly points out, the right way in Python to do these things is to leverage the power of `duck-typing`. You're right in asking how can Python version be any different from SmallTalk, but preferred approach might be (is?) different. – Jeffrey Jose Dec 29 '10 at 07:42