Search code examples
pythoniteratoryield

Python yield statement returns the same value every time


EDIT: I want to see the solution using the next statement. I am accessing a weather app API that returns a json object, a part of the information of that object is the daily time of sunrise and sunset, here is its content (for three days):

my_dict = {
"daily": [
    {
      "dt": "2020-06-10 12:00:00+01:00",
      "sunrise": "2020-06-10 05:09:15+01:00",
      "sunset": "2020-06-10 19:47:50+01:00"
    },
    {
        "dt": "2020-06-11 12:00:00+01:00",
        "sunrise": "2020-06-11 05:09:11+01:00",
        "sunset": "2020-06-11 19:48:17+01:00"
    },
    {
      "dt": "2020-06-12 12:00:00+01:00",
      "sunrise": "2020-06-12 05:09:08+01:00",
      "sunset": "2020-06-12 19:48:43+01:00"
    }
]
}

And here is the function that should return a tuple per day, but it does not. It keeps returning a tuple of data for the same day, the first day.

daily_return = my_dict['daily']


def forecast(daily_return):
    # daily_return is a list
    for day in daily_return:
        # day becomes a dict
        sunrise = day['sunrise']
        sunset = day['sunset']
        yield sunrise, sunset

for i in range(3):
    print(next(forecast(daily_return)))

And here is the output:

('2020-06-10 05:09:15+01:00', '2020-06-10 19:47:50+01:00')
('2020-06-10 05:09:15+01:00', '2020-06-10 19:47:50+01:00')
('2020-06-10 05:09:15+01:00', '2020-06-10 19:47:50+01:00')

Solution

  • Because you're initiating the generator every time you loop instead of looping a range just iterate the generator:

    for sunrise, sunset in forecast(daily_return):
        print(sunrise, sunset)
    

    If you only want the first 3 you can zip it with a range or use itertools.islice as @cs95 has shown:

    for sunrise, sunset, _ in zip(forecast(daily_return), range(3)):
        print(rise, set)
    

    If you must use next then initiate the generator outside the loop:

    gen = forecast(daily_return)
    for i in range(3):
        print(next(gen))
    

    You can also use operator.itemgetter to achieve this same functionality instead of your custom function:

    from operator import itemgetter
    from itertools import islice
    
    forecast_gen = map(itemgetter('sunrise', 'sunset'), daily_return)
    
    for sunrise, sunset in islice(forecast_gen, 3):
        print(sunrise, sunset)