Reputation: 5993
Let's say we have the following classes:
class Duck(object):
pass
class OldFashionedDuck(Organism, Duck):
def look(self):
self.display_biological_appearance()
def walk(self):
self.keep_balance_on_two_feet()
def quack(self):
self.make_noise_with_lungs("Quack!")
class ArtificialDuck(Robot, Duck):
def look(self):
self.display_imitation_biological_appearance()
def walk(self):
self.engage_leg_clockwork()
def quack(self):
self.play_sound("quack.au")
In this example, OldFashionedDuck and ArtificialDuck have no common implementation, but by construction they will both return True for isinstance(..., Duck).
This is not perfect, but it is something that I thought might help respect both duck typing, and (via empty mixin inheritance) allow isinstance(). In essence it offers a contract to meet an interface, so it's not really calling isinstance() based on the class that does all the work, but based on an interface that anyone can opt-in to.
I've seen articles based on "isinstance() considered harmful," because it breaks duck typing. However, I at least as a programmer would like to know, if not necessarily where an object gets a function from, but whether it implements an interface.
Is this approach useful, and if so can it be improved upon?
Upvotes: 1
Views: 835
Reputation: 12755
Even though I don't want to overestimate this term, but: Your approach is not pythonic. Do duck-typing, or don't do it.
If you want to be sure that your implementation of a "interface" implements everything it should: test it!
For smaller projects, it's easy to remember what you need. And you can simply try it.
I agree that for larger projects, or even cooperation in teams, it's better to make sure that your type has everything it needs. In such a scenario, you definitely should use unit-tests to make sure your type is complete. Even without duck-typing, you need tests, so probably you won't need any extra-tests.
Guido van Rossum has talked about some interesting thoughts about duck-typing in this talk. It's so inspiring, and definitely worth watching.
Upvotes: 2
Reputation: 61526
I've seen articles based on "isinstance() considered harmful," because it breaks duck typing. However, I at least as a programmer would like to know, if not necessarily where an object gets a function from, but whether it implements an interface.
I think you're missing the point.
When we talk about "duck typing", what we really mean is not formalizing our interfaces. What you're trying to do, therefore, boils down to attempting to answer the question "how can I formalize my interface while still not formalizing my interface?".
We expect an object that was given to us to implement an interface - one that we described, not by making a base class, but by writing a bunch of documentation and describing behaviour (and if we're feeling especially frisky, setting up some kind of test suite) - because we said that this is what we expect (again in our documentation). We verify that the object implements the interface by attempting to use it as though it does, and treating any resulting errors as the caller's responsibility. Calling code that gives us the wrong object is naughty, and that's where the bug needs to be fixed. (Again, tests help us track these things down.)
In short, testing isinstance(this_fuzzball_that_was_handed_to_me, Duck)
doesn't really help matters:
It could pass the isinstance
check, but implement the methods in a way that violates our expectations (or, say, return NotImplemented
). Only real testing will do here.
It could pass the check, but actually completely fail to implement one or more of the methods; after all, the base Duck
doesn't contain any implementations, and Python has no reason to check for them in a derived class.
Perhaps more importantly, it could fail the check, even though it's a perfectly usable-as-a-duck fuzzball. Maybe it's some unrelated object that had quack
, walk
and look
functions directly, manually attached to it as attributes (as opposed to them being attributes of its class, which become methods when looked up).
Okay, so, "don't do that", you say. But now you're making more work for everyone; if clients don't always opt in, then it's useless and dangerous for the duck-using code to make the check. And meanwhile, what are you gaining?
This is related to the EAFP principle: don't try to figure out whether something is a Duck by looking at it; figure out if it's a Duck by treating it as a Duck and dealing with the gory mess if it isn't.
But if you don't care about the duck typing philosophy, and absolutely must force some semblance of rigor onto things, you might be interested in the standard library abc
module.
Upvotes: 6