Короткий опис правил розміщення?


472

Якими саме правилами визначено Python?

Якщо у мене є якийсь код:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Де xзнайдено? Нижче наведено список можливих варіантів:

  1. У вихідному вихідному файлі
  2. У просторі імен класів
  3. У визначенні функції
  4. У змінній індексу циклу for
  5. Всередині петлі для

Також є контекст під час виконання, коли функція spamпередається десь в іншому місці. А може лямбда-функції проходять трохи інакше?

Десь має бути проста посилання або алгоритм. Це заплутаний світ для проміжних програмістів Python.


2
Правила розміщення описуються досить коротко - але також повністю - в документації на Python: docs.python.org/3/reference/… .
jefe2000

Відповіді:


420

Насправді, стисле правило для роздільної здатності Python, від Learning Python, 3-е. Ред. . (Ці правила стосуються імен змінних, а не атрибутів. Якщо ви посилаєтесь на них без періоду, ці правила застосовуються.)

Правило LEGB

  • L okal - Імена, присвоєні будь-яким чином у межах функції ( defабо lambda) і не оголошені глобальними в цій функції

  • E nclosing-function - Імена, присвоєні в локальній області будь-яких і всіх статично огороджуючих функцій ( defабо lambda), від внутрішньої до зовнішньої

  • G lobal (module) - Імена, присвоєні на верхньому рівні файлу модуля, або шляхом виконання globalоператора у defфайлі

  • B uilt в (Python) - Імена встановлювати в вбудованому модулі імен: open, range, SyntaxErrorі т.д.

Так, у випадку з

code1
class Foo:
    code2
    def spam():
        code3
        for code4:
            code5
            x()

forЦикл не має свій власний простір імен. У порядку LEGB були б сфери застосування

  • L: місцевий в def spamcode3, code4і code5)
  • E: Будь-які додаткові функції (якщо весь приклад були в іншому def)
  • G: Чи були xдекларовані глобально в модулі (в code1)?
  • B: Будь-який вбудований xу Python.

xйого ніколи не знайдуть code2(навіть у тих випадках, коли ви, можливо, очікуєте цього, див . відповідь Анті або тут ).


45
Як застереження до глобального доступу - читання глобальної змінної може статися без явного декларування, але запис на неї без оголошення глобального (var_name) натомість створить новий локальний екземпляр.
Пітер Гібсон

12
Насправді @Peter, global(var_name)синтаксично неправильно. Правильний синтаксис був би global var_nameбез дужок. Однак у вас є дійсна точка.
мартіно

Якщо так, то чому змінну "y" foo не видно на "bar" нижче: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer

3
@Jonathan: Оскільки кожен yпишеться і global yдекларацій немає - дивіться коментар @ Петра.
martineau

@LakshmanPrasad Вона потрапляє у "E", але має одну особливу поведінку, яку варто згадати: вона є змінною класу, тому вона є "глобальною" серед її об'єктів. Присвоєння йому буде приводити до непередбачуваного і важко проблемам налагодження , якщо ви не знаєте , що ви робите.
Ctrl-C

157

По суті, єдине, що вводить нову область в Python - це визначення функції. Класи - це особливий випадок, коли все, що визначено безпосередньо в тілі, розміщується в просторі імен класу, але вони не є доступними безпосередньо в межах методів (або вкладених класів), які вони містять.

У вашому прикладі є лише 3 області, в яких буде шукано х:

  • Область спаму - містить усе, що визначено у коді3 та коді5 (а також код4, ваша змінна петля)

  • Глобальний діапазон - містить усе, що визначено в code1, а також Foo (і що б не змінилося після нього)

  • Вбудований простір імен. Трохи особливий випадок - він містить різні функції вбудованого Python та типи, такі як len () та str (). Як правило, це не повинно бути змінено жодним кодом користувача, тому очікуйте, що він містить стандартні функції та більше нічого.

Більше діапазонів з'являється лише тоді, коли ви вводите вкладену функцію (або лямбда) в картинку. Вони будуть вести себе майже так, як ви очікували, проте. Вкладена функція може отримати доступ до всього, що знаходиться в локальній області, а також до всього, що знаходиться в області застосування вкладеної функції. напр.

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

Обмеження:

До змінних в областях, відмінних від змінних локальної функції, можна отримати доступ, але їх неможливо відновити до нових параметрів без додаткового синтаксису. Натомість присвоєння створить нову локальну змінну замість впливу на змінну в батьківській області. Наприклад:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

Для того, щоб фактично змінити прив’язки глобальних змінних в межах функції, потрібно вказати, що змінна є глобальною за допомогою глобального ключового слова. Наприклад:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

В даний час немає можливості зробити те ж саме для змінних у застосуванні областей функцій , але Python 3 вводить нове ключове слово, nonlocalяке буде діяти аналогічно глобальному, але для вкладених областей функцій.


111

Грунтовної відповіді щодо часу Python3 не було, тому я тут зробив відповідь. Більшість того, що описано тут, детально описано у розділі 4.2.2 Роздільна здатність імен документації Python 3.

Як передбачено в інших відповідях, існують 4 основні сфери, LEGB - для локальних, закритих, глобальних та вбудованих. Окрім них, існує особливий спектр, орган класу , який не містить вкладеної області для методів, визначених у класі; будь-які призначення в тілі класу роблять змінну звідти на зв'язаною в тілі класу.

Тим більше, жодного блокового твердження, окрім defіclass створює змінну область. У Python 2 розуміння списку не створює змінної області, однак у Python 3 змінна циклу в межах розуміння списку створюється в новій області.

Продемонструвати особливості корпусу класу

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

Таким чином, на відміну від функції функції, ви можете перепризначити змінну на те саме ім'я в тілі класу, щоб отримати змінну класу з тим самим іменем; подальший пошук цього імені вирішує замість змінної класу.


Одне з найбільших сюрпризів для багатьох новачків Python - це те, що forцикл не створює змінної області. У Python 2 розуміння списку також не створюють сфери застосування (в той час як генератори та диктують розуміння!) Натомість вони просочують значення у функції або глобальній області застосування:

>>> [ i for i in range(5) ]
>>> i
4

Зрозуміння можуть бути використані як хитрий (або жахливий, якщо ви хочете) спосіб внесення змінних змінних в лямбда-виразах у Python 2 - вираз лямбда створює змінну область, як і у defзаяві, але в межах лямбда жодні заяви не дозволені. Призначення, що є висловлюванням в Python, означає, що не допускаються призначення змінних у лямбда, але розуміння списку - це вираз ...

Така поведінка була зафіксована в Python 3 - ніякі вираження розуміння чи генератори не протікають змінними.


Глобальний справді означає область модуля; основним модулем пітона є __main__; всі імпортовані модулі доступні через sys.modulesзмінну; отримати доступ до __main__одного можна використовувати sys.modules['__main__'], або import __main__; цілком прийнятно отримати доступ та призначити там атрибути; вони відображатимуться як змінні у глобальній області основного модуля.


Якщо ім’я коли-небудь присвоюється в поточному діапазоні (крім сфери класу), воно вважатиметься приналежним до цього діапазону, інакше вважатиметься приналежним до будь-якого вкладеного області, що присвоює змінній (можливо, вона не буде призначена все ж, або зовсім не), або, нарешті, глобальний розмах. Якщо змінна вважається локальною, але вона ще не встановлена ​​або була видалена, то читання значення змінної призведе до UnboundLocalError, що є підкласом NameError.

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

Область може заявити, що вона явно хоче змінити глобальну змінну (область модуля) за допомогою глобального ключового слова:

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

Це також можливо, навіть якщо воно було затінене в області застосування:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

У python 2 не існує простого способу змінити значення в області, що додається; зазвичай це моделюється за допомогою змінного значення, такого як список довжиною 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

Однак у python 3 nonlocalна допомогу приходить:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

nonlocalДокументація каже , що

Імена, перелічені в нелокальному твердженні, на відміну від перелічених у глобальній заяві, повинні посилатися на раніше існуючі прив'язки в обкладинці (область, у якій має бути створена нова прив'язка, не може бути визначена однозначно).

тобто nonlocalзавжди посилається на найпотужнішу зовнішню неглобальну область, де ім'я було пов'язане (тобто присвоєне, в тому числі використовується як forцільова змінна, у withпункті або як функціональний параметр).


Будь-яка змінна, яка не вважається локальною для поточного обсягу, або будь-яка охоплює область, є глобальною змінною. У глобальному словнику модуля знайдено глобальне ім'я; якщо його не знайдено, глобальний слід шукати з вбудованого модуля; назву модуля було змінено з python 2 на python 3; у python 2 це було, __builtin__а в python 3 зараз його називають builtins. Якщо ви призначите атрибут вбудованого модуля, його буде видно після цього будь-який модуль як читабельну глобальну змінну, якщо тільки цей модуль не затінює їх власною глобальною змінною з тим же ім'ям.


Читання вбудованого модуля також може бути корисним; припустимо, що ви хочете функцію друку в стилі python 3 в деяких частинах файлу, але інші частини файлу все ще використовують printоператор. У Python 2.6-2.7 ви можете отримати функцію Python 3 за printдопомогою:

import __builtin__

print3 = __builtin__.__dict__['print']

from __future__ import print_functionФактично не імпортує printфункції ніде в Python 2 - замість цього він просто відключає правил синтаксичного аналізу для printзатвердження в поточному модулі, обробки , printяк і будь-який інший ідентифікатор змінної, і , таким чином , дозволяючи printфункції розглядати в вбудованих команд.


23

Правила розміщення для Python 2.x були окреслені вже в інших відповідях. Єдине, що я хотів би додати, це те, що в Python 3.0 також існує концепція не локальної сфери (позначена ключовим словом "нелокальне"). Це дозволяє безпосередньо отримувати доступ до зовнішніх областей та відкриває можливість робити деякі акуратні трюки, включаючи лексичні закриття (без негарних хакерів, що стосуються змінних предметів).

EDIT: Ось PEP з додатковою інформацією про це.


23

Трохи більш повний приклад сфери застосування:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

вихід:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200

6
Це чудова відповідь. Однак я вважаю, що відмінності між methodі method_local_refслід висвітлити. methodможе отримати доступ до глобальної змінної та роздрукувати її як у 5. Global x. Але method_local_refне може, тому що пізніше він визначає локальну змінну з тим самим іменем. Ви можете перевірити це, видаливши x = 200рядок і побачити різницю
kiril

@brianray: Що з z?
Малик А. Румі

@kiril Я додав замітку про це
brianray

@ MalikA.Rumi я видалив z, як це було не цікаво
brianray

Дивно, але це єдине чітке пояснення областей Python, які я міг знайти на всіх SO. Просто використовуючи дуже базовий приклад. Дякую!
not2qubit

13

Python вирішує ваші змінні - загалом - з трьох доступних просторів імен.

У будь-який час під час виконання є щонайменше три вкладені області застосування, простори імен яких доступні безпосередньо: найпотужніший діапазон, який шукається спочатку, містить локальні назви; простори імен будь-яких огороджувальних функцій, які шукаються, починаючи з найближчої області застосування; середній діапазон, який шукається далі, містить глобальні назви поточного модуля; а самий зовнішній обсяг (останній пошук) - це область імен, що містить вбудовані імена.

Є дві функції: globalsі localsякі показують вам вміст двох цих просторів імен.

Простори імен створюються пакетами, модулями, класами, побудовою об'єктів та функціями. Інших ароматів просторів імен немає.

У цьому випадку виклик функції, названої x, повинен бути вирішений у місцевому просторі імен або глобальному просторі імен.

Місцевим у цьому випадку є тіло функції методу Foo.spam.

Глобальне - добре - глобальне.

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

Інших областей немає. Оператор for(та інші складні висловлювання, такі як ifі try) не створюють нових вкладених областей. Тільки визначення (пакети, модулі, функції, класи та екземпляри об'єкта.)

Всередині визначення класу імена є частиною простору імен класів. code2, наприклад, має бути кваліфіковано за назвою класу. Взагалі Foo.code2. Однак, self.code2це також буде працювати, тому що об'єкти Python дивляться на клас, що міститься, як запасний.

Об'єкт (екземпляр класу) має змінні екземпляри. Ці імена знаходяться в просторі імен об'єкта. Вони повинні бути кваліфіковані об'єктом. ( variable.instance.)

Із класового методу ви маєте місцевих та глобальних. Ви скажете self.variableвибрати екземпляр як простір імен. Ви помітите, що selfце аргумент для кожної функції члена класу, що робить її частиною локального простору імен.

Див Python Scope правила , Python Область , Область видимості змінної .


5
Це застаріло. Починаючи з 2.1 (7 років тому) існує більше двох областей, оскільки вкладені функції вводять нові області застосування, тому функція в межах функції матиме доступ до її локальної сфери, обсягу функцій, що додаються, та загальної сфери (також вбудованих).
Брайан

Вибачте, це вже не так. Python has two namespaces available. Global and local-to-something.
Різван Кассім

9

Де знайдено х?

x не знайдено, як ви його не визначили. :-) Його можна знайти в коді1 (глобальний) або код3 (локальний), якщо його помістити.

code2 (члени класу) не видно коду всередині методів одного класу - ви зазвичай отримуєте доступ до них за допомогою self. code4 / code5 (циклі) живуть у тій самій області, що і code3, тому якби ви написали в x там, ви б змінили екземпляр x, визначений у code3, не роблячи новий x.

Python має статичний обсяг, тому якщо ви передаєте "спам" на іншу функцію, спам все ще матиме доступ до глобальних даних в модулі, з якого він походить (визначений у коді1), та будь-яких інших областях, що містять область (див. Нижче). До учасників code2 знову можна отримати доступ через себе.

лямбда не відрізняється від деф. Якщо у вас є лямбда, яка використовується всередині функції, це те саме, що визначити вкладену функцію. У Python 2.2 далі доступні вкладені області застосування. У цьому випадку ви можете зв'язати х на будь-якому рівні функції вкладення, і Python підбере найпотаємніший екземпляр:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

fun3 бачить екземпляр x з найближчої області, що містить область, яка є областю функції, пов'язаною з fun2. Але інші x екземпляри, визначені fun1 та глобально, не впливають.

До nested_scopes - в Python pre-2.1 та 2.1, якщо ви спеціально не попросите цю функцію за допомогою імпорту від майбутнього - fun1 та fun2, сфери fun2 не помітні для fun3, тому відповідь S.Lott справедлива, і ви отримаєте глобальний х :

0 0

1

У Python,

будь-яка змінна, якій присвоєно значення, є локальною для блоку, в якому відображається призначення.

Якщо змінну неможливо знайти в поточній області, зверніться до порядку LEGB.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.