user2315423
user2315423

Reputation: 639

Python adjust the return type to consider the input types

In C++ and C# and others, you can overload or adjust the return type to match the input types. Using Python I find references that the return type should be consistent regardless. Like in this question

Now for my code that wants to match return type to param type

def myUpper(what_is_this_thing):
    try:
        return what_is_this_thing.upper()  
    except AttributeError:  
        try:
            return {k:what_is_this_thing[k].upper() for k in what_is_this_thing} 
        except TypeError:
        return [x.upper() for x in what_is_this_thing] 

print myUpper('test') 
print myUpper(['test', 'and more'])
print myUpper({'one':'test', 'two': 'and more'})
print myUpper(['test', 1000])

output

TEST
['TEST', 'AND MORE']
{'two': 'AND MORE', 'one': 'TEST'}
An exception is rased because the payload does not have a upper method

So how bad is this python sin? I mostly still work in 2.7 I know 3.3 has type hints, learning that will need to wait for later in the summer.

Anyone have a less sinful way to achieve most of the benefits? or a coherent argument why this should not be done?

Addendum:
Other than the Python3 Moses which I like. I feel compelled to find if this question is best answered with something like in python 2.7

def myUpper(s): 
    return s.upper() 
print myUpper('test') 
print [s.myUpper() for s in 'test', 'and more'] d = { 'one':'test', 'two': 'and more'} 
print {k:d[k].myUpper() for k in d}

In summary spread comprehension stuff out in the code even if is quite common. Choose proliferation of comprehension over obscure return data types?

I suspect I would remove 400+ comprehension lines in the final code if I did it with adjusting return types. But if that is too strange then so be it.

It comes down to readability ver violation of the unwritten rule about 1 function 1 return type.

Upvotes: 2

Views: 603

Answers (4)

Andriy Ivaneyko
Andriy Ivaneyko

Reputation: 22021

Putting my five cents there:

hanlders = {
    str: (lambda what_is_this_thing: what_is_this_thing.upper()),
    dict: (lambda what_is_this_thing: {k:what_is_this_thing[k].upper() for k in what_is_this_thing}),
    list: (lambda what_is_this_thing: [x.upper() for x in what_is_this_thing]), 
}
print handlers[type(what_is_this_thing)](what_is_this_thing)

Upvotes: 1

Moses Koledoye
Moses Koledoye

Reputation: 78546

If you're looking to have some consistency of the return type with the argument (precisely, first argument), you can create overloaded implementations of your function with functools.singledispatch; one of the reasons I'll say you start moving to Python 3:

from functools import singledispatch

@singledispatch
def my_upper(what_is_this_thing):
    return what_is_this_thing.upper()  

@my_upper.register(list)
def _(this_is_a_list):
    ...
    return this_is_also_a_list

@my_upper.register(dict)
def _(this_is_a_dict):
    ...
    return this_is_also_a_dict

Upvotes: 3

Mike Scotty
Mike Scotty

Reputation: 10782

You could use isinstance - and passing the keys / values of a dict as well as the items of a list recursively to the function will handle more types:

def myUpper(o):
    if(isinstance(o, str)):
        return o.upper()
    elif(isinstance(o, list)):
        return [myUpper(x) for x in o]
    elif(isinstance(o, dict)):
        return {myUpper(k):myUpper(v) for k,v in o.items()}
    else:
        return o

Upvotes: 0

You can actually check the type instead of relying on exceptions.

v = 'one'
if type(v) == str:
    # Treat it as a string
elif type(v) == list:
    # Treat it as a list
elif type(v) == dict:
    # Treat is as a dict

Upvotes: 0

Related Questions