0

I would like to make a function that can take a single element or a list as arguments.

def Density(m, *vol):
    """
    Inputs:
    m    is the mass.
    vol  is the volume.
    Output:
    rho  is the density.
    """
    for _ in vol:
        if isinstance(vol, float):
            rho = m / _
        elif isinstance(vol, (list,)):
            rho = m / _ * 1000
        else:
            raise ValueError('It is neither a float nor a list, apparently!')
    return rho

So, if I define a float and a list

flt = 0.25
lst = [0.25, 0.26, 0.24]

and try to pass it through Density

a = Density(50, flt)
b = [Density(50, _) for _ in lst]

the error I created appears.

  • What am I doing wrong?
naughty_waves
  • 265
  • 1
  • 13
  • 2
    stop using `_` in your loops! `_` is a symbol that means "I'm not going to use this value." Name it something. – Adam Smith Jun 06 '19 at 16:35
  • in the second attempt, you're not passing `lst` to the `Density` function. – David Zemens Jun 06 '19 at 16:35
  • 1
    Possible duplicate of [Use of \*args and \*\*kwargs](https://stackoverflow.com/questions/3394835/use-of-args-and-kwargs) – meowgoesthedog Jun 06 '19 at 16:37
  • if you do `Density(50, lst)` the type is tuple. – David Zemens Jun 06 '19 at 16:38
  • If you are doing math-y type things to the value that you pass in, I HIGHLY suggest using `numpy` arrays. In fact, if you use `numpy` arrays instead of lists, that will clear up this whole mess, and it won't be necessary to distinguish between them, since you can math `numpy` arrays... – Him Jun 06 '19 at 16:38
  • Thank you, guys, for setting me straight. – naughty_waves Jun 06 '19 at 19:21
  • @Scott, out of curiosity, how would you implement this as an `numpy` array? – naughty_waves Jun 07 '19 at 09:10
  • I am sorry, but it is not at all clear what you are attempting to accomplish, so I wouldn't presume to try and implement your class myself. However, if you define a function `def foo(m, vol):`, then you do not need to loop over the items in `vol` in order to perform multiplication if it is a `numpy` array, so there should be no need to distinguish between floats and arrays. For example if `vol = numpy.array([0.25, 0.26, 0.24])`, then `m / vol * 1000` is well-defined, and is equal to a new array containing all of the elements of `vol`, divided into `m` and then multiplied by 1000. Try it. – Him Jun 09 '19 at 17:39

3 Answers3

2

When you send arguments with this: *vol syntax, they are stored not in lists, but in tuples:

Finally, the least frequently used option is to specify that a function can be called with an arbitrary number of arguments. These arguments will be wrapped up in a tuple (see Tuples and Sequences). Before the variable number of arguments, zero or more normal arguments may occur.

Moreover, because of Density(m, *vol): syntax vol is always a tuple. So you should check its length. If it is equal to 1, it is a float. If it is more than one - it is a "list".

You should change your loop to this:

    for v in vol:
        if len(vol) == 1:
            rho = m / v
        elif len(vol) > 1:
            rho = m / v * 1000
        else:
            raise ValueError('It is neither a float nor a list, apparently!')

P.S. Your last line is incorrect. Instead of this:

b = [Density(50, _) for _ in lst]

you should write this:

b = [Density(50, *lst)]

vurmux
  • 9,420
  • 3
  • 25
  • 45
  • I noticed that it actually does not work as intended, as it is not able to identify the `float` segment and thus also multiplies `flt` with `1000`. Do you have a solution? I also added `rho = [] in front of the `for` loop and altered `rho = m / _ * 1000` to `rho.append(m / _ * 1000)`. – naughty_waves Jun 07 '19 at 10:01
  • Thank you again, @vurmux! You have no idea how much I appreciate your help! – naughty_waves Jun 07 '19 at 10:22
1

Debug your function by adding this print command and you will see that the parameter vol is not a list.

print(type(vol))

Thus change your code from;

OLD: elif isinstance(vol, (list,)):
NEW: elif isinstance(vol, (tuple,)):
jose_bacoy
  • 12,227
  • 1
  • 20
  • 38
1

Just to add to already good answer by vumux I would check for base class of tuple and a list like so:

elif issublclass(vol, collections.abc.Collection)

or

elif issublclass(vol, collections.abc.Iterable)

Depengding on your specific needs of course, so for example you know you can iterate through what was passed.

So you can pass anything that inherits from a collection

CodeSamurai777
  • 3,285
  • 2
  • 24
  • 42