Questions about duck typing for a design problem

I have an argument with a colleague about what Duck Typing really is. Well, I know the theory for sure: “If it walks like a duck and it quacks like a duck, then it must be a duck”. But I still have many questions, mainly because all the tutorials and blog posts one can find on the topic use trivial code like this to showcase the concept:

class Duck:
    def quack(self):
        print("Quack")

class Dog:
    def quack(self):
        print("Woof! Err... pardon me, quack")

def make_quacker_quack(quacker):
    quacker.quack()

Is “having the same method/property name” enough to consider it duck typing? Consider this not so trivial code here:

class Polygon:
    sides: list[int]

    def perimeter():
        return sum(sides)

class PolygonArray:
    polygons: numpy.ndarray[Polygon]

    def perimeter():
        return numpy.ndarray([p.perimeter() for p in self.polygons])

def print_perimeter(something_with_perimeter):
    print(something_with_perimeter.perimeter())

Sure, a plain print function works. But what if I want to do something with the .perimeter()? Some users might suddenly assume that .perimeter() is an array, and try things like .perimeter().mean(). It looks to me then that duck typing only goes as far as your user requirements and expectations go, but I might be completely misunderstanding something.

The reason why this argument came up is that in poliastro, an open source library for Astrodynamics I develop, we have an Orbit class with several geometric properties (.a, .ecc, .inc) that are scalars, and we are working to introduce an OrbitArray, that would contain an array of such orbits and perform some specialized parallel computations. My colleague proposes making OrbitArray essentially have the same properties and methods as Orbit, while I am not at all convinced: I see the convenience of it, but on the other hand I’m worried about user code (and even our own code) starting to grow isinstance and hasattr checks all over the place. Sure, “the user should know what they’re doing”, but this looks to me like it’s begging for trouble.

My question is: are we doing a favor to our users by offering a similar interface on both (hence: to have OrbitArray.inc return an ndarray of inclinations), or are we shooting ourselves in the foot? And also, do folks have some literature, resources, or advanced material to draw some inspiration from? (In case it isn’t obvious, I’m a self-taught programmer with a bit knowledge gap when it comes to software engineering, OOP design, SOLID principles, and such.)

1 Like

That’s an interesting question. While Wikipedia might not be the ultimate authority on this topic, let’s see what they have to say.

In duck typing, an object is of a given type if it has all methods and properties required by that type.

The above suggests that a Dog instance and a Duck instance from your first example are of equivalent types.

However, earlier, the article has this:

“If it walks like a duck and it quacks like a duck, then it must be a duck”

Well, a Dog does have a quack method, as does a Duck, but the Dog does not quack in the same manner as a Duck. So, there seems to be a contradiction between the two cited criteria.

EDIT (January 14, 2022):

… however, with regard to this, we might consider a subtlety concerning its meaning:

… has all methods and properties required by that type.

We might argue that the required quack method for a Duck is one that has it making a quacking sound, and that while the Dog does have a quack method, it does not have the required quack method.

IMO, duck typing involves the same names and “akin” return types. So a
.inc which now returns an array instead of a scaler isn’t duck typing.
Whereas a .size which returned an int in one class and a float in
another might be close enough for me, since these can be used in most of
the same places.

So I’d be:

  • against a .inc returning an array
  • but for a .incs returning an array if .inc-like values

i.e. have the name grammaticly different in a way which matches the
change in “type shape”.

… and have the names be this similar because they’re accessing the
same aspect of an orbit, aiding inuition.

Cheers,
Cameron Simpson cs@cskk.id.au