использование with – как узнать, какие переменные находятся в контексте
Вопрос
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))
Видимо Color
и d
и Ellipse
находятся в пространстве имен self.canvas
, но откуда Python это знает userdata
не находится в том же пространстве имен?
Решение
Редактировать:Этот ответ получился немного длинным, поэтому вот краткое изложение:
with self.canvas
определяет текущий момент активный холст для следующего блока кода.- Все инструкции по рисованию, такие как
Color
илиEllipse
рисовать на активном холсте.
Пространства имен на самом деле не имеют к этому никакого отношения, важен контекст (см. ниже).
А with
оператор позволяет вам использовать так называемый контекстные менеджеры.
Синтаксис такой
with thing [as foo]:
где thing
обычно это функция, украшенная contextlib.contextmanager
декоратор.Что именно делает контекстный менеджер, зависит от того, как thing
реализован.
Но чего он не делает, так это волшебного появления переменной в вашей области видимости.Ссылку на контекст можно получить с помощью необязательного as foo
пункт, но это все. Color
и Ellipse
в вашем примере приходят откуда-то еще (вероятно, импорт?).
Для того, чтобы узнать что именно за контекстный менеджер в with self.canvas
линия делает, вам следует посмотреть на Документация по API или исходный код для kivy.graphics.instructions.Canvas
.
Вот соответствующий отрывок из руководства:
Используя оператор с ним, все последовательные команды рисования, которые правильно отступают, изменят этот холст.С помощью заявления также гарантировано, что после нашего рисунка внутреннее состояние может быть очищено должным образом.
Итак использовать из Color
и Ellipse
влияет self.canvas
, но они никак не определяются оператором with.
Если посмотреть на исходный код, то вот как это работает:
def class CanvasBase(InstructionGroup):
def __enter__(self):
pushActiveCanvas(self)
def __exit__(self, *largs):
popActiveCanvas()
__enter__
и __exit__
определить, что произойдет, если войти в контекстный менеджер (перед первой строкой кода с отступом после with
заявление) и вышел.
В этом случае холст просто надвигается на куча который определяет активный в данный момент холст (и извлекается из него при выходе из контекстного менеджера).
В kivy.graphics.instructions.Instruction
, очевидный базовый класс для всех инструкций рисования, родительский элемент установлен на текущий активный холст:
self.parent = getActiveCanvas()
Другие советы
На самом деле, Color
и Ellipse
импортированы из kivy.graphics
чуть выше в коде:
from kivy.graphics import Color, Ellipse
Чтобы ответить на ваш вопрос о пространствах имен, Python вообще не нужно «знать», из какого пространства имен он получает переменные.Он имеет очень простые правила пространства имен по сравнению с такими языками, как Java, которые ищут функции, объекты, классы, глобальные области и области пакетов одна за другой.Python имеет одно глобальное пространство имен (для каждого модуля) и стек локальных пространств имен (например,вложенные функции могут получать переменные из внешних функций).Он просто перемещается по списку областей, пока не найдет нужное имя переменной.
А with
утверждение выше имеет особое значение, но я думаю, что даже with
не может неявно вводить новые переменные в локальную область видимости (одну переменную можно вводить явно с помощью as
пункт, однако).