0

here is a question about programming in general and more precisely about python: I want to pass a parameter to a function which will sometimes be a type, and sometimes an array of that same type. I would also like to write the body of the function to do something to that one element when it's only one element, and to do it pointwise to every element and return a list of them when it's an array.

Edit: let me add that for now I am using

def foo(cards):
    if isinstance(cards, list) == False :
            cards = [cards]

    for card in cards:
        my_stuff

But it doesn't look too pythonic to me and I was wondering if there was another way using unpacking *, or some other operator. Thanks!

redfiloux
  • 113
  • 6

3 Answers3

2

This is a nice example of polymorphism

You can try to iterate it and if fails assume that is a single value

def somefunc(value):
    try:
        for v in value:
            dosomething(v)
    except TypeError:
        dosomething(value)

You can also use isinstance to test if is a list

def somefunc(value):
    if isinstance(value, list):
        for v in value:
             dosomething(v)
    else:
        dosomething(value)

First approach is more pythonic and more general too

-- Edit --

Just extending my answer. The first example is more general because it tries to iterate over the value instead of trying go check it's type. There are much more things that are iterable in python other than lists, for example, iterating over dics, gives you keys, iterating over files, give you lines, iterating over generators give you generated values, and so on. When you inspect for a type, only that type and its subtypes would work, but when try to iterate over it, anything that implements the iterator protocol will work.

This is what is behind Ask for forgiveness is better than ask for permission principle. By being a duck typed language, trying for behavior is more general than subtyping, I hope that helps

geckos
  • 5,687
  • 1
  • 41
  • 53
  • 1
    Also see [how do I determine if an object is iterable?](https://stackoverflow.com/q/1952464/5987). – Mark Ransom Nov 01 '19 at 02:06
  • Edge case, but the try version wouldn't work if v happens toimplements __iter__() – Mars Nov 01 '19 at 02:18
  • Mars, don't? I would need to call `iter(value)` on it? – geckos Nov 01 '19 at 02:24
  • This works fine for me: https://gist.github.com/dhilst/d7f4b0f9624c746bc238c33fad511949 – geckos Nov 01 '19 at 02:27
  • Not crashing doesn't mean working. Your code prints 0-9, but it should print `<__main__.HasIter object at 123123213123>` – Mars Nov 01 '19 at 04:20
  • In other words, even though *value* was an object, that object also implements iter, so runs through the same path as a list – Mars Nov 01 '19 at 04:21
  • Ah, sorry, I said that was an edge case, but a common "iterable" that we probably wanted to print as a whole, would be a string! – Mars Nov 01 '19 at 04:24
1

One approach to method overloading in Python:

def foo(item):
    if type(item) == list:
        print("do list jobs")
    else:
        print("do other jobs)

In other strongly typed languages you'd have to declare two versions of foo with different parameter lists so the compiler would know which one to call.

neutrino_logic
  • 1,289
  • 1
  • 6
  • 11
1

Python doesn't do method overriding, so you have to separate it yourself. One way to do this somewhat cleanly is to use one wrapper function to handle the input, and another for the actual processing.

You could try this:

def foo(cards):
    if not isinstance(cards, list):
            cards = [cards]

    for card in cards:
        _foo(card)

def _foo(card):
    my_stuff

Personally, I prefer using positive bools when I can, and would change the order to this:

def foo(cards):
    if isinstance(cards, list):    #Removed "not"
        for card in cards:
            _foo(card)
    else:
        _foo(cards) #Note: cards is singular here!

def _foo(card):
    my_stuff

Mars
  • 2,505
  • 17
  • 26
  • 1
    Your first version is what I'm currently doing, and since no other answer necessarily seems better, I'll accept this asnwer. Thanks – redfiloux Nov 01 '19 at 22:36