Question

I'm a C++/C# developer and recently I've started to learn Python. I think I understand the concept of duck typing right now except for one thing.

When I'm coding in C++ or C# I sometimes create completely unrelated classes that have similar methods with similar signatures, for example: Create(), Open(), Close(), ProcessData(), etc. It's not a big deal in C++ or C# (unless you want to mess with C++ templates), but in languages with duck typing like Python this could cause a disaster. Consider the following example:

class DbTransaction:
    def rollback(self):
        pass

class SourceControlChangeset:
    def rollback(self):
        pass

def rollback_transaction(transaction):
    transaction.rollback()

transaction = DbTransaction()
changeset = SourceControlChangeset()
rollback_transaction(transaction)
rollback_transaction(changeset) # Oops!

Maybe be example is a bit artificial, but I think this could happen pretty easily in a big project with many classes.

What should I do in this situation? I think that type checking is not "pythonic" so my only idea is to change naming convention for class methods like this:

class DbTransaction:
    def rollback_transaction(self):
        pass

class SourceControlChangeset:
    def rollback_changeset(self):
        pass

Any ideas?

Was it helpful?

Solution

In Clean Code, Robert Martin argues that even with static typing, you should avoid what he calls "punning," meaning giving the same name to two methods that aren't semantically similar. That means if you consider transaction.rollback and changeset.rollback to be semantically different, you should try to choose another name for one of them, whether you are doing duck typing or not.

In your particular example the name makes the semantics of each case obvious, even though the context is different, so it's okay to leave the name the same. You're mostly concerned with preventing accidentally passing one kind of object into a context meant for the other.

To be blunt, that's the trade-off you make when choosing duck typing. You increase the risk of using the wrong type, which requires somewhat more testing, but in exchange you gain increased reusability with less hassle. If you want the benefits of strong static typing, you should choose your language and architecture accordingly. You can't have your cake and eat it too.

OTHER TIPS

The unfortunate side effect of Duck Typing is that sometimes it isn't a duck, even if it walks like a duck and quacks like a duck. Duck Typing only works in limited areas of similarity.

Your example, however, is itself a problem. You should never rollback_transaction(transaction), you should always transaction.rollback(). If you have a valid reason to wrap the behavior of a method, you should do it "objectally", not procedurally, by creating a wrapper object whose method wraps the inner object's method.

Licensed under: CC-BY-SA with attribution
scroll top