Определение классов в языке Python

Все данные в языке 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 может также принимать и другие аргументы:


Вместе с синтаксисом класса к нам приходят три новых типа:
  • объекты класса - описывают сами классы и их поведение;
  • объекты экземпляра - объекты типом которых является наш класс;
  • связанные методы - они ссылаются на объект и ссылаются на функцию внутри класса.
С объектами класса мы можем вызвать конструктор, а во вторых создавать атрибуты и обращаться к ним. У объектов экземпляров мы можем лишь создавать атрибуты и обращаться к ним. А связанные методы мы можем лишь вызывать.


Методы

Пример методов у списков:
>>> 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
При первом вызове метода bar для объекта
   self.val = self.val + 1
выполняется правая часть выражения (в области видимости объекта нет переменной val - берется из области видимости КЛАССА) и присваивается НОВОЙ переменной в области видимости ОБЪЕКТА.

Комментариев нет:

Отправить комментарий