【问题标题】:Optional yield or return in python3. How to?python3中的可选yield或return。如何?
【发布时间】:2021-09-02 03:37:37
【问题描述】:

我想要一个可以选择返回或产生结果的函数。 这是一个例子。

def f(option=True):
    ...
    for...:
        if option:
            yield result
        else:
            results.append(result)

    if not option:
        return results

当然,这是行不通的,我用 python3 试过了,不管我设置什么选项值,我总是得到一个生成器。 据我了解,python 检查函数的主体,如果存在yield,那么结果将是一个生成器。 有什么办法可以解决这个问题并制作一个可以随意返回或屈服的函数?

【问题讨论】:

  • 为什么需要这个?难道你不能总是把它用作生成器并且只让它产生一次if not option吗?
  • XY Problem吗?您能否详细说明您要在这里解决的问题?
  • 我有一个从文件中提取数据的函数,我大部分时间都需要将数据作为一个列表,但有时(例如在大文件的情况下)这个函数的生成器版本可能很有用.当然,我可以遍历生成器并列出一个列表,但我真的更愿意添加一些额外的功能。如果我只让步一次,它只会给我列表中的一项,而不是所有列表,这不是我想要的
  • @alec_djinn: 然后使用list(f()) 从生成器中提取所有值。
  • 这会给我正确的结果,但不会在我的函数中添加该功能。

标签: python python-3.x return generator yield


【解决方案1】:

基于class 的方法

class FunctionAndGenerator:
    def __init__(self):
        self.counter = 0
        
    def __iter__(self):
        return self
        

# You need a variable to indicate if dunder next should return the string or raise StopIteration. 
# Raising StopIteration will stop the loop from iterating more.
# You'll have to teach next to raise StopIteration at some point

    def __next__(self):
        self.counter += 1 
        if self.counter > 1 :
            raise StopIteration 
        return f"I'm a generator and I've generated {self.counter} times"        

    def __call__(self):
        return "I'm a function"
        
x = FunctionAndGenerator()
print(x())
    
for i in x:
    print(i)
I'm a function
I'm a generator and I've generated 1 times

[Program finished] 

【讨论】:

    【解决方案2】:

    你不能。 任何使用yield 都会使函数成为生成器。

    您可以使用一个使用list() 将生成器生成的所有值存储在列表对象中并返回的函数来包装您的函数:

    def f_wrapper(option=True):
        gen = f()
        if option:
            return gen    # return the generator unchanged
        return list(gen)  # return all values of the generator as a list
    

    但是,一般来说,这是糟糕的设计。不要让你的函数像这样改变行为;坚持一种返回类型(生成器一个对象),不要让它在两者之间切换。

    考虑将其拆分为两个函数:

    def f():
        yield result
    
    def f_as_list():
        return list(f())
    

    如果您需要生成器,请使用f(),如果您想要一个列表,请使用f_as_list()

    由于list()(和next() 仅访问生成器的一个值)是内置函数,因此您很少需要使用包装器。直接调用这些函数即可:

    # access elements one by one
    gen = f()
    one_value = next(gen)
    
    # convert the generator to a list
    all_values = list(f())
    

    【讨论】:

      【解决方案3】:

      这个怎么样?

      def make_f_or_generator(option):
          def f():
              return "I am a function."
          def g():
              yield "I am a generator."
          if option:
              return f
          else:
              return g
      

      这至少让您可以选择创建函数或生成器。

      【讨论】:

      • 这是一个有趣的想法......它并不像我想要的那样干净,但绝对可以让我随意改变“选项”。我正在测试它...
      • 按我的意愿工作!谢谢你。一点点修改,应该是 return f() / return g() (带括号)
      • @alec_djinn:您确实意识到我提出的next(generator()) 选项正是这样做的,对吧?
      • @MartijnPieters 不是真的,你为什么认为我应该使用 next() 代替?我正在使用最终解决方案更新帖子,以便您对其发表评论...
      • @alec_djinn:因为您在编辑评论之前发布的内容表明您返回的唯一值是您曾经产生的。在生成器上使用next() 将访问该值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-07
      • 2021-11-18
      相关资源
      最近更新 更多