![]() |
![]() |
С точки зрения объектно-ориентированного программирования любые значения (числа, строки, списки, кортежи и т.д.), на которые ссылаются переменные, являются объектами.
Объект представляет собой конкретный предмет или сущность (реальную или абстрактную), имеющую четко определенное функциональное назначение в данной предметной области. Объект обладает состоянием, поведением и идентичностью; структура и поведение схожих объектов определяет общий для них класс; термины "экземпляр класса" и "объект" взаимозаменяемы.
Узнать класс объекта можно с помощью функции 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)