1

How can I assign the results of a function call to multiple variables when the results are stored by name (not index-able), in python.

For example (tested in Python 3),

import random

# foo, as defined somewhere else where we can't or don't want to change it
def foo():
    t = random.randint(1,100)
    # put in a dummy class instead of just "return t,t+1" 
    # because otherwise we could subscript or just A,B = foo()
    class Cat(object):
        x = t
        y = t + 1

    return Cat()

# METHOD 1
# clearly wrong; A should be 1 more than B; they point to fields of different objects
A,B = foo().x, foo().y
print(A,B)

# METHOD 2
# correct, but requires two lines and an implicit variable
t = foo()
A,B = t.x, t.y
del t # don't really want t lying around
print(A,B)

# METHOD 3
# correct and one line, but an obfuscated mess
A,B = [ (t.x,t.y) for t in (foo(),) ][0]
print(A,B) 
print(t) # this will raise an exception, but unless you know your python cold it might not be obvious before running

# METHOD 4
# Conforms to the suggestions in the links below without modifying the initial function foo or class Cat.
# But while all subsequent calls are pretty, but we have to use an otherwise meaningless shell function
def get_foo():
    t = foo()
    return t.x, t.y

A,B = get_foo()

What we don't want to do

If the results were indexable ( Cat extended tuple/list, we had used a namedtuple, etc.), we could simply write A,B = foo() as indicated in the comment above the Cat class. That's what's recommended here , for example.

Let's assume we have a good reason not to allow that. Maybe we like the clarity of assigning from the variable names (if they're more meaningful than x and y) or maybe the object is not primarily a container. Maybe the fields are properties, so access actually involves a method call. We don't have to assume any of those to answer this question though; the Cat class can be taken at face value.

This question already deals with how to design functions/classes the best way possible; if the function's expected return value are already well defined and does not involve tuple-like access, what is the best way to accept multiple values when returning?

Community
  • 1
  • 1
en_Knight
  • 5,301
  • 2
  • 26
  • 46

1 Answers1

3

I would strongly recommend either using multiple statements, or just keeping the result object without unpacking its attributes. That said, you can use operator.attrgetter for this:

from operator import attrgetter
a, b, c = attrgetter('a', 'b', 'c')(foo())
user2357112
  • 260,549
  • 28
  • 431
  • 505