3

I have a function

def foo(a):
    first_thing = 'first' + a
    second_foo =  'second' + a + 'bar'
    return first_thing, second_foo

which returns tuples. How can I achieve something like

class Thing(object):
    def __init__(self, a):
        first_thing, second_foo = foo(a)
        self.first_thing = first_thing
        self.second_foo = second_foo

in a nicer and more automated fashion?

I experimented with:

def __init__(self, a):
        for key, value in foo(a):
            setattr(self, key, value)

But can't unpack correctly.

cs95
  • 379,657
  • 97
  • 704
  • 746
Georg Heiler
  • 16,916
  • 36
  • 162
  • 292

3 Answers3

2

Your function returns a tuple of values (a 2-tuple) not an iterable of 2-tuples. You're iterating over that tuple which contains strings, and you can't unpack those returned strings into twos.

You can stick with the original solution or unpack the items directly into the instance attributes:

self.first_thing, self.second_foo = foo(a)

And for many attributes:

_ATTR_NAMES = ('first_thing', 'second_foo')

class Thing(object)
    def __init__(self, a):
        for key, value in zip(_ATTR_NAMES, foo(a)):
            setattr(self, key, value)
Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
1

Why not just:

class Thing(object):
    def __init__(self, a):
        self.first_thing, self.second_foo = foo(a)

You do not need the first line inside __init__() function.

As per your comment, you can return a dictionary in your foo function and use setattr(), the updated solution would be:

def foo(a):
    first_thing = 'first' + a
    second_foo =  'second' + a + 'bar'
    return {'first_thing': first_thing, 'second_foo': second_foo}


class Thing(object):
    def __init__(self, a):
        for k, v in foo(a).items():
            setattr(self, k, v)
ettanany
  • 19,038
  • 9
  • 47
  • 63
  • But this means that I manually need to specify all the values. can't this be made a bit nicer i.e. something like java reflection which would take the literal name i.e. `first_thing` and store `first_thing`as an attribute and its respective value? – Georg Heiler Sep 10 '17 at 11:22
  • @GeorgHeiler in this case, you will need to return a dictionary instead of tuple in your `foo` function – ettanany Sep 10 '17 at 11:23
  • Would you recommend `self.__dict__.update(foo(a))` or `setattr(self, k, v)`? – Georg Heiler Sep 10 '17 at 11:29
  • Personally I recommend `setattr()` as it's more safer. You can find some details in this answer https://stackoverflow.com/a/14504828/4575071 – ettanany Sep 10 '17 at 11:37
1

If your function returns a varying number of arguments, you could return a dictionary from foo instead, and update the __dict__ attribute.

def foo(a):
    return {'first' : 'first' + a, 'second' : 'second' +  a + 'bar'}

class Thing(object):
    def __init__(self, a):
        self.__dict__.update(foo(a))

In [1233]: f = Thing('test')

In [1234]: f.first
Out[1234]: 'firsttest'

In [1235]: f.second
Out[1235]: 'secondtestbar'

Note the caveats with this approach, the most significant one being the lack of control over what is being updated.

cs95
  • 379,657
  • 97
  • 704
  • 746