0

I just want to be able to unpack the instance variables of class foo, for example:

x = foo("name", "999", "24", "0.222")
a, b, c, d = *x
a, b, c, d = [*x]

I am not sure as to which is the correct method for doing so when implementing my own __iter__ method, however, the latter is the one that has worked with mixed "success". I say mixed because doing so with the presented code appears to alter the original instance object x, such that it is no longer valid.

class foo:
  def __init__(self, a, b, c, d):
    self.a = a
    self.b = b
    self.c = c
    self.d = d

  def __iter__(self):
    return iter([a, b, c, d])

I have read the myriad posts on this site regarding __iter__, __next__, generators etc., and also a python book and docs.python.org and seem unable to figure what I am not understanding. I've gathered that __iter__ needs to return an iterable (which can be just be self, but I am not sure how that works for what I want). I've also tried various ways of playing around with implementing __next__ and iterating over vars(foo).items(), either by casting to a list or as a dictionary, with no success.

I don't believe this is a duplicate post on account that the only similar questions I've seen present a single list sequence object attribute or employ a range of numbers instead of a four non-container variables.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Todd
  • 1
  • What makes you think returning a list from `__iter__` alters `x`? If whatever's iterating over it changes `a` etc. then `x` would change *however* you implement the iterator. Could you just make it a `Sequence` (i.e. implement `__getitem__` and `__len__` per https://docs.python.org/3/library/collections.abc.html)? – jonrsharpe Feb 16 '17 at 21:13
  • Your first unpacking is missing a comma at the end, by the way. Also, could you elaborate on "alter the original instance object `x`"? How so? – Dimitris Fasarakis Hilliard Feb 16 '17 at 21:16
  • @jonrsharpe I assumed that I was returning an iterator for the explicitly defined list (i.e. allowing the obj's instance variables to be iterated over like a regular list - and unpacked similarly ). I can try to make it a Sequence sure, it'll just take more learning. I definitely appreciate that resource and will be checking it out. @Jim Fasarakis-Hilliard, as presently implemented if I attempt to do, say : `a, b, c, d = *x,` a, b, c, and d will hold the correct values, but then any attempt to access x - as before the unpacking - raises "TypeError: 'float' object is not subscriptable" – Todd Feb 17 '17 at 23:54
  • I was referring to *"the presented code appears to alter the original instance object x, such that it is no longer valid"* - could you show a [mcve]? It's not behaviour I'd expect given you're creating a fresh list of immutable objects, so it might be useful to explore what exactly you're doing with it. You're missing a few `self.` references, though. – jonrsharpe Feb 18 '17 at 08:41

2 Answers2

1

If you want the instance's variables, you should access them with .self:

def __iter__(self):
    return iter([self.a, self.b, self.c, self.d])

with this change,

a, b, c, d = list(x)

will get you the variables.


You could go to the more risky method of using vars(x) or x.__dict__, sort it by the variables name (and that's why it is also a limited one, the variables are saved in no-order), and extract the second element of each tuple. But I would say the iterator is definitely better.

Uriel
  • 15,579
  • 6
  • 25
  • 46
1

You can store the arguments in an attribute (self.e below) or return them on function call:

class foo:
  def __init__(self, *args):
    self.a, self.b, self.c, self.d = self.e = args

  def __call__(self):
    return self.e

x = foo("name", "999", "24", "0.222")
a, b, c, d = x.e
# or
a, b, c, d = x()
Friedrich
  • 117
  • 1
  • 1