Python-сообщество
- Начало
- » Python для экспертов
- » Область видимости и ссылки на аргументы
#1 Авг. 3, 2015 21:52:58
ArtikUA Зарегистрирован: 2015-05-14 Сообщения: 8 Репутация: 0 Профиль Отправить e-mail
Область видимости и ссылки на аргументы
Есть словарь и две функции: первая переворачивает значение ключа name этого словаря, а вторая — делает все буквы заглавными:
def first(town): town['name'] = town['name'][::-1] return town def second(town): town['name'] = town['name'].upper() return town town = 'name': 'Moscow', 'population': '10000000'> town = first(town) town = second(town) print(town['name'])
Всё работает отлично, но в каждом аргументе при объявлении функции IDE ругается на “Shadows name ‘town’ from outer scope”
И в самом деле, если мы сделаем так:
def first(): town['name'] = town['name'][::-1] def second(): town['name'] = town['name'].upper() town = 'name': 'Moscow', 'population': '10000000'> first() second() print(town['name'])
То результат не изменится.
Почему функции видят то, что по идее должно быть за пределами области видимости?
Я ведь не делал global town
Как сделать что бы всё было на “чистых” функциях?
Отредактировано ArtikUA (Авг. 3, 2015 21:54:17)
#2 Авг. 3, 2015 23:43:36
JOHN_16 От: Россия, Петропавловск-Камчатск Зарегистрирован: 2010-03-22 Сообщения: 3292 Репутация: 221 Профиль Отправить e-mail
Область видимости и ссылки на аргументы
Потому что о реализации области видимости в Питоне вы толкьо слышали, а не изучали. Он работает ровно так как и положено. Книга Лутца “Програмируем с Python”(3 изд.) глава 16: Области видимости функции. Вырезка:
Разрешение имен: правило LEGB
Для инструкции def:
• Поиск имен ведется самое большее в четырех областях видимости:
локальной, затем в объемлющей функции (если таковая имеется),
затем в глобальной и, наконец, во встроенной.
По умолчанию операция присваивания создает локальные имена.
• Глобальные объявления отображают имена на область видимости
вмещающего модуля.
Другими словами, все имена, которым присваиваются значения
внутри инструкции def (или внутри выражения lambda, с которым мы
познакомимся позже), по умолчанию являются локальными; функции могут
использовать имена в лексически (т. е., физически) объемлющих
функциях и в глобальной области видимости, но чтобы иметь возможность
изменять их, они должны быть объявлены глобальными. Схема
разрешения имен в языке Python иногда называется правилом LEGB,
название которого состоит из первых букв названий областей видимости:
• Когда внутри функции выполняется обращение к неизвестному
имени, интерпретатор пытается отыскать его в четырех областях
видимости — в локальной (local, L), затем в локальной области
любой объемлющей инструкции def (enclosing, E) или в выражении
lambda, затем в глобальной (global, G) и, наконец, во встроенной
(built-in, Б). Поиск завершается, как только будет найдено первое
подходящее имя.
• Когда внутри функции выполняется операция присваивания (а не
обращение к имени внутри выражения), интерпретатор всегда
создает или изменяет имя в локальной области видимости, если в этой
функции оно не было объявлено глобальным.
• Когда выполняется присваивание имени за пределами функции (т.е.
на уровне модуля или в интерактивной оболочке), локальная область
видимости совпадает с глобальной — с пространством имен модуля.
Т.е. механизм поиска переменной с именем Foo и присваивание переменной имени Foo — существенно отличается.
Если брать определение чистой функции, то скорее всего так:
def first(value): return value[::1] town['name'] = first(town['name'])
_________________________________________________________________________________
полезный блог о python john16blog.blogspot.com
Shadows name from outer scope python что это
Область видимости или scope определяет контекст переменной, в рамках которого ее можно использовать. В Python есть два типа контекста: глобальный и локальный.
Глобальный контекст
Глобальный контекст подразумевает, что переменная является глобальной, она определена вне любой из функций и доступна любой функции в программе. Например:
name = "Tom" def say_hi(): print("Hello", name) def say_bye(): print("Good bye", name) say_hi() say_bye()
Здесь переменная name является глобальной и имеет глобальную область видимости. И обе определенные здесь функции могут свободно ее использовать.
Локальный контекст
В отличие от глобальных переменных локальная переменная определяется внутри функции и доступна только из этой функции, то есть имеет локальную область видимости:
def say_hi(): name = "Sam" surname = "Johnson" print("Hello", name, surname) def say_bye(): name = "Tom" print("Good bye", name) say_hi() say_bye()
В данном случае в каждой из двух функций определяется локальная переменная name. И хотя эти переменные называются одинаково, но тем не менее это две разных переменных, каждая из которых доступна только в рамках своей функции. Также в функции say_hi() определена переменная surname, которая также является локальной, поэтому в функции say_bye() мы ее использовать не сможем.
Скрытие переменных
Есть еще один вариант определения переменной, когда локальная переменная скрывают глобальную с тем же именем:
name = "Tom" def say_hi(): name = "Bob" # скрываем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Tom
Здесь определена глобальная переменная name. Однако в функции say_hi определена локальная переменная с тем же именем name. И если функция say_bye использует глобальную переменную, то функция say_hi использует локальную переменную, которая скрывает глобальную.
Если же мы хотим изменить в локальной функции глобальную переменную, а не определить локальную, то необходимо использовать ключевое слово global :
name = "Tom" def say_hi(): global name name = "Bob" # изменяем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Bob
nonlocal
Выражение nonlocal прикрепляет идентификатор к переменной из ближайщего окружающего контекста (за исключением глобального контекста). Обычно nonlocal применяется во вложенных функциях, когда надо прикрепить идентификатор за переменной или параметром окружающей внешней функции. Рассмотрим ситуацию, где это выражение может пригодиться:
def outer(): # внешняя функция n = 5 def inner(): # вложенная функция print(n) inner() # 5 print(n) outer() # 5
Здесь вложенная локальная функция inner() выводит на консоль значение переменной n , которая определена во внешней функции outer(). Затем в функции outer() вызывается внутренняя функция inner().
При вызове функции outer() здесь мы ожидаемо увидим на консоли два раза число 5. Однако в данном случае вложенная функция inner() просто получает значение. Теперь возьмем другую ситуацию, когда вложенная функция присваивает значение переменной:
def outer(): # внешняя функция n = 5 def inner(): # вложенная функция n = 25 print(n) inner() # 25 print(n) outer() # 5 # 25 - inner # 5 - outer
При присвоении значения во вложенной функции: n = 25 будет создаваться новая переменная n, которая скроет переменную n из окружающей внешней функции outer. В итоге мы получим при выводе два разных числа. Чтобы во вложенной функции указать, что идентификатор во вложенной функции будет представлять переменную из окружающей функции, применяется выражение nonlocal :
def outer(): # внешняя функция n = 5 def inner(): # вложенная функция nonlocal n # указываем, что n - это переменная из окружающей функции n = 25 print(n) inner() # 25 print(n) outer() # 25
Все, что вы хотели узнать про области видимости в Python, но стеснялись спросить
Сегодня мы будем говорить о важных теоретических основах, которые необходимо понимать и помнить, чтобы писать грамотный, читаемый и красивый код. Мы будем вести речь об областях видимости переменных. Эта статья будет полезна не только новичкам, но и опытным программистам, которые пришли в Python из другого языка и хотят разобраться с его механиками работы.
Области видимости определяют, в какой части программы мы можем работать с той или иной переменной, а от каких переменная «скрыта». Крайне важно понимать, как использовать только те значения и переменные, которые нам нужны, и как интерпретатор языка себя при этом ведет. А еще мы посмотрим, как обходить ограничения, накладываемые областями видимости на действия с переменными. В Python существует целых 3 области видимости:
- Локальная
- Глобальная
- Нелокальная
Обычно, речь заходит про области видимости, когда происходит знакомство с функциями. На их примере мы и будем рассматривать работу областей видимости переменных.
Локальная область видимости
Рассмотрим функцию, которая выведет список some_list поэлементно:
def print_list(some_list): for element in some_list: print(element)
Здесь element и some_list – локальные переменные, которые видны только внутри функции, и которые не могут использоваться за ее пределами с теми значениями, которые были им присвоены внутри функции при ее работе. То есть, если мы в основном теле программы вызовем print(element) , то получим ошибку:
NameError: name 'element' is not defined
Теперь мы поступим следующим образом:
def print_list(some_list): for element in some_list: print(element) element = 'q' print_list([1, 2, 3]) print(element)
1 2 3 q
Здесь переменная element внутри функции и переменная с таким же именем вне ее – это две разные переменные, их значения не перекрещиваются и не взаимозаменяются. Они называются одинаково, но ссылаются на разные объекты в памяти. Более того, переменная с именем element внутри функции живет столько же, сколько выполняется функция и не больше. Но будьте аккуратны с тем, чтобы давать локальным и глобальным переменным одинаковые имена, сейчас покажу почему:
def print_list(some_list): for element in sudden_list: print(element) sudden_list = [0, 0, 0] print_list([1, 2, 3])
0 0 0
Обратите внимание на то, что интерпретатор не указал нам на ошибки. А все потому что sudden_list находится в глобальной области видимости, то есть изнутри функции print_list мы можем к нему обращаться, поскольку изнутри видно то, что происходит снаружи. По причине таких механик работы старайтесь называть локальные переменные внутри функции не так, как называете переменные в глобальной области видимости.
Здесь важно поговорить о константах. Интерпретатору Python нет разницы как вы называете переменную, поэтому код выше будет лучше переписать в следующем виде:
SUDDEN_LIST = [0, 0, 0] def print_list(some_list): for element in SUDDEN_LIST: print(element) print_list([1, 2, 3])
Теперь все на своих местах. Дело в том, что в Python нельзя каким-то образом строго определить константу, как объект, который не должен быть изменен. Так что то, как вы используете значение переменной, имя которой записано заглавными буквами, остается лишь на вашей совести. Другой вопрос, что таким способом записанная переменная даст понять тому, кто будет читать ваш код, что переменная нигде изменяться не будет. Или по крайней мере не должна.
Глобальная область видимости
В Python есть ключевое слово global , которое позволяет изменять изнутри функции значение глобальной переменной. Оно записывается перед именем переменной, которая дальше внутри функции будет считаться глобальной. Как видно из примера, теперь значение переменной candy увеличивается, и обратите внимание на то, что мы не передаем ее в качестве аргумента функции get_candy() .
candy = 5 def get_candy(): global candy candy += 1 print('У меня <> конфет.'.format(candy)) get_candy() get_candy() print(candy)
В результате получим:
У меня 6 конфет. У меня 7 конфет. 7
Однако менять значение глобальной переменной изнутри функции – не лучшая практика и лучше так не делать, поскольку читаемости кода это не способствует. Чем меньше то, что происходит внутри функции будет зависеть от глобальной области видимости, тем лучше.
Лайфхак: Чтобы не мучиться с именованием переменных, вы можете вынести основной код программы в функцию main() , тогда все переменные, которые будут объявлены внутри этой функции останутся локальными и не будут портить глобальную область видимости, увеличивая вероятность допустить ошибку.
Нелокальная область видимости
Появилось это понятие в Python 3 вместе с ключевым словом nonlocal . Логика его написания примерно такая же, как и у global . Однако у nonlocal есть особенность. Nonlocal используется чаще всего во вложенных функциях, когда мы хотим дать интерпретатору понять, что для вложенной функции определенная переменная не является локальной, но она и не является глобальной в общем смысле.
def get_candy(): candy = 5 def increment_candy(): nonlocal candy candy += 1 return candy return increment_candy result = get_candy()() print('Всего <> конфет.'.format(result))
Всего 6 конфет.
Насколько это полезно вам предстоит решить самостоятельно. Больше примеров вы можете найти здесь.
В качестве вывода можно сформулировать несколько правил:
- Изнутри функции видны переменные, которые были определены и внутри нее и снаружи. Переменные, определенные внутри – локальные, снаружи – глобальные.
- Снаружи функций не видны никакие переменные, определенные внутри них.
- Изнутри функции можно изменять значение переменных, которые определены в глобальной области видимости с помощью спецификатора global .
- Изнутри вложенной функции с помощью спецификатора nonlocal можно изменять значения переменных, которые были определены во внешней функции, но не находятся в глобальной области видимости.
Также хочу пригласить всех желающих на бесплатный вебинар от OTUS, где изучим такой инструмент, как аннотация типов в Python: обсудим причины, по которым его многие недооценивают, рассмотрим ряд примеров из боевой практики, когда аннотация типов могла спасти или спасла ситуацию. Поговорим о том, как и когда внедрять проверку типов на своих проектах.
- python
- области видимости
- Блог компании OTUS
- Python
How to fix “Shadows name from outer scope” in PyCharm
PyCharm is a hugely popular Python IDE developed by JetBrains. PyCharm supports pretty much anything you can think of when you mention a modern IDE, such as debugging, syntax highlighting, project management, smart prompt, automatic completion, unit testing, version control… In addition to that, PyCharm also provides advanced web development for Django, doing data science with Anaconda and even IronPython.
While working with PyCharm, you might encounter some warning, a few of them are not Python error messages. “Shadows name from outer scope” is one of the most common error message that spread confusion among new users. This article will explain why it happens and what you can do to avoid it.
Why does “Shadows name from outer scope” happens?
Let’s take the code snippets below as our example :
the_list = [1, 2, 3] def data_log(the_list): # Warning: "Shadows 'the_list' from outer scope print(the_list) data_log(the_list)
You can clearly see that in the first line we define the_list in the global scope, and inside the function data_log , we reused that name. Reusing names in and out of functions will be referred as “shadowing names” in PyCharm, therefore causes “Shadows name from outer scope”. This is just a warning and doesn’t make your code unable to run.
While there’s nothing wrong with the example and it runs just fine, it can be the beginning of strange behaviour if you continue to ignore the error message.
Imagine data_log takes in not one but a few more arguments, and the inner logic of it gets more complex. You decide to manually rename the_list to array_object , but leaves a few miss here and there.
If you run the code again, it might just work, but the results will certainly be strange. That is because now the_list refers to the global object, array_object refers to the local one, they are different and you mixed them back and forth inside your function.
Avoid “Shadows name from outer scope”
Now that we know the reason behind the warning, the solution is quite simple : You need to avoid reusing names in your code.
Doing so will not only reduce weird behaviour in your code, but also make debugging easier, since you would end up with NameError if Python cannot find a global name or local name.
One more thing to remember is that you should not “shadow” modules, classes and functions the same way you shadow names, too.
This is how our example will look like if it doesn’t shadow names :
the_list = [1, 2, 3] def data_log(a_list): # Warning: "Shadows 'the_list' from outer scope print(a_list) data_log(the_list)
Or you can just move the global variables to another function like so :
def data_log(a_list): print(a_list) def main(): the_list = [1, 2, 3] data_log(the_list)
We’ve also covered how to fix locale.Error: unsupported locale setting once and for all, which might be a good read for new Python developers. We hope this article will help you solve your problem and write better, cleaner, error-free code.