Combining Generators to Seat Guests For an Event: Type Error: function object is not iterable

问题描述 投票:0回答:2

我希望能够使用生成器为每位客人分配一张桌子和一个座位号,并提供餐点选择。我需要创建一个生成器函数,它将接收客人的字典作为输入,并使用我在前面几行代码中创建的连接生成器座位/食物选择生成器。我需要编写的这个函数应该产生一个来宾名称的元组和连接的生成器的下一个值。

我认为正确的答案应该是这样的: (Tim,“Chicken”,“Table 1”,f“Seat 1”) 然后为下一位客人

(Tonya, "Chicken", "Table 1", f"Seat 2)

关于我的代码有什么问题,我的具体问题是为什么代码末尾的 for 循环不可迭代。我怎样才能使它可迭代?我是否以正确的方式处理这个问题?

根据 Codecademy 的说明,他们似乎想让我了解如何使用生成器并将其他生成器与 yield from 语句结合起来。他们提供了以下提示:

next() 函数可以在类似下面的 yield 语句中被调用:

yield (name, next(another_generator))

我试图创建一个函数 guest_assignment() ,它将利用一个 for 循环来定位客人字典中的键。然后它会生成名称并使用 next() 函数将客人放在下一张桌子上。相反,我得到了一个 TypeError: function object is not iterable.

至于研究步骤,我查看了 Codecademy 的论坛,查看了之前的堆栈溢出示例,并且查看了类型错误,但我不知道是否需要使 guest_assignment 可迭代,或者我是否犯了一个错误制作我的发电机或沿途的某个地方。

Error Message: 
Traceback (most recent call last):
  File "script.py", line 72, in <module>
    print(tuple(seating_chart))
  File "script.py", line 68, in combined_generators
    yield from combined_tables
TypeError: 'function' object is not iterable

提供的代码:

Tim,22
Tonya,45
Mary,12
Ann,32
Beth,20
Sam,5
Manny,76
Kenton,15
Kenny,27
Dixie,46
Mallory,32
Julian,4
Edward,71
Rose,65
    script.py
    
        guests = {}
    def read_guestlist(file_name):
      text_file = open(file_name,'r')
      while True:
        line_data = text_file.readline().strip().split(",")
        if len(line_data) < 2:
        # If no more lines, close file
          text_file.close()
          break
        name = line_data[0]
        age = int(line_data[1])
        guests[name] = age
        guest_addition = yield name # modified code to create variable
        if guest_addition is not None:  # we know a guest addition has been passed in. 
          split_guest_addition = guest_addition.split(",")
          # we need to split the ["Jane, 35"] data first and store it in a variable.
          new_guest_name = split_guest_addition[0]
          # we take the newly created variable and store the 0 index into new guest name
          new_guest_age = int(split_guest_addition[1])
          # we take the newly creted variable and store the 1 index into the new guest age variable
          # we want to place jane in the guest dictionary. how did they do this look at line 12
          guests[new_guest_name] = new_guest_age # added new guest here
          
    #input the name of the dictionary we want to update. 
    # new_guest name is the "Jane" or the guest name that we want to add. it is the key that we want to input into the dictionary. and it updates the dictionary even though it is not being set on the right hand side of the equals sign. the new_guest_age value is the value side of the dictionary. we update the key and the value on the same line of code!
    
    guest_list_generator = read_guestlist("guest_list.txt")
    guest_index  = 0
    for guest in guest_list_generator:
      # print(guest)
      guest_index += 1
      if guest_index >= 10:
        break
    guest_list_generator.send("Jane,35")
    
    for guest in guest_list_generator:
      print(guest) # Jane does not need to be on the list in this case per Mark.
    guest_list_21_and_over = (name for name,age in guests.items() if age >= 21)
    print(list(guest_list_21_and_over))
    
    def table_one():
      for iteration in range(1,6):
        yield ("Chicken", "Table 1", f"Seat {iteration}")
      print("\n")
    
    def table_two():
      for iteration in range(1,6):
        yield("Beef", "Table 2", f"Seat {iteration}")
      print("\n")
    
    def table_three():
      for iteration in range(1,6):
        yield("Fish", "Table 3", f"Seat {iteration}")
      print("\n")
    
    def combined_tables():
      yield from table_one()
      yield from table_two()
      yield from table_three()
    
    all_tables = combined_tables()
    
    def guest_assignment():
      for name in guests.keys():
        yield name, next(combined_tables())
    
    def combined_generators():
      yield from combined_tables
      yield from guest_assignment
    
    seating_chart = combined_generators()
    print(tuple(seating_chart))
python generator iterable
2个回答
0
投票

首先,你不需要生成器来做你想做的事。

例如此代码完全按照您给出的示例执行:

def read(path, sep=","):
    res = []
    with open(path) as f:
        for line in f:
            if sep in line:
                name, num = line.split(sep)
                res.append((name, int(num)))
    return res

def assign_seats(guests, menus=["Chicken", "Beef", "Fish"], n_tables=5, n_seats=3):
    res = []
    for menu in menus:
        for table_num in range(1, n_tables+1):
            res.append((menu, f"Table {table_num}", f"Seat {table_num}"))
    return ((name, menu, table, seat) for ((name, age), (menu, table, seat)) in zip(guests, res)) 

list(assign_seats(res))                                                 

它返回:

[('Tim', 'Chicken', 'Table 1', 'Seat 1'),
 ('Tonya', 'Chicken', 'Table 2', 'Seat 2'),
 ('Mary', 'Chicken', 'Table 3', 'Seat 3'),
 ('Ann', 'Chicken', 'Table 4', 'Seat 4'),
 ('Beth', 'Chicken', 'Table 5', 'Seat 5'),
 ('Sam', 'Beef', 'Table 1', 'Seat 1'),
 ('Manny', 'Beef', 'Table 2', 'Seat 2'),
 ('Kenton', 'Beef', 'Table 3', 'Seat 3'),
 ('Kenny', 'Beef', 'Table 4', 'Seat 4'),
 ('Dixie', 'Beef', 'Table 5', 'Seat 5'),
 ('Mallory', 'Fish', 'Table 1', 'Seat 1'),
 ('Julian', 'Fish', 'Table 2', 'Seat 2'),
 ('Edward', 'Fish', 'Table 3', 'Seat 3'),
 ('Rose', 'Fish', 'Table 4', 'Seat 4')]

代码:

您使用发电机与客人灵活分配桌子、座位和菜单。

但是

zip()
结合解构就可以了。

然而,生成器的内存占用最少。 但是对于这么少的客人——即使是 1000 人,任何普通的个人电脑将他们当作列表来处理也不会有任何问题。

阅读文件:

使用

with open(filepath) as f:
然后在行上循环比使用 while 循环更 pythonic。

在实际生活中,人们甚至会这样做:

import pandas as pd                                                     

df = pd.read_csv("list.txt", header=None)  
df.columns = ["name", "age"]                                              

# df would contain:
       name  age
0       Tim   22
1     Tonya   45
2      Mary   12
3       Ann   32
4      Beth   20
5       Sam    5
6     Manny   76
7    Kenton   15
8     Kenny   27
9     Dixie   46
10  Mallory   32
11   Julian    4
12   Edward   71
13     Rose   65

df.values
会从中列出一个列表。 (好的,一个 numpy 数组,但可以像列表列表一样使用它)。 作为一个功能,这将是:

import pandas as pd

def read(filepath):
    df = pd.read_csv(filepath)
    df.columns = ["name", "age"]
    return df.to_numpy()

您的脚本存在逻辑错误: 表 1 始终有座位 1,表 2 始终有座位 2,依此类推。 我不认为这是你想要的。所以实际上每个表都应该有 3个座位(3个菜单)或最多k个座位。

为什么像你这样使用发电机是不好的

我认为你使用生成器的方式不是很好,因为你有 对几乎每个座位进行硬编码。所以你的代码不可扩展。 如果您决定在菜单中添加一道新菜, 你必须重写你的整个程序。 也就是说,您应该像我一样使用嵌套的 for 循环。

也许你甚至不需要嵌套的 for 循环。也许您希望程序仅将客人映射到一定数量的带座位的桌子。 也许您希望它们随机分布。 在这种情况下,最好首先用表格和菜单构建所有座位 - 并为每个座位分配一个索引号。 然后获取客人名单,并为每位客人随机分配一个索引。

可以这样做:

# use one the read functions above.
guests = read("list.txt")

# let's say there are 5 tables with each 3 seats:
def get_table_seats(n_tables, n_seats):
    return [(i_table, i_seat) for i_table in range(1, n_tables+1) for i_seat in range(1, n_seats+1)]

table_seats = get_table_seats(n_tables=5, n_seats=3)                                 

# table_seats contain now all combinations of table and seat numbers: 
[(1, 1),
 (1, 2),
 (1, 3),
 (2, 1),
 (2, 2),
 (2, 3),
 (3, 1),
 (3, 2),
 (3, 3),
 (4, 1),
 (4, 2),
 (4, 3),
 (5, 1),
 (5, 2),
 (5, 3)]

# now, we write a function which assigns randomly menus
# (we could say seat 1, 2, 3 get each of the menus)
# but we could also give each seat an index using `enumerate()`
def index_table_seat(table_seat):
    return [(i, i_table, i_seat) for i, (i_table, i_seat) in enumerate(table_seat)]

i_table_seat = index_table_seat(table_seats)

# which holds:
[(0, 1, 1),
 (1, 1, 2),
 (2, 1, 3),
 (3, 2, 1),
 (4, 2, 2),
 (5, 2, 3),
 (6, 3, 1),
 (7, 3, 2),
 (8, 3, 3),
 (9, 4, 1),
 (10, 4, 2),
 (11, 4, 3),
 (12, 5, 1),
 (13, 5, 2),
 (14, 5, 3)]

indexes = [i for i, table, seat in i_table_seat]

# in a normal world, guests would choose their menu
# so the menu would be already assigned in the guests list.

# but okay, let's pretend we assign every index a menu
# and the randomize the order.
# let's use your generator idea and take a list of menus
# and make them cycling:

def cycle(lst):
    while True:
        for el in lst:
            yield el

menu = ["Chicken", "Beef", "Fish"]
menu_ = cycle(menu)
# from now on with `next(menu_)` you can cycle through the menu list
# ad infinitum.

import random

def assign_randomized_menu(indexes, menu):
    menu_ = cycle(menu)
    # menu_ behaves like an endless list in zip()
    # we use random sample to randomize order of indexes
    return {index: menu for index, menu in zip(random.sample(indexes, k=len(indexes)), menu_)}

# we use a dictionary, to be able to recall for every index its menu

# now, we assign every guest randomized to an index:
def assign_index_to_guest(indexes, guests):
    return {index: guest for index, guest in zip(random.sample(indexes, k=len(indexes)), guests)}

index2guest = assign_index_to_guest(indexes, guests) 
index2menu  = assign_randomized_menu(indexes, menu)


def assign_randomized(guests, i_table_seat, menu):
    indexes = [i for i, table, seat in i_table_seat]
    index2guest = assign_index_to_guest(indexes, guests) 
    index2menu  = assign_randomized_menu(indexes, menu)
    return [(i, index2guest.get(i, [None, None])[0], index2menu[i], table, seat) for i, table, seat in i_table_seat]

# we could make it return a dictionary
# and make a data frame out of it

def assign_randomized(guests, i_table_seat, menu):
    indexes = [i for i, table, seat in i_table_seat]
    index2guest = assign_index_to_guest(indexes, guests) 
    index2menu  = assign_randomized_menu(indexes, menu)
    return [{'index': i, 'name': index2guest.get(i, [None, None])[0], 'menu': index2menu[i], 'table': table, 'seat': seat} for i, table, seat in i_table_seat]

pd.DataFrame(assign_randomized(guests, i_table_seat, menu))

# to make it reproducible, you can give the random machine a seed:
random.seed(1)
pd.DataFrame(assign_randomized(guests, i_table_seat, menu))
# then the result will always be:

    index     name     menu  table  seat
0       0   Edward     Fish      1     1
1       1      Ann  Chicken      1     2
2       2      Tim     Fish      1     3
3       3    Kenny     Beef      2     1
4       4     Beth     Beef      2     2
5       5    Dixie     Fish      2     3
6       6  Mallory     Fish      3     1
7       7    Manny  Chicken      3     2
8       8   Kenton     Beef      3     3
9       9    Tonya     Beef      4     1
10     10     Rose     Fish      4     2
11     11      Sam  Chicken      4     3
12     12     Mary  Chicken      5     1
13     13   Julian     Beef      5     2
14     14     None  Chicken      5     3

0
投票

Codecademy 论坛中的某人能够提供将 yield 和 next 函数与生成器一起使用的代码。

guests = {}
def read_guestlist(file_name):
    text_file = open(file_name,'r')
    while True:
        x = yield
        if x is None:
          line_data = text_file.readline().strip().split(",")
        else:
          line_data = x.split(",")
        if len(line_data) < 2:
        # If no more lines, close file
            text_file.close()
            break
        name = line_data[0]
        age = int(line_data[1])
        guests[name] = age
        yield name 
                                            
guestlist = read_guestlist('guest_list.txt')

for k in range(20):
  print(next(guestlist))

#Add Jane,35 to the generator
next(guestlist)
guestlist.send("Jane,35")

for k in range(8):
  print(next(guestlist))

def guest_age(guest_dict):
  for key,val in guest_dict.items():
    if val > 21:
      yield key

age_of_guest = guest_age(guests)

for obj in age_of_guest:
  print(obj)

def table_one():
  for j in range(1,2):
    for k in range(1,6):
      yield "Chicken", "Table {}".format(j),"Seat {}".format(k)

def table_two():
  for j in range(2,3):
    for k in range(6,11):
      yield "Beef", "Table {}".format(j), "Seat {}".format(k)

def table_three():
  for j in range(3,4):
    for k in range(11,16):
      yield "Fish", "Table {}".format(j), "Seat {}".format(k)


def combined_tables():
  yield from table_one()
  yield from table_two()
  yield from table_three()

combined_tables = combined_tables()

def guest_assignment():
  for name in guests.keys():
    yield name, next(combined_tables)

guest_assignment = guest_assignment()
for person in guest_assignment:
  print(person)
© www.soinside.com 2019 - 2024. All rights reserved.