Классы в Python
  1. Атрибуты класса foo = ИмяКласса() содержат поля и методы:
  2. Поляя
    1. public self.a (обращение: foo.a)
    2. protected self._b (обращение: foo._b)
    3. private self.__c (обращение: foo._ИмяКласса__c)
  3. Методы
    1. public self.func(self,...) (обращение: foo.func(...))
    2. private self.__func(self,...)
    3. магические self.__func__(self,...)
      1. Конструктор __new__ (обычныо не используется)
      2. Инициализатор __init__(self, аргументы...)
      3. Деструктор __del__ (отработает, даже если __init__ упадёт)
  4. Примеры:
# Пустой класс
class ИмяКласса:
	pass
 
# Пример №1
class ИмяКласса:
	a = 1  
	
	def __init__(self, b, c):
		# Правило хорошего тона - все поля должны возникнуть внутри __init__-а.
		self.b = b
		self.c = c
 
foo1 = ИмяКласса(2,3)
foo2 = ИмяКласса(4,5)
 
ИмяКласса.a = 10  # Значение поля a поменялось у всех переменных класса ИмяКласса
print(foo1.a, foo2.a)
 
foo1.a = 20  # Значение поля a поменяется только у переменной foo1.a
print(foo1.a, foo2.a)
Наследование
  1. Для наследования после имени класса указывается родительский класс: class B(A):
  2. Передаются все атрибуты, даже __init__ с инициализируемыми в нём полями. Для того, чтобы атрибуты были новыми, они просто объявляются с теми же именами.
  3. Private поля наследуются только в общедоступном виде self._РодительскийКласс__a
  4. Примеры:
class A:
    def __init__(self, a=1):
        self.a = a
        
class B(A):
    pass
        
class C(A):
    def __init__(self):
        super().__init__(2)
        # Дальше инит продолжается стандартно
        ...
 
print(B().a)  # 1
print(C().a)  # 2
Перегрузка методов
  1. Беcклассовая перегрузка методов разное исполнение методов класса при разных передаваемых аргументах (тип, длина и т.д.). Например, функция разности может возвращать число при численных аргументах, строку при аргументах-строках.
  2. Перегрузка методов в классе в основном используется для магических методов.
  3. Логические внутренние методы класса:
    1. __eq__ a == b
    2. __ne__ a != b
    3. __lt__ a < b
    4. __le__ a <= b
    5. __gt__ a > b
    6. __ge__ a >= b
  4. Арифметические внутренние методы класса:
    1. __add__ a + b
    2. __mul__ a * b
    3. __sub__ a - b
    4. __mod__ a % b
    5. __truedib__ a / b
  5. Другие методы:
    1. __repr__(self) как виден экземпляр класса в отладчике и консоли
    2. __str__(self) как объект превратится в строку (например, в print)
    3. __call__(self, args) вызов экземпляра класса как функции
    4. __hash__(self) вычисляет хеш для экземпляра класса hash(...)
      1. Он нужен, чтобы понимать различие между переменными этого класса. Например, когда переменные класса образуют set или ключи dict.
  6. Список магических методов для перегрузки: https://pythonworld.ru/osnovy/peregruzka-operatorov.html
  7. Примеры:
class A:
	def __init__(self, a=1, b=2, c=3):
		self.a = a
		self.b = b
		self.c = c
 
	def __str__(self):
		return f"Класс А с полями a={self.a} и b={self.b}"
 
	def __eq__(self, other):
		return self.a == other.a and self.b = other.b
 
	def __hash__(self):
        # Здесь хэш считается по tuple, в который включены два поля.
        # Хэши для двух экземпляров класса будут разные, если значение хотя бы одно из полей у них разное.
        # Логически это как будто очень близко к __eq__, но технически используется для совсем других целей.
        return hash((self.data, self.tag))
 
foo1 = A(1,2,3)
foo2 = A(1,2,4)
print(foo1)
print(foo1 == foo2)