I have a function like this:
def foo(bar):
...
Now bar can either be a Django record or the slug pointing to a record. I need this flexibility so that I can call this function when I have the actual record at hand or am calling this from a more generic function that only has bar available as a string (pulled from a database).
I realize I can do something like:
def foo(bar):
try:
bar.pk
except AttributeError:
bar = Bar.objects.get(slug=bar)
...
But this doesn’t seem elegant at all.
If I can I’d like to avoid using isinstance.
You are by definition not using Duck Typing. Duck Typing says that “if it talks like a duck and looks like a duck, it’s a duck.”
Duck Typing means you can pass two objects of completely different classes to the method and have it work because they both implement the same methods/attributes (or handles missing ones gracefully). This means that the method never cares about about what type it gets, just that whatever object you pass it has the attributes it expects to use.
In your case you want to pass an object sometimes and a string that can be used to look up said object other times. This has nothing to do with Duck Typing.
isinstance is the right way to solve this. In this case this is the clearest way to solve the problem, anything else is going to be more complicated, harder to understand have 0 benefits. You could use try/except on an attribute or hasattr, but that’s likely to leave any future developers more confused than anything else. Duck Typing is great it replaces casting various subclasses around to match some particular function, but duck typing doesn’t apply in this case.
In short. Just use isinstance. For your case it’s the right (pythonic) way to do it.