Объектно-ориентированное программирование в Python
Python — это процедурно-ориентированный и одновременно объектно-ориентированный язык программирования.
Процедурно-ориентированный
«Процедурно-ориентированный» подразумевает наличие функций. Программист может создавать функции, которые затем используются в сторонних скриптах.
Объектно-ориентированный
«Объектно-ориентированный» подразумевает наличие классов. Есть возможность создавать классы, представляющие собой прототипы для будущих объектов.
Создание класса в Python
Синтаксис для написания нового класса:
class ClassName:
'Краткое описание класса (необязательно)'
# Код ...
- Для создания класса пишется ключевое слово
class
, его имя и двоеточие (:). Первая строчка в теле класса описывает его. (По желанию) получить доступ к этой строке можно с помощьюClassName.__doc__
- В теле класса допускается объявление атрибутов, методов и конструктора.
Атрибут:
Атрибут — это элемент класса. Например, у прямоугольника таких 2: ширина (width
) и высота (height
).
Метод:
- Метод класса напоминает классическую функцию, но на самом деле — это функция класса. Для использования ее необходимо вызывать через объект.
- Первый параметр метода всегда
self
(ключевое слово, которое ссылается на сам класс).
Конструктор:
- Конструктор — уникальный метод класса, который называется
__init__
. - Первый параметр конструктора во всех случаях
self
(ключевое слово, которое ссылается на сам класс). - Конструктор нужен для создания объекта.
- Конструктор передает значения аргументов свойствам создаваемого объекта.
- В одном классе всегда только один конструктор.
- Если класс определяется не конструктором, Python предположит, что он наследует конструктор родительского класса.
# Прямоугольник.
class Rectangle :
'Это класс Rectangle'
# Способ создания объекта (конструктор)
def __init__(self, width, height):
self.width= width
self.height = height
def getWidth(self):
return self.width
def getHeight(self):
return self.height
# Метод расчета площади.
def getArea(self):
return self.width * self.height
Создание объекта с помощью класса Rectangle:
# Создаем 2 объекта: r1 & r2
r1 = Rectangle(10,5)
r2 = Rectangle(20,11)
print("r1.width = ", r1.width)
print("r1.height = ", r1.height)
print("r1.getWidth() = ", r1.getWidth())
print("r1.getArea() = ", r1.getArea())
print("-----------------")
print("r2.width = ", r2.width)
print("r2.height = ", r2.height)
print("r2.getWidth() = ", r2.getWidth())
print("r2.getArea() = ", r2.getArea())
Что происходит при создании объекта с помощью класса?
При создании объекта класса Rectangle
запускается конструктор выбранного класса, и атрибутам нового объекта передаются значения аргументов. Как на этом изображении:
Конструктор с аргументами по умолчанию
В других языках программирования конструкторов может быть несколько. В Python — только один. Но этот язык разрешает задавать значение по умолчанию.
Все требуемые аргументы нужно указывать до аргументов со значениями по умолчанию.
class Person:
# Параметры возраста и пола имеют значение по умолчанию.
def __init__(self, name, age=1, gender="Male"):
self.name = name
self.age = age
self.gender= gender
def showInfo(self):
print("Name: ", self.name)
print("Age: ", self.age)
print("Gender: ", self.gender)
Например:
from person import Person
# Создать объект Person.
aimee = Person("Aimee", 21, "Female")
aimee.showInfo()
print(" --------------- ")
# возраст по умолчанию, пол.
alice = Person( "Alice" )
alice.showInfo()
print(" --------------- ")
# Пол по умолчанию.
tran = Person("Tran", 37)
tran.showInfo()
Сравнение объектов
В Python объект, созданный с помощью конструктора, занимает реальное место в памяти. Это значит, что у него есть точный адрес.
Если объект AA
— это просто ссылка на объект BB
, то он не будет сущностью, занимающей отдельную ячейку памяти. Вместо этого он лишь ссылается на местоположение BB
.
Оператор ==
нужен, чтобы узнать, ссылаются ли два объекта на одно и то же место в памяти. Он вернет True
, если это так. Оператор !=
вернет True
, если сравнить 2 объекта, которые ссылаются на разные места в памяти.
from rectangle import Rectangle
r1 = Rectangle(20, 10)
r2 = Rectangle(20 , 10)
r3 = r1
# Сравните r1 и r2
test1 = r1 == r2 # --> False
# Сравните r1 и r3
test2 = r1 == r3 # --> True
print ("r1 == r2 ? ", test1)
print ("r1 == r3 ? ", test2)
print (" -------------- ")
print ("r1 != r2 ? ", r1 != r2)
print ("r1 != r3 ? ", r1 != r3)
Атрибуты
В Python есть два похожих понятия, которые на самом деле отличаются:
- Атрибуты
- Переменные класса
Стоит разобрать на практике:
class Player:
# Переменная класса
minAge = 18
maxAge = 50
def __init__(self, name, age):
self.name = name
self.age = age
Атрибут
Объекты, созданные одним и тем же классом, будут занимать разные места в памяти, а их атрибуты с «одинаковыми именами» — ссылаться на разные адреса. Например:
from player import Player
player1 = Player("Tom", 20)
player2 = Player("Jerry", 20)
print("player1.name = ", player1.name)
print("player1.age = ", player1.age)
print("player2.name = ", player2.name)
print("player2.age = ", player2.age)
print(" ------------ ")
print("Assign new value to player1.age = 21 ")
# Присвойте новое значение атрибуту возраста player1.
player1.age = 21
print("player1.name = ", player1.name)
print("player1.age = ", player1.age)
print("player2.name = ", player2.name)
print("player2.age = ", player2.age)
Python умеет создавать новые атрибуты для уже существующих объектов. Например, объект player1
и новый атрибут address
.
from player import Player
player1 = Player("Tom", 20)
player2 = Player("Jerry", 20)
# Создайте новый атрибут с именем «address» для player1.
player1.address = "USA"
print("player1.name = ", player1.name)
print("player1.age = ", player1.age)
print("player1.address = ", player1.address)
print(" ------------------- ")
print("player2.name = ", player2.name)
print("player2.age = ", player2.age)
# player2 е имеет атрибута 'address' (Error!!)
print("player2.address = ", player2.address)
Вывод:
player1.name = Tom
player1.age = 20
player1.address = USA
-------------------
player2.name = Jerry
player2.age = 20
Traceback (most recent call last):
File "C:/Users/gvido/class.py", line 27, in <module>
print("player2.address = ", player2.address)
AttributeError: 'Player' object has no attribute 'address'
Атрибуты функции
Обычно получать доступ к атрибутам объекта можно с помощью оператора «точка» (например, player1.name
). Но Python умеет делать это и с помощью функции.
Функция | Описание |
---|---|
getattr (obj, name[,default]) | Возвращает значение атрибута или значение по умолчанию, если первое не было указано |
hasattr (obj, name) | Проверяет атрибут объекта — был ли он передан аргументом «name» |
setattr (obj, name, value) | Задает значение атрибута. Если атрибута не существует, создает его |
delattr (obj, name) | Удаляет атрибут |
from player import Player
player1 = Player("Tom", 20)
# getattr(obj, name[, default])
print("getattr(player1,'name') = " , getattr(player1,"name"))
print("setattr(player1,'age', 21): ")
# setattr(obj,name,value)
setattr(player1,"age", 21)
print("player1.age = ", player1.age)
# Проверка, что player1 имеет атрибут 'address'?
hasAddress = hasattr(player1, "address")
print("hasattr(player1, 'address') ? ", hasAddress)
# Создать атрибут 'address' для объекта 'player1'
print("Create attribute 'address' for object 'player1'")
setattr(player1, 'address', "USA")
print("player1.address = ", player1.address)
# Удалить атрибут 'address'.
delattr(player1, "address")
Вывод:
getattr(player1,'name') = Tom
setattr(player1,'age', 21):
player1.age = 21
hasattr(player1, 'address') ? False
Create attribute 'address' for object 'player1'
player1.address = USA
Встроенные атрибуты класса
Объекты класса — дочерние элементы по отношению к атрибутам самого языка Python. Таким образом они заимствуют некоторые атрибуты:
Атрибут | Описание |
---|---|
__dict__ | Предоставляет данные о классе коротко и доступно, в виде словаря |
__doc__ | Возвращает строку с описанием класса, или None , если значение не определено |
__class__ | Возвращает объект, содержащий информацию о классе с массой полезных атрибутов, включая атрибут __name__ |
__module__ | Возвращает имя «модуля» класса или __main__ , если класс определен в выполняемом модуле. |
class Customer:
'Это класс Customer'
def __init__(self, name, phone, address):
self.name = name
self.phone = phone
self.address = address
john = Customer("John",1234567, "USA")
print ("john.__dict__ = ", john.__dict__)
print ("john.__doc__ = ", john.__doc__)
print ("john.__class__ = ", john.__class__)
print ("john.__class__.__name__ = ", john.__class__.__name__)
print ("john.__module__ = ", john.__module__)
Вывод:
john.__dict__ = {'name': 'John', 'phone': 1234567, 'address': 'USA'}
john.__doc__ = Это класс Customer
john.__class__ = <class '__main__.Customer'>
john.__class__.__name__ = Customer
john.__module__ = __main__
Переменные класса
Переменные класса в Python — это то же самое, что Field в других языках, таких как Java или С#. Получить к ним доступ можно только с помощью имени класса или объекта.
Для получения доступа к переменной класса лучше все-таки использовать имя класса, а не объект. Это поможет не путать «переменную класса» и атрибуты.
У каждой переменной класса есть свой адрес в памяти. И он доступен всем объектам класса.
from player import Player
player1 = Player("Tom", 20)
player2 = Player("Jerry", 20)
# Доступ через имя класса.
print ("Player.minAge = ", Player.minAge)
# Доступ через объект.
print("player1.minAge = ", player1.minAge)
print("player2.minAge = ", player2.minAge)
print(" ------------ ")
print("Assign new value to minAge via class name, and print..")
# Новое значение minAge через имя класса
Player.minAge = 19
print("Player.minAge = ", Player.minAge)
print("player1.minAge = ", player1.minAge)
print("player2.minAge = ", player2.minAge)
Вывод:
Player.minAge = 18
player1.minAge = 18
player2.minAge = 18
------------
Assign new value to minAge via class name, and print..
Player.minAge = 19
player1.minAge = 19
player2.minAge = 19
Составляющие класса или объекта
В Python присутствует функция dir
, которая выводит список всех методов, атрибутов и переменных класса или объекта.
from player import Player
# Вывести список атрибутов, методов и переменных объекта 'Player'
print(dir(Player))
print("\n\n")
player1 = Player("Tom", 20)
player1.address ="USA"
# Вывести список атрибутов, методов и переменных объекта 'player1'
print(dir(player1))
Вывод:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'maxAge', 'minAge']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'address', 'age', 'maxAge',
'minAge', 'name']