Все данные в языке Python представлены объектами и отношениями между объектами. У любого объекта есть тип. Для определения собственных типов в языке Python есть классы. Классы позволяют описать поведение объектов данного класса.
class MyClass: a = 10 def func(self): print('Hello')
В отличие от функций тело классов исполняется в момент определения самого класса. Для тела класса также как и для функций создается отдельный неймспейс и те имена, которые в этом неймспейсе остались затем закрепляются за объектом класса:
- MyClass.a
- MyClass.func
a и func являются атрибутами класса MyClass.
Механизм инстанцирования или конструктор
Конструктор это единственный механизм, которым создаются новые объекты.
Для создания списка мы также можем записать lst = list() или lst = [].
Для самого класса верно, что:
- мы можем вызвать от него конструктор;
- и использовать атрибуты (т,е. присваивать новые или переиспользовать какие-то старые, которые мы уже определили).
Для экземпляров классов можно лишь пользоваться атрибутами. Также как и с классом с каждым экземпляром нашего класса будет ассоциировано пространство имен. И именно в этом пространстве имен создаются новые имена и присваиваются им новые объекты. Что у объектов классов, что у объектов экземпляров есть собственное пространство имен и атрибуты в этом пространстве имен можно создавать и изменять.
Для того чтобы определить поведение конструктора нужно внутри класса определить функцию init:
В общем функция init принимает больше одного аргумента. Первый аргумент, который она принимает это уже какой-то экземпляр нашего класса. Сначала создается пустой (без атрибутов) объект класса. Уже этот объект передается в функцию init в качестве self. Идеологически функция init всего лишь устанавливает атрибуты для нашего объекта self.
Функция init может также принимать и другие аргументы:
Интерпретатор сначала ищет метод в неймспейсе самого экземпляра, а затем пытается найти этот атрибут в неймспейсе класса.
При вызове метода inc в неймспейсе нашего экземпляра содержится только имя count поэтому интерпретатор идет дальше и смотрит в неймспейсе класса. Когда интерпретатор в процессе поиска имени доходит до самого класса и если это имя внутри класса является еще и функцией, он считает, что это самое имя для нашего экземпляра является методом и по аналогии с конструктором, когда мы вызываем метод мы подставляем себя в качестве аргумента функции.
Любому атрибуту и имени внутри неймспейса экземпляра должен соответствовать какой-то объект. Для x.inc это так называемый связанный метод (bound method). Связанные методы это такие специальные объекты. Мы сначала находим функцию, которой соответствует наш x.inc, а затем связываем ее с объектом. Вызов x.inc() эквивалентен вызову Counter.inc(x).
Метод add_tags пытается добавить теги в атрибут self.tags. Однако в конструкторе мы явно не определяем tags у каждого из экземпляров. Поэтому add_tags не может найти tags в экземпляре, но находит его в классе. Метод add_tags идеологически не правильный потому что меняет класс, а не объект. tags должен быть атрибутом конкретного экземпляра, потому что теги должны ассоциироваться с песней, а не с классом.
В результате выполнения следующего кода будут выведены числа 2, 3, 3.
При первом вызове метода bar для объекта
self.val = self.val + 1
выполняется правая часть выражения (в области видимости объекта нет переменной val - берется из области видимости КЛАССА) и присваивается НОВОЙ переменной в области видимости ОБЪЕКТА.
В общем функция init принимает больше одного аргумента. Первый аргумент, который она принимает это уже какой-то экземпляр нашего класса. Сначала создается пустой (без атрибутов) объект класса. Уже этот объект передается в функцию init в качестве self. Идеологически функция init всего лишь устанавливает атрибуты для нашего объекта self.
Функция init может также принимать и другие аргументы:
Вместе с синтаксисом класса к нам приходят три новых типа:
- объекты класса - описывают сами классы и их поведение;
- объекты экземпляра - объекты типом которых является наш класс;
- связанные методы - они ссылаются на объект и ссылаются на функцию внутри класса.
С объектами класса мы можем вызвать конструктор, а во вторых создавать атрибуты и обращаться к ним. У объектов экземпляров мы можем лишь создавать атрибуты и обращаться к ним. А связанные методы мы можем лишь вызывать.
Методы
Пример методов у списков:>>> x = [2, 3, 1] >>> x.sort() >>> print(x) [1, 2, 3] >>> x.append(4) >>> print(x) [1, 2, 3, 4] >>> top = x.pop() >>> print(top) 4 >>> print(x) [1, 2, 3]
Интерпретатор сначала ищет метод в неймспейсе самого экземпляра, а затем пытается найти этот атрибут в неймспейсе класса.
При вызове метода inc в неймспейсе нашего экземпляра содержится только имя count поэтому интерпретатор идет дальше и смотрит в неймспейсе класса. Когда интерпретатор в процессе поиска имени доходит до самого класса и если это имя внутри класса является еще и функцией, он считает, что это самое имя для нашего экземпляра является методом и по аналогии с конструктором, когда мы вызываем метод мы подставляем себя в качестве аргумента функции.
Любому атрибуту и имени внутри неймспейса экземпляра должен соответствовать какой-то объект. Для x.inc это так называемый связанный метод (bound method). Связанные методы это такие специальные объекты. Мы сначала находим функцию, которой соответствует наш x.inc, а затем связываем ее с объектом. Вызов x.inc() эквивалентен вызову Counter.inc(x).
Переменные класса и переменные экземпляра
Если атрибут, который мы не нашли в экземпляре, но нашли в классе не является функцией, то мы можем просто его использовать.Метод add_tags пытается добавить теги в атрибут self.tags. Однако в конструкторе мы явно не определяем tags у каждого из экземпляров. Поэтому add_tags не может найти tags в экземпляре, но находит его в классе. Метод add_tags идеологически не правильный потому что меняет класс, а не объект. tags должен быть атрибутом конкретного экземпляра, потому что теги должны ассоциироваться с песней, а не с классом.
В результате выполнения следующего кода будут выведены числа 2, 3, 3.
class A: val = 1 def foo(self): A.val += 2 def bar(self): self.val += 1 a = A() b = A() a.bar() a.foo() c = A() print(a.val) # 2 print(b.val) # 3 print(c.val) # 3
self.val = self.val + 1
выполняется правая часть выражения (в области видимости объекта нет переменной val - берется из области видимости КЛАССА) и присваивается НОВОЙ переменной в области видимости ОБЪЕКТА.
Комментариев нет:
Отправить комментарий