7

with我刚刚意识到可以使用Python 语句在 Kivy 中添加顶点指令的方式有些神秘(至少对我而言) 。例如,使用的方式with是这样的:

... some code
class MyWidget(Widget)
    ... some code 

    def some_method (self):
        with self.canvas:
           Rectangle(pos=self.pos, size=self.size)

一开始我以为只是with偶尔使用的Python语句。但突然间我意识到事实并非如此。通常它看起来更像这样(示例取自此处):

with open('output.txt', 'w') as f:
   f.write('Hi there!')

通常as在实例之后有一个类似的东西和对象的别名。在 Kivy 示例中,我们没有定义和别名,这仍然可以。但令我困惑的部分是指令 Rectangle 仍然与 self.canvas 相关联。在阅读了with声明之后,我非常确信 Kivy 代码应该写成这样:

class MyWidget(Widget)
    ... some code 

    def some_method (self):
        with self.canvas as c:
           c.add (Rectangle(pos=self.pos, size=self.size))

我假设在内部该方法add是被调用的方法。假设是基于我们可以简单地添加矩形self.add (Rectangle(pos=self.pos, size=self.size))

我是否遗漏了有关withPython 语句的某些内容?或者这是 Kivy 实现的某种东西?

4

2 回答 2

5

我不认识 Kivy,但我想我可以猜到这个特定的结构是如何工作的。

with该语句不是保留您正在与之交互的对象(画布?)的句柄,而是将其存储在对您隐藏的某个全局变量中。然后,您在内部使用的语句with使用该全局变量来检索对象。在块结束时,全局变量作为清理的一部分被清除。

结果是一个折衷:代码不那么明确(这通常是 Python 中需要的特性)。但是,代码更短,这可能更容易理解(假设读者知道 Kivy 的工作原理)。这实际上是在 Python 中制作嵌入式 DSL 的技术之一。

涉及到一些技术细节。例如,如果您希望能够嵌套这样的结构(将一个with放在另一个里面),而不是一个简单的全局变量,您可能希望使用一个全局变量来保存这些对象的堆栈。此外,如果您需要处理线程,您将使用线程局部变量而不是全局变量。但是通用机制仍然是相同的——Kivy 使用了一些保存在你无法直接控制的地方的状态。

于 2013-06-23T03:40:46.223 回答
4

这句话没有什么特别神奇的with地方,但也许你不知道它是如何工作的?

为了在with语句中使用任何对象,它必须实现两种方法: __enter____exit__. __enter__with进入块时调用,并在出于任何原因__exit__退出块时调用。

当然,对象在其__enter__方法中的作用取决于它。由于我没有 Kivy 代码,我只能猜测它的canvas.__enter__方法在某处设置了一个全局变量,并Rectangle检查该全局变量以查看它应该在哪里绘制。

于 2013-06-23T03:40:24.953 回答