Сокрытие окружающей среды от закрытия - PullRequest
0 голосов
/ 06 марта 2019

Я смотрел это видео Объектно-ориентированное программирование - это плохо .Тема не имеет отношения к вопросу, который я задам, но видео приводит к этому.Точно, в 41:10 он говорит о функции, которой, кажется, нет в python, он говорит о способности иметь вложенные функции / блоки, которые не знают о полном локальном контексте, но вместо этого, знают о некоторых переменныхвыбрано вручную.По сути, он просит о возможности удалить / частично скрыть окружение от замыкания.

Вот как должна выглядеть нужная функция в python (например, с использованием use декоратора):

def foo():
  x, y, z = 1, 2, 3

  def baz():
    print(x, y)  # prints "1 2"

  @use(y)
  def bar():
    print(y)  # Should print "2"
    z = 4     # Should create a local variable named `z`
    print(x)  # Should raise a NameError (`x` not defined)

  bar(), baz())

foo()

Я пытался реализовать этот декоратор, но безуспешно, поэтомуЯ попытался сделать это с более простой целью: не скрыть частично среду, а полностью ее скрыть.Вот решение, которое я нашел:

import ctypes

def foo():
  x, z = 1

  def baz():
    print(x)  # prints 1

  @hide_environment
  def bar(y):
    print(y)  # prints 2
    z = 3
    print(z)  # prints 3
    print(x)  # NameError is raised

  baz(), bar(2)

def hide_environment(f):
  def environment_agnostic_f(*args, **kwargs):
    remove_environment(f)
    return f(*args, **kwargs)
  return environment_agnostic_f

def remove_environment(f):
  if f.__closure__ is None: 
    return
  for v in f.__closure__:
    py_obj = ctypes.py_object(v)
    ctypes.pythonapi.PyCell_Set(py_obj, None)  # that look like it could go wrong

foo()

У меня есть четыре вопроса:

  1. Уже существует в python?
  2. Возможно ли реализовать use механизм, описанный в видео (внутри python)?
  3. Правильно ли реализована моя реализация hide_environment?

Я не понимаю, как 2. возможно, так как у меня нетидея, если возможно перехватить / или найти адрес ячейки, в который было помещено значение y.Я не уверен, что понимаю, как это делается, но из того, что я понимаю, когда читается foo, «компилятор» (не уверен, стоит ли так его называть) помещает все locals из * 1028.* в __closure__ объектах, на которые позже будут ссылаться bar и baz.Это привело к моему последнему вопросу:

Есть ли где-нибудь в документации, где описывается способ закрытия?

1 Ответ

0 голосов
/ 06 марта 2019

Это не идет с Python, но самый простой способ реализовать это - просто проверить, что использует функция, и выдать исключение, если она использует слишком много:

def allow_closure_vars(*okay_names):
    def checker(f):
        used_names = set(f.__code__.co_freevars)
        bad_names = used_names - set(okay_names)
        if bad_names:
            raise ValueError(f"Function uses the following variables it shouldn't: {bad_names}")
        return f
    return checker

def f():
    x, y = 1, 2
    @allow_closure_vars('x')
    def g():
        print(y)
    g()

f()

Попытка определить g вызовет ValueError.

Ваша реализация на самом деле не запрещает доступ к закрывающим переменным. Он просто отменяет назначение переменных, так же, как оператор del. Функция все еще имеет доступ к переменным, но они не были назначены. Скорее всего, это связано с внешней функцией, которая, вероятно, не ожидала, что эти переменные будут неназначенными, и не помешает любому коду присваивать новые значения переменным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...