【问题标题】:Unexpected decorator behavior, "if" statement executes after if 1==2:意外的装饰器行为,“if”语句在 if 1==2 之后执行:
【发布时间】:2019-05-29 07:33:29
【问题描述】:

А 几小时前我提交了一个related question 并得到了一个答案,为什么我需要将yield 添加到我的装饰器中才能正常运行。 我最近回忆起我省略了它是有原因的 - 我无法解释的奇怪行为。

如果我是盲人,我提前道歉,但我花了几个小时盯着和玩这段代码,这就是我得到的:

def decor(func):
    def wrapper(*args, **kwargs):
        if 1==2:
            print ("Generator")
            for item in func(*args, **kwargs):
                print(item)
                #yield(item)
        else:
            print ("Not generator")
            res = func(*args, **kwargs)
            print(res)
            return res
    return wrapper

@decor            
def f():
    return "a"


f()

"""
Output:
    Not generator
    a

"""

如果我删除yield 之前的评论,则根本没有输出。

这是为什么呢?我在if 1==2: 语句中更改的任何内容怎么可能对脚本产生任何影响?

【问题讨论】:

  • Python 中的数字 1 和 2 是浮点数吗?
  • @RobertHarvey 不,它们是很好的旧整数,除非写成 1.0 等等。这里没有浮点问题。
  • 您的wrapper 不能既是普通函数又是生成器函数。有条件地将包装器定义为生成器或普通函数并将其返回。函数定义中的 yield anywhere 自动使整个事物成为生成器。这发生在函数定义时
  • @RobertHarvey 当我访问该链接并点击“运行”时,我得到的正是 OP 报告的输出。当我取消注释 yield 时,我收到一个错误,这可能是 OP 想要询问的内容。

标签: python python-3.x generator decorator python-decorators


【解决方案1】:

如果一个函数在主体中包含yieldanywhere,那么它就是一个生成器函数。 yield 是否被执行并不重要。 1 == 2 为假的事实与此无关。

考虑以下函数:

def addone(numbers):
    for number in numbers:
        yield number + 1

当您拨打addone([]) 时会发生什么? yield 永远不会执行,但 addone 仍然返回一个生成器。为什么会有所不同:

def addone(numbers):
    if numbers:
        for number in numbers:
            yield number + 1

因此很明显,yield 是否实际执行是无关紧要的。唯一相关的事实是 yield 是否存在于函数体中。

如何修复该功能

修复比较简单,只需将带有yield的部分拉到一个单独的函数中即可:

import types

def wrapper(*args, **kwargs):
    result = func(*args, **kwargs)
    if isinstance(result, types.GeneratorType):
        print("Is a generator")
        return wrap_generator(result)
    print("Not a generator")
    return result

def wrap_generator(gen):
    for item in gen:
        print(item)
        yield item

以后如何避免

一般来说,这里的问题是函数要么是生成器(并使用yield),要么是普通函数(并使用return)。在同一个函数中同时使用yieldreturn 会有点混乱!

对于 Python,事实证明,如果在同一个函数中同时使用 yieldreturn,则该函数是生成器函数。这可能有点令人困惑,所以就风格而言,我通常会避免在同一个函数中同时使用 returnyield

【讨论】:

  • 我看到 Pythonistas 理解你所说的,但我完全不明白。为什么永远不会执行的代码块中的yield 会对任何代码产生任何影响?
  • @RobertHarvey 别担心,你知道 python 中的生成器吗?它...从 python 开始时你并不真正需要了解的东西。但是这个问题和收益声明都围绕着它。
  • 我认为关键是当生成器返回参数时OP未能报告脚本错误,因此很难知道为什么这个答案是相关/正确的(它是) ——把整个东西变成发电机[显然]不是故意的。我们这些不是 Pythonista 的人不一定会意识到这样的事情是错误的。因此,在这里做一点澄清可能会有用:假设的先验知识稍微有点过多。
  • @RobertHarvey:Python 怎么可能知道代码块在执行函数之前是否会执行?这是不可能的。
  • @DietrichEpp:好的,但这不是完全直观的行为,是吗?有没有一种 Pythonic 的方法可以摆脱这个泥潭?也许不把yield 放在永远不会执行的if 中?
猜你喜欢
  • 1970-01-01
  • 2017-10-28
  • 1970-01-01
  • 2021-04-25
  • 2015-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-03
相关资源
最近更新 更多