Определите переменные, которые представляют собой списки строк в области действия вызывающего

Я хотел бы идентифицировать все списки строк, которые пользователь определил в своей интерактивной оболочке.

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__)
Другие вопросы по тегам