Is it Pythonic to mimic method overloading?
Is it pythonic to mimic method overloading as found in statically typed languages? By that I mean writing a function that checks the types of its arguments 开发者_StackOverflow社区and behaves differently based on those types.
Here is an example:
class EmployeeCollection(object):
@staticmethod
def find(value):
if isinstance(value, str):
#find employee by name and return
elif isinstance(value, int):
#find employee by employee number and return
else:
raise TypeError()
Not very Pythonic, except perhaps, in 2.6 or better, if all the checks rely on the new abstract base classes, which are intended in part exactly to facilitate such use. If you ever find yourself typechecking for concrete classes, then you know you're making your code fragile and curtailing its use.
So, for example, checking if you have an instance of numbers.Integral is not too bad -- that new ABC exists in good part exactly to ease such checking. Checking if you have an instance of int
is a disaster, ruling out long
, gmpy.mpz
, and a bazillion other kinds of integer-like numbers, to absolutely no good purpose: never check for concrete classes!
Strings are a difficult case, but the basestring abstract class (not one of the new kind of ABCs) is a possibility. A bit too restrictive, perhaps, but if you're using other ABCs around it, it might kinda, sorta work, if you really have to. Most definitely not str
-- why ever rule out unicode
?!
Not really, since you lose the ability to use types that are not-quite-that-but-close-enough. Create two separate methods (find_by_name()
and find_by_number()
) instead.
No, type checking here isn't Pythonic. Another option if you don't fancy multiple methods is to stick with one method but use multiple parameters:
def find(name=None, employee_number=None):
if sum(x != None for x in (name, employee_number)) != 1:
#raise exception - exactly one value should be passed in
if name is not None:
#find employee by name and return
if employee_number is not None:
#find employee by employee number and return
When used the intent is as obvious as with multiple methods:
employee1 = x.find(name="John Smith")
employee2 = x.find(employee_number=90210)
I would say yes, it is 'Pythonic' And there are examples to back this up (which the other posters have not given). To answer this properly there should be examples!
From python core:
string.startswith()
It accepts either a string or a tuple (of strings).string.endswith()
In pymongo:
find_one()
accepts either a dict object to lookup, or it will use another other object as the id.
Sorry I don't know more, but I think there are MANY examples of methods that behave differently according to the parameters given. That is part of the beauty of not enforcing types.
A more pythonic way of getting to this kind of functionality is to just try and use it in the preferred way (whatever that might mean) and if the argument doesn't support that, try an alternative.
Here'd two ways to do that:
class EmployeeCollection(object):
def find(value):
try:
#find employee by name and return
catch:
try:
#find employee by employee number and return
catch:
raise TypeError()
but thats kind of yucky. here's how I usually do this:
class EmployeeCollection(object):
def find(value):
if hasattr(value, 'join'):
#find employee by name and return
elif hasattr(value, '__div__'):
#find employee by employee number and return
else:
raise TypeError()
In reality, the actual attribute I'd check for depends on what happens in those comments, I'll prefer to check for an attribute that I actually use.
精彩评论