Почему нам нужно указывать self в конструкторе __init__, когда экземпляр еще не создан? - PullRequest
0 голосов
/ 01 ноября 2018

При выполнении следующего кода:

class Test():
    def __init__(self):
        self.hi_there()
        self.a = 5
    def hi_there(self):
        print(self.a)

new_object = Test()
new_object.hi_there()

Я получил ошибку:

Traceback (most recent call last):
  File "/root/a.py", line 241, in <module>
    new_object = Test()
  File "/root/a.py", line 233, in __init__
    self.hello()
  File "/root/a.py", line 238, in hello
    print(self.a)
AttributeError: 'Test' object has no attribute 'a'

Зачем нам нужно указывать self внутри функции, пока объект еще не инициализирован? Возможность вызова функции hi_there() означает, что объект уже установлен, но как получится, если другие переменные, относящиеся к этим экземплярам, ​​еще не были инициализированы? Что такое self внутри функции __init__, если это еще не «полный» объект?

Очевидно, что эта часть кода работает:

class Test():
    def __init__(self):
        #self.hi_there()
         self.a = 5
         self.hi_there()

    def hi_there(self):
       print(self.a)


new_object = Test()
new_object.hi_there()

Я пришел из мира C ++, там вы должны объявить переменные, прежде чем назначать их. Я полностью понимаю ваше использование self. Хотя я не понимаю, какой смысл использовать self внутри __init__(), если объект self не полностью инициализирован.

Ответы [ 3 ]

0 голосов
/ 01 ноября 2018

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

Кто-то исправляет меня, если я ошибаюсь, но в Python определение класса или функции является динамическим. Я имею в виду, что определение класса или функции происходит во время выполнения: это регулярные операторы, которые выполняются так же, как и другие.

Эта языковая функция позволяет использовать мощные функции, такие как декорирование поведения функции, для обогащения ее дополнительными функциями (см. Декораторы).

Поэтому, когда вы создаете экземпляр класса Test, вы пытаетесь вызвать функцию hello() до того, как явно установите значение a. Поэтому класс Test еще не знает о своем атрибуте a. Это должно быть прочитано последовательно.

0 голосов
/ 01 ноября 2018

На самом деле, объект уже был создан при вызове __init__. Вот почему вам нужно self в качестве параметра. И из-за того, как Python работает внутренне, у вас нет доступа к объектам без self (Имейте в виду, что его не нужно называть self, вы можете называть его как угодно, если только оно является допустимым именем . Экземпляр всегда является первым параметром метода, каким бы ни было его имя.).

Правда в том, что __init__ не создает объект, он просто инициализирует его. Существует метод класса с именем __new__, который отвечает за создание экземпляра и его возврат. Вот где создается объект.

Теперь, когда объект получает атрибут a. Это в __init__, но у вас есть доступ к его методам внутри __init__. Я не совсем осведомлен о том, как работает создание объектов, но методы уже установлены, как только вы дойдете до этой точки. Это не происходит со значениями, поэтому они недоступны, пока вы сами не определите их в __init__.

В основном Python создает объект, предоставляет ему его методы, а затем дает вам экземпляр, чтобы вы могли инициализировать его атрибуты.

EDIT

Еще одна вещь, которую я забыл упомянуть. Так же, как вы определяете __init__, вы можете определить __new__ самостоятельно. Это не очень часто, но вы делаете это, когда вам нужно изменить фактическое создание объекта. Я видел это только при определении метаклассов ( Что такое метаклассы в Python? ). Другой метод, который вы можете определить в этом случае - __call__, что дает вам еще больший контроль.

0 голосов
/ 01 ноября 2018

Нет магии. Ко времени вызова __init__ объект создается и его методы определены, но у вас есть возможность установить все атрибуты экземпляра и выполнить всю остальную инициализацию. Если вы посмотрите на исполнение в __init__:

def __init__(self):
    self.hi_there()
    self.a = 5
def hi_there(self):
    print(self.a)

первое, что происходит в __init__, это то, что hi_there вызывается. Метод уже существует, поэтому вызов функции работает, и мы возвращаемся к hi_there(), что делает print(self.a). Но это проблема: self.a еще не установлен, поскольку это происходит только во второй строке __init__, но мы вызвали hi_there из первой строки __init__. Выполнение не достигло строки, в которой вы задали self.a = 5, поэтому вызов метода self.hi_there(), выпущенный до того, как это назначение может использовать self.a, никак не может быть выполнен. Вот почему вы получаете AttributeError.

...