Путаница в области видимости на уроках питона - PullRequest
0 голосов
/ 05 октября 2019

В настоящее время я рассматриваю учебники по python на python.org. Я пришел из C ++ и из учебника по классам (https://docs.python.org/3/tutorial/classes.html) я вижу, что область видимости схожа с областью видимости в C ++. В области определения объема и вложенности говорится следующее:

"В любое времяво время выполнения существует как минимум три вложенные области, пространства имен которых доступны напрямую:
- самая внутренняя область, которая ищется первой, содержит локальные имена
- области действия любых включающих функций, которые ищутся, начиная сближайшая окружающая область, содержит нелокальные, но также и неглобальные имена
- следующая за последней область содержит глобальные имена текущего модуля
- самая внешняя область (последняя) - это пространство имен, содержащее встроенныеnames "

Однако я попытался использовать следующий код на той же странице:

class Dog:

    tricks = []             

    def __init__(self, name):
        self.name = name

    def add_trick(self, trick):
        self.tricks.append(trick)     #this is the troublesome self

>>> d = Dog('Fido')     
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')    #without the self this complains
>>> e.add_trick('play dead')
>>> d.tricks                
['roll over', 'play dead']

Если я удаляю self в self.tricks.append(trick), код не скомпилируется и не выдастNameError: name 'tricks' is not defined при вызове функции d.add_trick('roll over').

Почему это происходит? Как я понял из абзаца выше, функция add_trick должна выглядетьдля переменной с именем tricks сначала в своей локальной области, а затем, если она не найдена, в ближайшей охватывающей области, которая является областью действия класса Dog, и там она должна найти ее ,без необходимости использования self. Чего мне не хватает?

Ответы [ 3 ]

2 голосов
/ 05 октября 2019

Ваша ошибка в том, что вы думаете, что класс - это сфера. Это не так. Как указано в приведенном вами документе, области видимости - это функции или модули.

1 голос
/ 05 октября 2019

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

Нелокальная область действия предназначена для включающих функций . Объявление класса не является функцией. Его пространство имен отбрасывается после того, как оно используется для создания __dict__ объекта класса, поэтому переменные на уровне класса не могут создавать нелокальные значения во вложенных функциях. Думайте о назначениях на уровне класса и чтениях переменных как о неявных присваиваниях и чтениях из скрытого мнения, а не как о локальных функциях. (Метаклассы могут даже заменить этот скрытый dict некоторым другим отображением.)

Но область видимости класса добавляет один нелокальный, __class__. Это редко используется напрямую, но это важно для формы с нулевым аргументом super().

Это сам объект класса, поэтому он не инициализируется до тех пор, пока не завершится выполнение объявления класса. Таким образом, __class__.tricks будет работать внутри метода, если он вызывается после , когда выполняется тело класса (обычный случай), но не если он вызывается во время выполнения тела класса.

Есть и другие области, о которых нужно знать в Python. Понимания создают локальную область видимости, подобно функциям. (Они в основном скомпилированы как функции генератора - типа с yield внутри.) Кроме того, перехваченные исключения ограничены их условием обработки.

Вы можете увидеть пространство имен локальных пользователей, используя встроенные locals() и глобальные переменные. используя globals(). Встроенная область - это просто встроенный модуль. Нелокалы хитры. Они действительно появятся в locals(), если компилятор увидит, что они используются. Объекты функций сохраняют ссылку на нелокальные значения, которые они используют в своем атрибуте __closure__, который является кортежем ячеек.

0 голосов
/ 05 октября 2019

Помогает ли этот пример:

In [27]: foobar = 'pre'                                                         
In [28]: class AClass(): 
    ...:     print('class def:', foobar) 
    ...:     foobar = 'class' 
    ...:     def __init__(self): 
    ...:         print('init:', foobar) 
    ...:         print(self.foobar) 
    ...:         self.foobar='instance' 
    ...:         print(self.foobar) 
    ...:                                                                        
class def: pre                  
In [29]: AClass.foobar                                            
Out[29]: 'class'

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

In [30]: x = AClass()                                                           
init: pre
class
instance
In [31]: x.foobar                                                               
Out[31]: 'instance'
In [32]: x.__class__.foobar                                                     
Out[32]: 'class'

Когда мы определяем экземпляр, foobar приходит из внешнего глобального пространства имен,self.foobar первоначально обращается к пространству имен класса, но затем переопределяется как переменная экземпляра.

Изменение глобального foobar отражено в следующем создании экземпляра:

In [33]: foobar = 'post'                                                        
In [34]: y = AClass()                                                           
init: post
class
instance
In [35]: y.__class__.foobar                                                     
Out[35]: 'class'

Ваше руководствоpage, в разделе 9.3.1. Class Definition Syntax говорится, что в определении класса есть новое локальное пространство имен. Вот где определяется foobar='class'. Но после определения класса это пространство имен переименовывается в AClass.

Создание экземпляра выполняется вне определения класса. Таким образом, он «видит» глобальный foobar, а не локальный класс. Он должен указывать пространство имен.

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

...