Внутренняя функция недоступна для понимания - PullRequest
0 голосов
/ 09 мая 2018

Я пишу некоторый код Python 2.7.12 и очень удивлен следующей ошибкой в ​​выполнении этого кода:

def validate_available_links(self, link_dict, sub_element=None):

    def strip_dirt(key_val):
        #TODO: properly strip HTML encoded characters
        key_val = key_val.replace("\u2039", "<")
        key_val = key_val.replace("\u203a", ">")
        return key_val


    # Scrape all the links from the current webpage and verify
    # it with the provided link dictionary.
    if sub_element is None:
        act_links = self.suiteContext['browser'].webdriver.find_elements_by_tag_name('a')
    else:
        act_links = sub_element.find_elements_by_tag_name('a')
    result = {strip_dirt(lnk.text): lnk.get_attribute('href') for lnk in act_links}
    diff_keys = set(result.keys()) - set(link_dict.keys())
    diff_values = set(result.values()) - set(link_dict.values())
    self.tear_down_hook()
    for l_text, l_url in link_dict.iteritems():
        self.cfg.logger.info("[VALIDATION] Verify Link text [{}] and URL [{}]."
                             .format(l_text, l_url))
    import pdb; pdb.set_trace()

А при выполнении кода

(Pdb) result = {strip_dirt(lnk.text): lnk.get_attribute('href') for lnk in act_links}
*** NameError: global name 'strip_dirt' is not defined
(Pdb) strip_dirt
<function strip_dirt at 0x0651BBB0>
(Pdb) result = {strip_dirt(lnk.text): lnk.get_attribute('href') for lnk in act_links}
*** NameError: global name 'strip_dirt' is not defined
(Pdb) strip_dirt('x')
'x'
(Pdb) {strip_dirt(lnk.text): lnk.get_attribute('href') for lnk in act_links}
*** NameError: global name 'strip_dirt' is not defined

Может кто-нибудь объяснить, почему внутренняя функция strip_dirt недоступна для понимания в словаре, но предназначена для остальной части внешней функции?

1 Ответ

0 голосов
/ 29 мая 2018

Я не эксперт pdb, поэтому, пожалуйста, поправьте меня, если я ошибаюсь.

Вот вам MCVE :

import pdb

def f():
    def g(n): return 2*n
    pdb.set_trace()

f()

Теперь, в pdb, как и ожидалось:

ipdb> g(5)
10

Но откуда взято g имя?

ipdb> 'g' in globals()
False
ipdb> 'g' in locals()
True

Хорошо, g в locals() переменных f. Когда вы создаете список или диктовку, у вас есть новые locals() переменные:

ipdb> [locals() for _ in range(1)]
[{'_': 0, '.0': <range_iterator object at 0x7f1924003d80>}]

Следовательно, в понимании списка / слова g не входит ни в locals(), ни в globals():

ipdb> [g(1) for _ in range(1)]
*** NameError: name 'g' is not defined

Теперь большой вопрос: почему это работает в запущенной программе, а не в ipdb? Я надеюсь, у меня есть начало объяснения:

import pdb
import inspect

def f():
    def g(n): return 2*n
    print([g(1) for _ in range(1)])
    print([inspect.stack()[1].frame.f_locals for _ in range(1)])
    pdb.set_trace()

f()

# output:
[2] # it works because...
[{'g': <function f.<locals>.g at 0x7f1916692488>}] # ...g is in the parent stack frame

В ipdb, как замечено:

ipdb> [g(1) for _ in range(1)]
*** NameError: name 'g' is not defined

Но если вы берете g вручную из родительского фрейма, это работает:

ipdb> [inspect.stack()[1].frame.f_locals for _ in range(1)]
[{'g': <function f.<locals>.g at 0x7f1916692488>, '__return__': None}]

ipdb> [inspect.stack()[1].frame.f_locals['g'](1) for _ in range(1)]
[2]

Вывод: похоже, что ipdb не дает прямого доступа к значениям, хранящимся в родительском фрейме, в отличие от работающей программы.

Обратите внимание, что это, вероятно, в значительной степени зависит от реализации Python. (Я использовал CPython.)

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