Python -类内容管理器.如果你把产量放在里面__exit__为什么会省略这个方法?

Python - Class Content manager. Why __exit__ method is omitted if you put yield inside of it?

提问人:Денис Торопов 提问时间:9/22/2023 更新时间:9/22/2023 访问量:51

问:

class OracleConnected():
    def __init__(self):
        self.oracle_username = oracle_username[1]
        self.oracle_password = oracle_password[1]
        self.dsn = cx_Oracle.makedsn(hostname[1], port[1], sid[1])
    def __enter__(self):
        try:
            print('Then this')
            cx_Oracle.init_oracle_client(lib_dir=lib_dir)
            self.connection = cx_Oracle.connect(user=self.oracle_username, password=self.oracle_password, dsn=self.dsn)
        except Exception as e:
            print(f'Error connecting to Oracle: \n{e}')
        else:
            yield self.connection

    def __exit__(self, exc_type, exc_val, exc_tb):
            print('Finally do this')
            #self.connection.close()
            yield self.connection.close()


with OracleConnected() as connection:
    print('It first do this')
    cur = next(connection).cursor()
    cur.execute("select *From payments where id = 324986093")
    print(cur.fetchall())
  1. 如果一切都保持不变,我得到: 它首先执行此操作 然后这个 [(324986093, 391908893, 391908893, 1228, 无, datetime.datetime(2023, 4, 24, 0, 0)]

  2. 而不是 yield self.connection.close() -> 只写 self.connection.close(): 它首先执行此操作 然后这个 [(324986093, 391908893, 391908893, 1228, 无, datetime.datetime(2023, 4, 24, 0, 0)] 最后,执行此操作

那么问题来了,产量有什么特别之处,它给我带来了这么多麻烦?

P.S. 另外,返回 self.connection.close() -> 而不是 yield self.connection.close() 我得到了想要的答案: 它首先执行此操作 然后这个 [(324986093, 391908893, 391908893, 1228, 无, datetime.datetime(2023, 4, 24, 0, 0)] 最后,执行此操作

附言最重要的是,如果我可以问 - 想象一下我们解决了这个谜团。同一代码中的另一个问题,如果我在使用 self.connection 连接到服务器时遇到错误并说它给出错误,那么我将此错误传递给退出,退出也给了我一个错误 - 如何处理这种情况。所以我没有通过它退出

我试着阅读文档,但似乎没有任何效果。我只是好奇省略退出的原因是什么

Python 产量 contextmanager

评论

2赞 Tom Karzes 9/22/2023
你为什么要尝试使用而不是?你是否明白,通过在函数中加入一个语句,它会将该函数变成一个生成器? 并且非常不同。yieldreturnyieldyieldreturn
0赞 Денис Торопов 9/23/2023
我需要没有必要偏爱 over return 尽管如此,我只是想尝试一下,它给了我这个奇怪的结果 所以问题仍然存在,为什么 yield 给出一个结果,而 return 给出另一个结果 为什么当我使用 yield 时,它会跳过整个退出方法,就好像它不存在一样。另一方面,当我更改 yield -> return 时(并在内部进行一些更改,以便它可以与函数而不是生成器一起使用)它会“进入”退出关闭连接并打印(“最后这样做”)yield
0赞 Tom Karzes 9/23/2023
yield不会完全跳过该方法。它只是表现不同。调用时,不是立即执行函数体,而是返回一个生成器。如果稍后调用返回的生成器(直接调用或通过迭代它),将执行函数体,直到它执行语句。阅读有关产量和发电机的信息。这比随机尝试它们,然后试图从某人那里获得多余的、个性化的解释要有意义得多。__exit__nextyield
0赞 Tom Karzes 9/23/2023
您需要了解的是,如果函数在函数体中的任何位置都包含语句,那么在函数中的任何语句执行之前,从一开始调用它的行为就会有所不同。相反,调用它将返回一个生成器。函数体的执行实际上不会开始,直到(或等效的)应用于返回的生成器。yieldnext
0赞 Денис Торопов 9/23/2023
你是绝对正确的,我应该先更仔细地阅读,我只是被卡住了,不知道该怎么做,也不知道它不起作用的原因是什么。谢谢yield

答:

1赞 Alex Hall 9/22/2023 #1

__enter__并且不应该有,因为那样它们将返回一个生成器,并且您必须遍历生成器才能完成该方法的执行。__exit__yield

您可能会与 @contextlib.contextmanager 混淆,后者是一个装饰器,可以应用于生成器函数以创建简单的上下文管理器,而无需编写带有 和 的类。__enter____exit__

评论

0赞 Денис Торопов 9/23/2023
我可能会感到困惑。但是你能解释为什么在我的情况下使用是一个坏主意吗?在身体内部,我做了一些工作,例如使用类提供的生成器。当我这样做而不是 -> 并在内部使用语句进行一些更改时,它会很好地工作并在内部进行 gose ?yieldwithnext(connection)OracleConnected()yieldreturn__exit__
0赞 Alex Hall 9/23/2023
在代码中,自动使用 yield 意味着这些方法返回生成器。 返回一个生成器,所以 in 是一个生成器。 推进生成器并从 返回。但也返回一个生成器,并且返回的生成器上没有任何调用或任何东西,因此里面的代码根本不会运行,因为它正在等待。就像你删除了 它永远不会运行包含 .__enter__connectionwith OracleConnected() as connection:next(connection)self.connectionyield self.connection__exit__next()next()__enter__print('Then this')
0赞 Денис Торопов 9/23/2023
是的,好吧,现在有道理了。谢谢!