Насколько плохи теневые имена, определенные во внешних областях?

Я просто переключился на Pycharm, и я очень доволен всеми предупреждениями и подсказками, которые он дает мне для улучшения моего кода. За исключением этого, которого я не понимаю:

This inspection detects shadowing names defined in outer scopes.

Я знаю, что плохой практикой является доступ к переменной из внешней области, но в чем проблема с затенением внешней области?

Вот один пример, где Pycharm дает мне предупреждение:

data = [4, 5, 6]

def print_data(data): # <-- Warning: "Shadows 'data' from outer scope
    print data

print_data(data)

Ответ 1

Нет ничего сложного в вашем выше фрагменте, но представьте себе функцию с несколькими аргументами и еще несколькими строками кода. Затем вы решите переименовать свой аргумент data как yadda, но пропустите одно из мест, которое оно использует в теле функции... Теперь data относится к глобальному, и вы начинаете с странного поведения - где бы вы гораздо более очевидным NameError, если у вас не было глобального имени data.

Также помните, что в Python все является объектом (включая модули, классы и функции), поэтому нет различных пространств имен для функций, модулей или классов. Другой сценарий заключается в том, что вы импортируете функцию foo в верхней части своего модуля и используете ее где-то в своем теле функции. Затем вы добавляете новый аргумент в свою функцию и называете его - неудача - foo.

Наконец, встроенные функции и типы также находятся в одном пространстве имен и могут быть затенены одинаково.

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

Ответ 2

В настоящее время наиболее одобренный и принятый ответ и большинство ответов здесь упускают из виду.

Не имеет значения, какова длина вашей функции или как вы называете переменную описательно (чтобы минимизировать вероятность потенциального конфликта имен).

Тот факт, что ваша локальная переменная или ее параметр имеют общее имя в глобальной области видимости, совершенно не имеет значения. И на самом деле, как бы тщательно вы ни выбирали имя локальной переменной, ваша функция никогда не сможет предвидеть, "будет ли мое классное имя yadda также использоваться в качестве глобальной переменной в будущем?". Решение? Просто не беспокойся об этом! Правильное мышление заключается в том, чтобы спроектировать вашу функцию так, чтобы она использовала входные данные и только из ее параметров в сигнатуре, так что вам не нужно заботиться о том, что находится (или будет) в глобальной области видимости, и тогда затенение вообще не станет проблемой.

Другими словами, проблема теневого копирования имеет значение только тогда, когда вашей функции нужно использовать локальную переменную с тем же именем И глобальную переменную. Но вы должны избегать такого дизайна в первую очередь. У кода OP на самом деле нет такой проблемы с дизайном. Просто PyCharm недостаточно умен и выдает предупреждение на всякий случай. Итак, просто чтобы порадовать PyCharm, а также сделать наш код чистым, посмотрите, как это решение цитирует из ответа Сильевска полное удаление глобальной переменной.

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()

Это правильный способ "решить" эту проблему, исправляя/удаляя вашу глобальную вещь, а не настраивая текущую локальную функцию.

Ответ 3

Хорошим обходным решением в некоторых случаях может быть перемещение кода vars + в другую функцию:

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()

Ответ 4

Это зависит от продолжительности функции. Чем дольше функция, тем больше шансов, что кто-то изменит ее в будущем, напишет data, думая, что это означает глобальное. На самом деле это означает локальный, но поскольку функция так долго не очевидна для них, что существует локальное с этим именем.

Для вашей примерной функции, я думаю, что затенение глобальной не плохо.

Ответ 5

data = [4, 5, 6] #your global variable

def print_data(data): # <-- Pass in a parameter called "data"
    print data  # <-- Note: You can access global variable inside your function, BUT for now, which is which? the parameter or the global variable? Confused, huh?

print_data(data)

Ответ 6

Сделайте это:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()

Ответ 7

Похоже, это 100% шаблон кода Pytest

увидеть:

https://docs.pytest.org/en/latest/fixture.html#conftest-py-sharing-fixture-functions

У меня была такая же проблема, вот почему я нашел этот пост;)

# ./tests/test_twitter1.py
import os
import pytest

from mylib import db
# ...

@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

И он предупредит с помощью This inspection detects shadowing names defined in outer scopes.

Чтобы это исправить, просто переместите ваш twitter в ./tests/conftest.py

# ./tests/conftest.py
import pytest

from syntropy import db


@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

И удалите twitter как в ./tests/test_twitter2.py

# ./tests/test_twitter2.py
import os
import pytest

from mylib import db
# ...

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

Это будет радовать QA, Pycharm и всех остальных