Mehdi Rezzag Hebla
Mehdi Rezzag Hebla

Reputation: 334

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')

Upvotes: 3

Views: 1368

Answers (3)

Jab
Jab

Reputation: 27485

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)

Upvotes: 7

Serge
Serge

Reputation: 3765

Your recreating the generator 3 times, then yield the same first element 3 times. Create the generator before the loop. Simple is better than complex.

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

Upvotes: 0

cs95
cs95

Reputation: 402263

I recommend converting forecast to a function that operates on a single entry. You can then call map to get an iterator and call next on it:

def forecast(day):
    return day['sunrise'], day['sunset']

it = map(forecast, daily_return)
print(next(it))
# ('2020-06-10 05:09:15+01:00', '2020-06-10 19:47:50+01:00')

If you want X number of entries from the iterator, use islice:

from itertools import islice
list(islice(map(forecast, daily_return), 3))
# [('2020-06-10 05:09:15+01:00', '2020-06-10 19:47:50+01:00'),
#  ('2020-06-11 05:09:11+01:00', '2020-06-11 19:48:17+01:00'),
#  ('2020-06-12 05:09:08+01:00', '2020-06-12 19:48:43+01:00')]

Upvotes: 2

Related Questions