usando with – como saber quais variáveis estão dentro do contexto
Pergunta
Em este código Kivy:
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
userdata = touch.ud
with self.canvas:
Color(1, 1, 0)
d = 30.
Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d))
userdata['line'] = Line(points=(touch.x, touch.y))
Aparentemente Color
e d
e Ellipse
estão dentro do namespace de self.canvas
, mas como o Python sabe disso userdata
não está dentro do mesmo namespace?
Solução
Editar:Esta resposta ficou um pouco longa, então aqui está o resumo:
with self.canvas
define o momento tela ativa para o seguinte bloco de código.- Todas as instruções de desenho como
Color
ouEllipse
desenhe na tela ativa.
Namespaces realmente não têm nada a ver com isso, é o contexto que importa (veja abaixo).
O with
instrução permite que você use os chamados gerenciadores de contexto.
A sintaxe é assim
with thing [as foo]:
onde thing
geralmente é uma função decorada com o contextlib.contextmanager
decorador.O que exatamente um gerenciador de contexto faz depende de como thing
é implementado.
Mas o que isso não faz é fazer com que a variável apareça magicamente no seu escopo.Uma referência ao contexto pode ser obtida pelo opcional as foo
cláusula, mas é isso. Color
e Ellipse
no seu exemplo vêm de outro lugar (provavelmente importações?).
Para descobrir o que exatamente o gerenciador de contexto no with self.canvas
linha faz, você deve olhar para o Documentação da API ou o Código fonte para kivy.graphics.instructions.Canvas
.
Aqui está o trecho relevante do tutorial:
Ao usar a instrução com ele, todos os comandos sucessivos de desenho que são corretamente recuados modificarão essa tela.A declaração com com também garante que, após o nosso desenho, o estado interno possa ser limpo corretamente.
Então o usar de Color
e Ellipse
afeta self.canvas
, mas eles não são definidos de forma alguma pela instrução with.
Olhando para o código-fonte, é assim que funciona:
def class CanvasBase(InstructionGroup):
def __enter__(self):
pushActiveCanvas(self)
def __exit__(self, *largs):
popActiveCanvas()
__enter__
e __exit__
definir o que acontece se um gerenciador de contexto for inserido (antes da primeira linha do código recuado após o with
declaração) e saiu.
Neste caso, a tela simplesmente é empurrada para um pilha que define a tela atualmente ativa (e é retirada dela se o gerenciador de contexto for encerrado).
Em kivy.graphics.instructions.Instruction
, a classe base aparente para todas as instruções de desenho, a pai está definido para a tela atualmente ativa:
self.parent = getActiveCanvas()
Outras dicas
Na verdade, Color
e Ellipse
são importados de kivy.graphics
um pouco mais alto no código:
from kivy.graphics import Color, Ellipse
Para responder sua pergunta sobre namespaces, o python não precisa "saber" de qual namespace está obtendo variáveis.Possui regras de namespace muito simples em comparação com linguagens como Java, que pesquisam escopos de função, objeto, classe, global e pacote um após o outro.Python tem um namespace global (por módulo) e uma pilha de namespaces locais (por exemplo,funções aninhadas podem obter variáveis de funções externas).Apenas percorre a lista de escopos até encontrar o nome da variável em questão.
O with
afirmação acima tem um significado especial, mas acho que mesmo with
não pode introduzir implicitamente novas variáveis no escopo local (ele pode introduzir uma variável explicitamente com o as
cláusula, no entanto).