Загрузка [MathJax]/jax/output/HTML-CSS/fonts/TeX/fontdata.js
 

printЯзык Python

printКлассы

С точки зрения объектно-ориентированного программирования любые значения (числа, строки, списки, кортежи и т.д.), на которые ссылаются переменные, являются объектами.

Объект представляет собой конкретный предмет или сущность (реальную или абстрактную), имеющую четко определенное функциональное назначение в данной предметной области. Объект обладает состоянием, поведением и идентичностью; структура и поведение схожих объектов определяет общий для них класс; термины "экземпляр класса" и "объект" взаимозаменяемы.

Узнать класс объекта можно с помощью функции type:

a=5
print(type(a)) # <class 'int'>
s='abc'
if type(s) is str: print('строка') # проверка класса объекта
b=[1,2,3]
print(type(b)) # <class 'list'>
c=(1,2,3)
print(type(c)) # <class 'tuple'>
Р’РІРѕРґ:

Выполнить
Вывод:

Состояние объекта характеризуется перечнем (обычно статическим) всех свойств (атрибутов, характеристик) данного объекта и текущими (обычно динамическими) значениями каждого из этих свойств.

Объекты не существуют изолированно, а подвергаются воздействию или сами воздействуют на другие объекты. Поведение объекта определяется выполняемыми над ним операциями и его состоянием, причем некоторые операции имеют побочное действие: они изменяют состояние. Состояние объекта представляет суммарный результат его поведения.

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

  • Модификатор – операция, которая изменяет состояние объекта;
  • Селектор – операция, считывающая состояние объекта, но не меняющая состояния;
  • Итератор – операция, позволяющая организовать доступ ко всем частям объекта в строго определенной последовательности;
  • Конструктор – операция инициализации собственных атрибутов объекта;
  • Деструктор – операция, освобождающая состояние объекта.

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

Идентичность (уникальность) – это такое свойство объекта, которое отличает его от всех других объектов. Обычно для различения объектов используют имя, но объект может использоваться в программе под несколькими синонимичными именами, что может привести к не прогнозируемому изменению состояния. В Python для проверки совпадения объектов используется операция is: x  , x quad tt"is not" quad y.

Класс определяется следующим образом:
tt"class" quad "имя_класса" tt":"
quad quad "атрибуты_класса"
quad quad "методы_класса"

Перед определением метода или класса можно указать декораторы после @.

"атрибуты_класса" являются общими переменными для всех объектов данного класса, к ним можно обращаться через класс: "имя_класса"."имя_атрибута" или как к собственным атрибутам объекта: "имя_объекта"."имя_атрибута".

"методы_класса" определяются как функции, но первым параметром такой функции указывается self – объект, к которому применяется данный метод. Метод можно вызывать следующим образом:
"имя_объекта"."имя_метода"("аргументы для параметров, кроме self")
или
"имя_класса"."имя_метода"("имя_объекта", "аргументы для остальных параметров")

Параметр self отсутствует у статических методов класса (декоратор @staticmethod), которые вызываются без указания объекта и могут обращаться только к атрибутам класса. Статические методы вызываются следующим образом:
"имя_класса"."имя_метода"("аргументы")

Вместо объекта можно получать в первом параметре его класс, если написать декоратор @classmethod. Такие методы также могут обращаться только к атрибутам класса через tt"self." "имя_атрибута", но их можно переопределять в производных классах. Вызов:
"имя_класса_или_объекта"."имя_метода"("аргументы")

Конструктор определяется как метод с именем __init__:

class Person:
  def __init__(self, name, age):
    self.name=name
    self.age=age
  def hello(self): # метод класса
    print('Hello,', self.name)

p=Person('Alex',25) # вызов конструктора
print(p.name,p.age)
p.hello() # вызов метода
p.age=-1 # можно изменять атрибуты
p.jobtitle='designer' # можно добавить атрибуты
print(p.jobtitle)
Р’РІРѕРґ:

Выполнить
Вывод:

Для предотвращения добавления атрибутов используют атрибут класса __slots__

class Person:
  __slots__='name','age'
  def __init__(self, name, age):
    self.name=name
    self.age=age

p=Person('Alex',25) # вызов конструктора
p.age=-1 # OK, можно изменять атрибут в __slots__
print(p.name,p.age)
p.jobtitle='designer' # Ошибка
Р’РІРѕРґ:

Выполнить
Вывод:

В примере атрибуту age можно присвоить отрицательное число или строку "adult". Для контроля реальные атрибуты делают "скрытыми", добавляя __ перед именем и определяя специальные методы для чтения и записи атрибута:

class Person:
  __slots__='__name','__age'
  def __init__(self, name, age):
    self.name=name
    self.age=age
  @property # декоратор метода
  def name(self): # селектор для свойства name
    return self.__name
  @name.setter 
  def name(self, name): # модификатор для свойства name
    if type(name) is str and len(name)>0:
      self.__name = name
    else:
      raise Exception("Недопустимое значение для имени")
  @property
  def age(self): # селектор для свойства age
    return self.__age
  @age.setter 
  def age(self, age): # модификатор для свойства age
    if type(age) is int and 0 <= age < 150:
      self.__age = age
    else:
      raise Exception("Недопустимое значение для возраста")

p=Person('Alex',25) # вызов конструктора
p.age=30 # OK
print(p.name,p.age)
p.age=-1 # Ошибка
Р’РІРѕРґ:

Выполнить
Вывод:

К "скрытым" атрибутам можно обращаться напрямую, это такое же соглашение, как и "константы".

Для наследования в производном классе структуры и поведения других классов имена базовых классов указываются в скобках после имени класса. Класс object является базовым для остальных классов по умолчанию. В производном классе можно переопределить любые методы и добавить новые атрибуты.

from abc import ABC, abstractmethod
import math
class Shape(ABC): # абстрактный класс
  @abstractmethod
  def area(self): pass # площадь, абстрактный метод
class Rectangle(Shape): # Прямоугольник
  def __init__(self,w,h):
    self.w=w
    self.h=h
  def area(self):
    return self.w*self.h
class Square(Rectangle): # Квадрат - это прямоугольник с одинаковой высотой и шириной
  def __init__(self,a):
    super().__init__(a,a) # вызов метода базового класса
class Circle(Shape): # Круг
  def __init__(self,r):
    self.r=r
  def area(self):
    return math.pi*self.r**2

a=Rectangle(5,7)
b=Square(9)
c=Circle(4)
print(a.area(),b.area(),c.area())

# функция isinstance используется для проверки, является объект экземпляром некоторого класса
print(isinstance(b,Square), isinstance(b,Rectangle), isinstance(b,Shape), isinstance(b,object))

s=Shape() # Ошибка, нельзя создать объект абстрактного класса
Р’РІРѕРґ:

Выполнить
Вывод:

Специальные методы класса позволяют перегрузить операции и встроенные функции для классов, определяемых программистом.

Название Назначение По умолчанию
__new__(cls, ...) создание объекта
__init__(self, ...) конструктор, инициализация собственных атрибутов
__del__(self) деструктор
__str__(self) str(x) return self.__repr__()
__repr__(self) repr(x)
__bytes__(self) bytes(x)
__format__(self, spec) f"{x:spec}" if spec=='': return self.__str__(); else: raise TypeError()
__eq__(self, other) x==y return True if x is y else NotImplemented
__ne__(self, other) x!=y return not self.__eq__(other)
__lt__(self, other)
__le__(self, other)
__gt__(self, other)
__ge__(self, other)
x<y
x<=y
x>y
x>=y
return NotImplemented
могут быть автоматически определены по __lt__
декоратором класса @total_ordering из functools
__hash__(self) hash(x) если __eq__ не определяется, то хэш адреса объекта, иначе TypeError
__bool__(self) bool(x) или if x: если определен __len__, то return self.__len__()>0
иначе return True
__getattr__(self, name) x.name для несуществующих атрибутов
__getattribute__(self, name) для всех x.name
__setattr__(self, name, value) x.name=value по умолчанию значение сохраняется во внутреннем словаре __dict__
__delattr__(self, name) del x.name
__dir__(self) dir(x)
__call__(self, args...) x(args...)
__len__(self) len(x)
__getitem__(self, key) x[key]
__setitem__(self, key, value) x[key]=value
__delitem__(self, key) del x[key]
__missing__(self, key) x[key] для несуществующих ключей
__iter__(self) итератор для объекта (генератор, возвращающий элементы последовательности или коллекции)
__reversed__(self) reversed(x) по умолчанию реализуется через __len__ и __getitem__
__contains__(self, item) item in x по умолчанию реализуется через __iter__ или __getitem__
__add__(self, other) x+y если не определена, пытается вызвать other.radd(self)
__sub__(self, other) x-y аналогично для остальных операций
__mul__(self, other) x*y
__matmul__(self, other) x@y
__truediv__(self, other) x/y
__floordiv__(self, other) x//y
__mod__(self, other) x%y
__divmod__(self, other) divmod(x,y)
__pow__(self, other, mod=None) x**y и pow(x, y)
__lshift__(self, other) x<<y
__rshift__(self, other) x>>y
__and__(self, other) x&y
__xor__(self, other) x^y
__or__(self, other) x|y
__iadd__(self, other) x+=y return self = self.__add__(other)
__isub__(self, other) x-=y аналогично для остальных операций
__imul__(self, other) x*=y
__imatmul__(self, other) x@=y
__itruediv__(self, other) x/=y
__ifloordiv__(self, other) x//=y
__imod__(self, other) x%=y
__ipow__(self, other) x**=y
__ilshift__(self, other) x<<=y
__irshift__(self, other) x>>=y
__iand__(self, other) x&=y
__ixor__(self, other) x^=y
__ior__(self, other) x|=y
__neg__(self) -x
__pos__(self) +x
__abs__(self) abs(x)
__invert__(self) ~x
__complex__(self) complex(x) return self.__index__()
__int__(self) int(x) return self.__index__()
__float__(self) float(x) return self.__index__()
__round__(self, ndigits=None) round(x)
__trunc__(self) math.trunc(x)
__floor__(self) math.floor(x)
__ceil__(self) math.ceil(x)
__enter__(self) with x: блок
__exit__(self, exc_type, exc_value, traceback) завершение with

Пример класса

import math
class Point:
  def __init__(self, x=0, y=0):
    self.x=x
    self.y=y
  def __abs__(self): return math.hypot(self.x,self.y) # расстояние от начала координат
  @property
  def r(self): return self.__abs__() # расстояние от начала координат
  @property
  def phi(self): return math.atan2(self.y,self.x) # угол
  def __add__(self,p): return Point(self.x+p.x,self.y+p.y)
  def __sub__(self,p): return Point(self.x-p.x,self.y-p.y)
  def __mul__(self,p):
    if type(p) is int or type(p) is float:
      return Point(p*self.x,p*self.y) # "масштабирование"
    else:
      return self.x*p.x+self.y*p.y # скалярное произведение
  def __matmul__(self,p): return self.x*p.y-self.y*p.x # векторное произведение
  def turn(self,a=None): # поворот
    if a==None: return Point(-self.y,self.x) # поворот на п/2
    else:
      ca=math.cos(a)
      sa=math.sin(a)
      return Point(self.x*ca-self.y*sa,-self.x*sa+self.y*ca)
  def __neg__(self): return Point(-self.x,-self.y) # поворот на п
  def __str__(self): return str((self.x,self.y)) 

p1=Point(1,2)
print(abs(p1), p1.r, p1.phi)
print(p1.turn(), p1.turn(math.pi/6), p1*5, -p1)
p2=Point(2,1.5)
print(p1+p2, p1-p2, p1*p2, p1@p2)
Р’РІРѕРґ:

Выполнить
Вывод:
loading