16

Is there a way to have a default value if the number of values to unpack is too little compared to the variable list?

For example:

a, b, c = read_json(request)

This works if read_json returns an array of three or more variable. If it only returns two, I get an exception while assigning c. So, is there a way to set c to a default value if it can't be unpacked properly? Something like:

a, b, (c=2) = read_json(request)

Which is similar to what you do when defining a function with default arguments.

Thank you!

Laurent
  • 409
  • 4
  • 10
  • 1
    is `read_json()` your own function? If so, why cannot you make sure it always returns 3 values? – Anand S Kumar Aug 12 '15 at 06:43
  • No, there isn't; you need to make sure you get three values back, or pad and slice on the right-hand side of the assignment. – jonrsharpe Aug 12 '15 at 06:47
  • 1
    You could go with `*` unpacking, e.g. `a, b, *c = read_json(request)`, where unpacking only two values would result in `c` being an empty `list`, but if you really need specific values, it's better to have the function `return` something a little more rigorous. – TigerhawkT3 Aug 12 '15 at 06:50
  • 1
    @TigerhawkT3 and only in Python 3.x – jonrsharpe Aug 12 '15 at 06:55
  • @jonrsharpe - of course in Python 3... everyone uses Python 3... right? Please? :P – TigerhawkT3 Aug 12 '15 at 07:00
  • @AnandSKumar yes, it's my own function. I cannot make it always return three values since the number of values it returns is variable and dependent on the input. – Laurent Aug 12 '15 at 07:17
  • @ldc it doesn't stop you from forcing the function to return 3 items. – Eli Korvigo Aug 12 '15 at 08:59

4 Answers4

23

You could try * unpacking with some post-processing:

a, b, *c = read_json(request)
c = c[0] if c else 2

This will assign a and b as normal. If c is assigned something, it will be a list with one element. If only two values were unpacked, it will be an empty list. The second statement assigns to c its first element if there is one, or the default value of 2 otherwise.

>>> a, b, *c = 1, 2, 3
>>> c = c[0] if c else 2
>>> a
1
>>> b
2
>>> c
3
>>> a, b, *c = 1, 2
>>> c = c[0] if c else 2
>>> a
1
>>> b
2
>>> c
2
TigerhawkT3
  • 44,764
  • 6
  • 48
  • 82
5

You can use chain function from itertools, which is part of the Python standard library. It serve as default filler in case if there are no values in the first list. 'defaults' list variable in my example can have number of different values for each variable that you unpack (in an example I have default value for all three values as 0).

from itertools import chain

defaults = [0] * 3
data = []

a, b, c, *_ = chain(data, defaults)
print(a, b, c)

data.append(1)
a, b, c, *_ = chain(data, defaults)
print(a, b, c)

data.append(2)
a, b, c, *_ = chain(data, defaults)
print(a, b, c)

data.append(3)
a, b, c, *_ = chain(data, defaults)
print(a, b, c)

data.append(4)
a, b, c, *_ = chain(data, defaults)
print(a, b, c)

Outputs:

0 0 0
1 0 0
1 2 0
1 2 3
1 2 3
Vlad Bezden
  • 59,971
  • 18
  • 206
  • 157
  • Why not go the whole hog and limit the number of items with islice() rather than *_? – Ian Goldby Apr 07 '20 at 12:47
  • 1
    Unfortunately this is only working if all values of defaults are the same, because chain starts to supplement from the beginning of `defaults` – M. Beining Jul 28 '20 at 09:26
0

In answer to the question no you can't do that.

Furthermore I would recommend against returning different numbers of arguments from functions - this will only cause compilcations further issues down the line (this question case in point). Everytime you call that function you will need to test if there were 2 or 3 values returned. (Unpacking could be useful here, but you will still need to check those returned variables). eg:

a, b, *others = read_json(request)
if others:
    c = others[0]

It would make more sense, assuming read_json is your function, if the function can return a dict with the default values set:

def read_json(request):
    ret = { 'c': 2 }
    # ... set 'a' and 'b' and 'c' if possible

    return ret

res = read_json(request)
c = res['c']
Richard
  • 2,757
  • 1
  • 17
  • 29
0

i know i am late but it is worth mentioning.

if all default values you want are the same, you can use this little trick:

def return_tuple():
    return (1, 2)

def return_list():
    return [1, 2]

a, b, c, *_ = return_tuple() + (0,)
print(a, b, c)
# 1 2 0

a, b, c, *_ = return_list() + [0]
print(a, b, c)
# 1 2 0

a, b, c, d, *_ = return_list() + [0, 0]
print(a, b, c, d)

you can have default values for other types:

a, b, c, d, *_ = list(range(1, 3)) + [0, 0]
print(a, b, c, d)

However, if default values are not same:

defaults = [10, 11, 12, 13]
result = [1, 2]
a, b, c, d, *_ = result + defaults[len(result):]
print(a, b, c, d)
# 1, 2, 12, 13

# other types
result = range(2)
a, b, c, d = list(result) + defaults[len(result):]
print(a, b, c, d)
# 0 1 12 13

hope someone else will find this useful

Drdilyor
  • 477
  • 1
  • 4
  • 14