Определите переменные, которые представляют собой списки строк в области действия вызывающего
Я хотел бы идентифицировать все списки строк, которые пользователь определил в своей интерактивной оболочке.
def lists_of_strings():
d = dict(globals(), **locals())
d = {k:v for (k, v) in d.items() if not k.startswith("_") and k != 'In'}
res = {}
for k, v in d.items():
if isinstance(v, list):
if all(map(lambda x: isinstance(x, str), v)):
res[k] = v
return res
Когда я определяю эту функцию в моей текущей оболочке, она работает:
>>> lists_of_strings()
{}
>>> mylist = ["a", "b"]
>>> lists_of_strings()
{"mylist": ["a", "b"]}
Теперь, если я переместить эту функцию в модуле mymodule
и импортировать его:
>>> from mymodule import lists_of_strings
>>> lists_of_strings()
{}
>>> mylist = ["a", "b"]
>>> lists_of_strings()
{}
Функция всегда возвращает пустой словарь. Почему это, и что более важно, я могу это исправить?
Некоторый контекст: я пытаюсь написать помощника в своем модуле, чтобы определить подходящие переменные, определенные пользователем в текущей записной книжке jupyter. Моя цель состоит в том, чтобы спросить пользователя, хотят ли они использовать эти переменные в качестве аргумента некоторой предопределенной функции.
1 ответ
Питона globals
встроенный возвращает словарь, содержащий глобальные переменные для текущего модуля. Это довольно приятная особенность, так как "глобальные" переменные имеют не весь процесс, а модуль (и, следовательно, файл) - позволяют каждому модулю работать в качестве пространства имен. Это упрощает большую работу, чтобы избежать столкновения имен в больших системах (вдвойне, если использовать сторонние компоненты).
Но, да, есть способ перейти к глобальным переменным другого рабочего контекста. Весь код Python выполняется в контексте frame
объект - он хранит ссылку на фактический байт-код, локальные переменные и глобальные переменные, используемые в настоящее время. Атрибут f_globals объекта frame фактически является тем же словарем, который возвращается globals
встроенный.
Итак, все, что вам нужно сделать, это получить ссылку на объект фрейма, где происходил вызов вашей функции - и это можно сделать, взяв .f_back
атрибут текущего кадра.
Это означает, что ваша функция будет работать между модулями (и после импорта), если вы измените ее на:
import sys
def lists_of_strings():
caller_frame = sys._getframe().f_back
d = dict(caller_frame.f_globals, **caller_frame.f_locals)
d = {k:v for (k, v) in d.items() if not k.startswith("_") and k != 'In'}
res = {}
for k, v in d.items():
if isinstance(v, list):
if all(map(lambda x: isinstance(x, str), v)):
res[k] = v
return res
Тем не менее, важно отметить, что не весь код должен использовать фреймы. Если вы хотите просто знать глобальные переменные в известном модуле, вы можете просто использовать этот модуль __dict__
атрибут:
import math
print (math.__dict__)