1

I am trying to figure out if there is an implicit way to iterate through list elements. Example: with the list someList = ['a','b','c'] you do not need to write a for loop to iterate through the list printing elements when print(someList) does an implicit iteration.

I have the following (simplified) python code:

class foo:
    def __init__(self):
        self.x=None
        self.y=None
        self.z=None

    def set(self, x, y , z):
        self.x = x
        self.y = y
        self.z = z

    def get(self):
        return {'x':self.x, 'y':self.y, 'z':self.z}


class bar:
    def __init__(self):
        self.foos=[]
    def add(self, foo):
        self.foos.append(foo)
    def get(self):
        return {'foos':self.foos}

Which are initialized using

f = foo()
f.set(1,2,3)

g = foo()
g.set(4,5,6)


h = bar()
h.add(f)
h.add(g)

Then calling

print(f.get())
print(g.get())
print(h.get())

which gives

{'z': 3, 'x': 1, 'y': 2}
{'z': 6, 'x': 4, 'y': 5}
{'foos': [<__main__.foo object at 0x0000000003541748>, <__main__.foo object at 0x000000000358C630>]}

What I'm ultimately looking to do is return a JSON like structure or Nested dictionary such that the returns of the foo.get() method are included in the call to bar.get().

I realize the array doesn't contain actual foo objects (only references to them).

I thought about using iterators like they were explained at http://anandology.com/python-practice-book/iterators.html . The problem with the example is that the items to be iterated over must be defined within the datastructure (i.e foo). The problem with including the iterator in the bar class is that foos list may not be the only collection to iterate over: I could have added collection foos2 (but did not for simplicity)

I considered the solution at Access nested dictionary items via a list of keys? , but the pprint gives me something similar to my own code. I am also not certain use of the reduce function would apply to my situation.

I also thought about doing operator overloading on the print function, but I'm not looking to get a string, just a data structure that easy to display but also navigate.

Is there a easy way or correct way to do this? Am I using the correct data-structure (i.e. should I be using something other than nested lists)?

I'm still new to Python so pleas be gentle.

Community
  • 1
  • 1
WoodMath
  • 521
  • 2
  • 8
  • 14
  • 1
    Are you doing this just for the purposes of printing? Or do you actually want to marshall these datatypes into a serializable format for saving somewhere? – Brendan Abel Apr 06 '16 at 20:23

3 Answers3

2

This returns the result of foo.get() in the return value of bar.get(). Is this what you are asking for?

class bar:
    ...
    def get(self):
        return {'foos':[foo.get() for foo in self.foos]}

Breaking down the return statement,

return (            # you know what this keyword does
    {               # create a dict
        'foos':     # with a key of "foos",
        [           # and a value that is a list
                    # (in this case, built from a list comprehension)
            x.get() # each element is the result of calling .get()
            for x in self.foos  # on each element of the "foos" list
        ]           # end of list comprehension
    }               # end of dict
)                   # end of return expression

So, the return statement returns a dict with exactly one key-value pair. The key is the literal string "foos". The value is a list composed of the result of calling .get() on each of the members of the self.foos list. (I changed the name of the loop variable and added parentheses to make life less confusing.)

And here is the result:

{'x': 1, 'z': 3, 'y': 2}
{'x': 4, 'z': 6, 'y': 5}
{'foos': [{'x': 1, 'z': 3, 'y': 2}, {'x': 4, 'z': 6, 'y': 5}]}

A perfectly equivalent implementation of bar.get() could use an explicit for loop, as shown below. I only use the list comprehension because I find that code easier to read. If you find the below easier, use it instead.

def get(self):
    result = []
    for x in self.foos:
        result.append(x.get())
    return result
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • in simple terms what is this doing? I know it's a list comprehension but don't understand how it works – Keatinge Apr 06 '16 at 20:22
  • @Rob : YES! Its exactly what I'm looking to do. Thank you. I suppose it wasn't an issue of **impliciit** vs **explicit** iteration but an issue of **inline** vs. **non-inline** iteration. That is I didn't want to write an explicit method (or anything beyond the `get` method's `return` statement) to iterate over the `foos` collection.) – WoodMath Apr 06 '16 at 20:29
  • @WoodMath - I'm not sure what you mean by "inline" and "non-inline" iteration. If you mean a for loop (like `for x in foos: ...`), stay tuned. I'll add that one next. – Robᵩ Apr 06 '16 at 20:31
  • @Rob - What I meant by inline iteration was the statement `return {'foos':[foo.get() for foo in self.foos]}`. Normally I've done `for` loops as `for x in coll: x.get()], but `return {'foos':[for foo in self.foos: foo.get()]}` is not valid python. Its one of these language specific nuances that is why I made the original post, as I need to alter how you think about common programming tasks. Thanks, again. – WoodMath Apr 06 '16 at 20:56
0

If you are needing to use foo objects like lists, the best idea I can give you is to make foo iterable, this can easily be done like this:

class foo(object):
    def __iter__(self):
        for item in [self.x, self.y, self.z]:
            yield item

This will then allow you to say things like list(foo)

when you need to convert an iterable object to JSON. You can also define what items (and their order) need to be iterated over in place of [self.x, self.y, self.z].

Edward Minnix
  • 2,889
  • 1
  • 13
  • 26
0

If you want to do it functionally without an explicit loop, you can use map:

class bar:
    def __init__(self):
        self.foos = []

    def add(self, foo):
        self.foos.append(foo)

    def get(self):
        return {'foos': list(map(foo.get, self.foos))}
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321