列表的Python函数`yield`,返回单个元素

时间:2015-01-30 13:11:53

标签: python generator

是否有可能在传递列表时生成一个生成生成器的函数,但是在给定单个值时会返回?

以此为例:

def duty2015(x):

  if type(x) in [list, np.ndarray]:
    for xi in x:
      yield new_duty(xi)

  else:

    sd = 0
    if x <= 120000:
      return sd
    elif x <= 250000:
      return (x-125000) * 0.02
    elif x <= 925000:
      return 2500 + (x-250000)*0.05
    elif  x <= 1500000:
      return 36250 + (x-925000)*0.1
    else:
      return 93750 + (x-1500000)*0.12

显然这不起作用,我得到SyntaxError: 'return' with argument inside generator错误。

我意识到我可以做这样的事情:

def duty2015(x):

  if type(x) in [list, np.ndarray]:
    for xi in x:
      for result in duty2015(xi):
        yield result

  else:

    sd = 0
    if x <= 125000:
      yield sd
    elif x <= 250000:
      yield (x-125000) * 0.02
    elif x <= 925000:
      yield 2500 + (x-250000)*0.05
    elif  x <= 1500000:
      yield 36250 + (x-925000)*0.1
    else:
      yield 93750 + (x-1500000)*0.12

但是当我在单个物品上调用它时,它会给我一个发电机,而我只是在它被调用更大的物品时才会得到它。

显然我可以把它作为一个列表,但这又不是最佳的。

对于下面答案中的评论,这样的事情会更好:

def duty_new(x, generator=False):

  if type(x) in [list, np.ndarray]:
    if generator:
      return (duty_new(xi) for xi in x)
    else:
      return [duty_new(xi) for xi in x]

  else:

    sd = 0
    if x <= 125000:
      return sd
    elif x <= 250000:
      return (x-125000) * 0.02
    elif x <= 925000:
      return 2500 + (x-250000)*0.05
    elif  x <= 1500000:
      return 36250 + (x-925000)*0.1
    else:
      return 93750 + (x-1500000)*0.12

因此,在正常使用下,它将具有可预测的行为或返回与传递给它的相同类型的参数(至少对于合理的参数,并且可能是因为它不会仅仅遍历numpy数组),但如果需要发电机,可以明确要求它?

4 个答案:

答案 0 :(得分:5)

返回一个生成器表达式,如下所示

def duty2015(x):

  if isinstance(x, list) or isinstance(x, np.ndarray):
      return (result for xi in x for result in duty2015(xi))
  else:
     ...
     ...

现在,每当您使用单个元素调用duty2015时,您将获得单独的值,否则您将获得一个生成器表达式,该表达式必须使用next协议进行迭代。

就个人而言,我觉得你的第二个版本是好的并且它是一致的,因为它使duty2015成为一个生成器函数,而且正如Martijn Pieters在评论中所提到的那样,它没有调用者猜测它得到了什么回来,更好地坚持下去。

注意:第一版和第二版的代码不同。我选择第二个版本中的代码来显示这个想法。

答案 1 :(得分:2)

这样怎么回事? :

def new_fun(x):
  return x + 1

def duty2015(x):

  if type(x) is list:
    return (new_fun(i) for i in x)

  else:

    sd = 0
    if x <= 120000:
      return sd
    elif x <= 250000:
      return (x-125000) * 0.02
    elif x <= 925000:
      return 2500 + (x-250000)*0.05
    elif  x <= 1500000:
      return 36250 + (x-925000)*0.1
    else:
      return 93750 + (x-1500000)*0.12

print duty2015([1,2,3])
print (1)

输出:

<generator object <genexpr> at 0x10be06b40>
1

这样你就可以使用return获取生成器对象。

答案 2 :(得分:0)

您可以返回另一个作为生成器的函数的结果,如下所示:

def dolist(li):
    for el in li:
        yield el * 2

def either(x):
    if isinstance(x, list):
        return dolist(x)
    else:
        return x * 2

print either(4) # prints 8
print list(either([2,3])) # prints [4,6]

答案 3 :(得分:0)

您可以从函数中返回生成器。通过

def duty2015(x):

  if isinstance(x, list) or isinstance(x, np.ndarray):
    return (new_duty(xi) for xi in x)
  else:
    ...

或使用辅助生成器函数:

def duty_gen(x):
    for xi in x:
        yield new_duty(xi)

def duty2015(x):

    if isinstance(x, list) or isinstance(x, np.ndarray):
        return duty_gen(x)