Pythonicway — ООП в Python
Объектно-ориентированным программированием называется такой подход к программированию, в котором используются понятия класса и объекта. Говоря проще, когда перед нами стоит задача запрограммировать какой-либо объект, то намного легче сделать это описав этот объект, чем написав ряд функций.
Терминология объектно-ориентированного программирования:
- Класс (Class): Определенный программистом прототип программируемого объекта с набором атрибутов (переменных и методов), которые описывают данный объект. Доступ к аттрибутам и методам осуществляется через точку
- Переменная класса (Class variable): Переменная, доступная для всех экземпляров данного класса. Определяется внутри класса, но вне любых методов класса.
- Экземпляр класса (Instance): Отдельный объект-представитель определенного класса.
- Переменная экземпляра класса (Instance variable): Переменная определенная внутри медота класса, принадлежащая только к этому классу.
- Метод (Method): Особая функция, определенная внутри класса.
- Наследование (Inheritance): Передача аттрибутов и методов родительского класса дочерним классам.
- Перегрузка функций (Function overloading): Изменение работы метода, унаследованного дочерним классом от родительского класса.
- Перегрузка операторов (Operator overloading): Определение работы операторов с экземплярами данного класса.
Создание класса в Python:
Определение класса начинается с ключевого слова class, после него следует имя класса и двоеточие. Теперь с отступом можно описывать сам класс.
class Elevator: """ Simple elevator class """ # Переменная класса. Сколько людей было перевезено ВСЕМИ лифтами people_lifted = 0 # Конструктор класса. Вызывается при создании экземпляра класса def __init__(self,name): self.name = name # переменная экземпляра класса. Количество людей перевезенных КОНКРЕТНЫМ лифтом self.people_lifted = 0 # Метод перевозки людей def lift(self): print ("{} lifted someone".format(self.name)) # Увеличиваем количество людей перевезенных ЭТИМ лифтом self.people_lifted += 1 # Увеличиваем количество людей перевезенных ВСЕМИ лифтами Elevator.people_lifted += 1 # Метод печатающий информацию о конкретном лифте def info(self): print (self.name, "lifted", self.people_lifted, "people out of", Elevator.people_lifted)
Создание экземпляров класса:
Чтобы создать экземпляр класса следует любой переменной присвоить значение имени класса, указав в скобках аргументы, которые принимает метод __init__().
elevator_1 = Elevator("OTIS") elevator_2 = Elevator("PHILLIPS")
Получение доступа к атрибутам и методам класса:
Чтобы получить доступ к атрибутам класса в Python следует после объекта поставить точку и написать имя переменной или метода, которые вы хотите использовать:
# Везем человека в лифте под именем OTIS elevator_1.lift() # Везем двоих человек в лифте под именем PHILLIPS elevator_2.lift() elevator_2.lift() # Получаем информацию по лифту под именем OTIS elevator_1.info() # Получаем информацию по лифту под именем PHILLIPS elevator_2.info()
Соединив все это в одном файле, получим следующее:
Углубленные темы объектно-ориентированного программирования, которые мы еще рассмотрим:
Наследование в Python.
Множественное наследование в Python.
Перегрузка операторов в Python.
Сокрытие данных класса в Python.
Принципы ООП.
Элегантное ООП на Python. Безболезненное преобразование словарей… | by Iuliia Averianova | NOP::Nuances of Programming
Сила Python в его гибкости. Это один из самых простых языков для объектно-ориентированного программирования. Иногда его даже критикуют за чрезмерную гибкость. Я покажу самый элегантный на мой взгляд способ объектно-ориентированного программирования в Python. Ключ к элегантности — библиотека marshmallow
. Она устанавливается командой pip install marshmallow
.
Для демонстрации давайте начнём с объявления простого класса User
:
class User(object):
def __init__(self, name, age):
self.name = name
self.age = agedef __repr__(self):
return f'I am {self.name} and my age is {self.age}'
OK. У User
только два атрибута: name
и age
. Обратите внимание, что я также реализовал метод __repr__
, чтобы мы легко могли вывести экземпляр для проверки. Затем импортируем некоторые модули и методы из библиотеки marshmallow
:
from marshmallow import Schema, fields, post_load
from pprint import pprint
Я импортировал pprint
, потому что собираюсь отобразить множество словарей и списков. Так они будут выглядеть лучше. Так как же использовать marshmallow
? Предельно просто: определите Schema
для класса User
:
class UserSchema(Schema):
name = fields.String()
age = fields.Integer()@post_load
def make(self, data, **kwargs):
return User(**data)
Для каждого атрибута необходимо объявить поля — fields
, а затем тип. Аннотация @post_load
опциональная. Она нужна для загрузки схемы в качестве экземпляра какого-либо класса. Следовательно, в нашем случае она нужна для генерации экземпляров User
. Метод make
реализует экземпляр с помощью атрибутов.
Если у нас есть словарь (объект JSON) и нам нужен экземпляр, пишем такой код:
data = {
'name': 'Chris',
'age': 32
}schema = UserSchema()
user = schema.load(data)
Всё просто! Вызываем метод схемы load()
и десериализуем объект JSON в экземпляр класса.
Что, если мы работаем с массивом JSON, содержащим множество объектов для десериализации? Не нужно писать for
, просто укажите many=True
:
data = [{
'name': 'Alice',
'age': 20
}, {
'name': 'Bob',
'age': 25
}, {
'name': 'Chris',
'age': 32
}]schema = UserSchema()
users = schema.load(data, many=True)
OK. Теперь мы знаем, что можем вызвать метод load()
для преобразования словарей в экземпляры. Как насчёт обратного действия? Используем метод dump()
:
dict = schema.dump(users, many=True)
Здесь users
— список экземпляров из предыдущего примера. Мы видим, как список пользователей преобразуется в массив JSON одной строкой кода!
Думаете, marshmallow
умеет только сериализовывать и десериализовывать экземпляры? Если бы это было так, я бы вряд ли взялся за эту статью. Самая мощная функция этой библиотеки — валидация. Начнём с простого примера. Сначала импортируем ValidationError
, исключение из marshmallow
:
from marshmallow import ValidationError
Помните, мы объявили UserSchema
выше с полем age
как Integer
? Что, если мы передадим недопустимое значение?
data = [{
'name': 'Alice',
'age': 20
}, {
'name': 'Bob',
'age': 25
}, {
'name': 'Chris',
'age': 'thirty two'
}]
Пожалуйста, обратите внимание, что третий объект — Chris
— в массиве JSON имеет недопустимый формат, из-за которого его нельзя преобразовать в целое число. Теперь вызовем метод load()
, чтобы десериализовать массив.
try:
schema = UserSchema()
users = schema.load(data, many=True)
except ValidationError as e:
print(f'Error Msg: {e.messages}')
print(f'Valid Data: {e.valid_data}')
Исключение поймано, оно сообщает о недопустимом целом числе. Представьте, что разрабатываете веб-приложение. Теперь вам не нужно писать сообщения об ошибках!
Кроме того, в этом примере только у третьего объекта возникла проблема валидации. Сообщение говорит нам о том, что ошибка произошла в индексе 2
, и допустимые объекты всё ещё могут быть выведены.
Конечно, проверки только по типам данных недостаточно. Библиотека поддерживает куда больше методов валидации. Добавим в класс User
атрибут gender
.
class User(object):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = genderdef __repr__(self):
return f'I am {self.name}, my age is {self.age} and my gender is {self.gender}'
Затем определим схему с валидациями. И, конечно, импортируем из библиотеки функцию validate
.
from marshmallow import validateclass UserSchema(Schema):
name = fields.String(validate=validate.Length(min=1))
age = fields.Integer(validate=validate.Range(min=18, max=None))
gender = fields.String(validate=validate.OneOf(['F', 'M', 'Other']))
Мы добавили валидации ко всем трём полям.
- У
name
длина должна быть хотя бы в 1 символ. Иными словами, поле не может быть пустым. - У
age
значение должно быть больше или равно 18. - Поле
gender
должно принимать одно из трёх значений.
Давайте определим объект JSON с недопустимыми значениями всех полей:
data = {
'name': '',
'age': 16,
'gender': 'X'
}
И попробуем загрузить его:
try:
UserSchema().load(data)
except ValidationError as e:
pprint(e.messages)
Неудивительно, что обнаружены исключения, но, когда я увидел этот вывод впервые, меня поразило, что сообщения об ошибках встроены по умолчанию. Я сэкономил кучу времени!
Вы можете спросить, ограничены ли мы встроенными методами валидации, такими как диапазон, длина или выбор одного из значений, как в примере выше. Что, если мы захотим написать собственный метод валидации? Конечно, это возможно:
def validate_age(age):
if age < 18:
raise ValidationError('You must be an adult to buy our products!')class UserSchema(Schema):
name = fields.String(validate=validate.Length(min=1))
age = fields.Integer(validate=validate_age)
gender = fields.String(validate=validate.OneOf(['F', 'M', 'Other']))
Здесь мы определили метод validate_age
с пользовательскими логикой и сообщением. Теперь определим объект JSON, чтобы протестировать этот метод. В следующем примере age
меньше 18.
data = {
'name': 'Chris',
'age': 17,
'gender': 'M'
}try:
user = UserSchema().load(data)
except ValidationError as e:
pprint(e.messages)
Теперь validate_age
применяет пользовательские логику и сообщение об ошибке. Ниже элегантная реализация:
class UserSchema(Schema):
name = fields.String()
age = fields.Integer()
gender = fields.String()@validates('age')
def validate_age(self, age):
if age < 18:
raise ValidationError('You must be an adult to buy our products!')
Через аннотацию мы определяем валидацию внутри класса.
Конечно, можно определить поля, требующие заполнения:
class UserSchema(Schema):
name = fields.String(required=True, error_messages={'required': 'Please enter your name.'})
age = fields.Integer(required=True, error_messages={'required': 'Age is required.'})
email = fields.Email()
В этом примере обязательны поля name
и age
. Теперь давайте проверим валидацию объектом без электронной почты:
data_no_email = {
'name': 'Chris',
'age': 32
}try:
user = UserSchema().load(data_no_email)
except ValidationError as e:
pprint(e.messages)
OK. Проблем не возникло. Что, если в объекте не указаны ни имя, ни возраст?
data_no_name_age = {
'email': '[email protected]'
}try:
user = UserSchema().load(data_no_name_age)
except ValidationError as e:
print(f'Error Msg: {e.messages}')
print(f'Valid Data: {e.valid_data}')
Появилось предупреждение и сообщения об ошибках, заданные для обязательных полей.
Иногда нужно задать значения по умолчанию для некоторых полей. Пользователям не обязательно вводить данные, вместо них будут использованы значения по умолчанию.
class UserSchema(Schema):
name = fields.String(missing='Unknown', default='Unknown')print(UserSchema().load({})) # Принять значение "missing"
print(UserSchema().dump({})) # Принять значение "default"
В marshmallow
существует два способа определить значения по умолчанию:
- Ключевое слово
missing
определяет значение по умолчанию, которое используется при десериализации экземпляра с помощьюload()
. - Ключевое слово
default
определяет значение по умолчанию, которое используется при сериализации экземпляра с помощьюdump()
.
В примере выше мы применили оба ключевых слова и поэкспериментировали над пустым объектом и с методом load()
, и с методом dump()
. В обоих случаях было добавлено поле name
со значением по умолчанию.
Это ещё не конец, продолжаем 🙂
Иногда возникает расхождение в реализации классов и фактических данных в JSON, то есть расхождение имён ключей или атрибутов. Например, в User
мы определили атрибут name
, однако в объекте JSON используется другое имя для этого поля — username
. В нашем случае не нужно ни повторно реализовывать классы, ни преобразовывать ключи в объекте JSON. Можно написать такой код:
class User(object):
def __init__(self, name, age):
self.name = name
self.age = agedef __repr__(self):
return f'I am {self.name} and my age is {self.age}'class UserSchema(Schema):
username = fields.String(attribute='name')
age = fields.Integer()@post_load
def make(self, data, **kwargs):
return User(**data)
Обратите внимание, что в User
есть поле name
, тогда как в UserSchema
есть поле username
. Но для username
определено, что его attribute
должен называться name
. Выведем экземпляр класса User
:
user = User('Chris', 32)
UserSchema().dump(user)
dump
корректно сериализовал экземпляр с именем поля username
. И наоборот:
data = {
'username': 'Chris',
'age': 32
}
UserSchema().load(data)
Даже если мы передадим JSON объект с ключом username
, он без проблем десериализуется в экземпляр User
.
И последнее, но не менее важное, чем всё остальное: marshmallow
поддерживает вложенные атрибуты.
class Address(object):
def __init__(self, street, suburb, postcode):
self.street = street
self.suburb = suburb
self.postcode = postcodedef __repr__(self):
return f'{self.street}, {self.suburb} {self.postcode}'class User(object):
def __init__(self, name, address):
self.name = name
self.address = addressdef __repr__(self):
return f'My name is {self.name} and I live at {self.address}'
Мы определили два класса: Address
и User
. У User
есть атрибут address
, имеющий тип Address
. Давайте проверим классы, реализовав объект:
address = Address('1, This St', 'That Suburb', '1234')
user = User('Chris', address)
print(user)
И определим схему:
class AddressSchema(Schema):
street = fields.String()
suburb = fields.String()
postcode = fields.String()@post_load
def make(self, data, **kwargs):
return Address(**data)class UserSchema(Schema):
name = fields.String()
address = fields.Nested(AddressSchema())@post_load
def make(self, data, **kwargs):
return User(**data)
Хитрость здесь в том, чтобы использовать fields.Nested()
для определения поля по другой схеме. У нас уже есть экземпляр User
. Давайте сделаем его дамп в JSON:
pprint(UserSchema().dump(user))
Как видите, экземпляр сериализован во вложенный объект JSON Конечно, обратный вариант тоже работает:
data = {
'name': 'Chris',
'address': {
'postcode': '1234',
'street': '1, This St',
'suburb': 'That Suburb'
}
}
pprint(UserSchema().load(data))
На сегодня всё. Спасибо, что прочитали! Код из статьи вы найдёте на Google Colab.
Читайте также:
Читайте нас в Telegram, VK и Яндекс.Дзен
Объектно-ориентированное программирование Python (ООП в Python)
В этой статье мы расскажем об объектно-ориентированном программировании (ООП) в Python и его фундаментальных концепциях.Python — это мультипарадигмальный язык. Это означает, что он поддерживает различные подходы к программированию.
Одной из наиболее популярных парадигм является создание объектов. Она известна как объектно-ориентированное программирование (ООП).
Объект имеет две характеристики:
- атрибуты;
- поведение;
Рассмотрим на примере:
Объект – это попугай:
- имя, возраст, цвет являются атрибутами;
- пение, танцы — это поведение;
Концепция ООП в Python направлена на создание кода для многократного использования. Эта концепция также известна как DRY (Don’t Repeat Yourself).
В Python концепция ООП реализует несколько принципов:
Наследование | Использование элементов из нового класса без изменения существующего класса. |
Инкапсуляция | Скрытие приватных элементов класса от других объектов. |
Полиморфизм | Концепция использования объекта с одинаковым интерфейсом без получения информации о его типе и внутренней структуре. |
Мы можем представить класс как эскиз попугая с метками. Он содержит все данные об имени, цвете, размере и т. д. На основе этого описания можно изучить попугая. В данном случае попугай является объектом.
Примером для класса попугая может быть:
Мы используем ключевое слово class для определения пустого класса Parrot . Из класса мы создаем экземпляр – объект определенного класса.
Объект — это экземпляр класса. В определении класса задается только описание объекта. Пример создания объекта класса Parrot:
В данном случае obj является объектом класса Parrot.
Предположим, что у нас есть информация о попугае. Теперь нужно показать, как построить класс и объекты Parrot.
class Parrot: # атрибут класса species = "bird" # атрибут экземпляра def __init__(self, name, age): self.name = name self.age = age # создаем экземпляр класс Parrot blu = Parrot("Blu", 10) woo = Parrot("Woo", 15) # получаем доступ к атрибутам класса print("Blu is a {}".format(blu.__class__.species)) print("Woo is also a {}".format(woo.__class__.species)) # получаем доступ к атрибутам экземпляра print("{} is {} years old".format( blu.name, blu.age)) print("{} is {} years old".format( woo.name, woo.age))
Результат работы программы:
Blu is a bird Woo is also a bird Blu is 10 years old Woo is 15 years old
Сначала мы создаем класс с именем Parrot. Затем мы определяем атрибуты. Они являются характеристикой объекта.
Затем мы создаем экземпляры класса Parrot. В данном случае blu и woo являются ссылками на новые объекты.
После этого мы получаем доступ к атрибуту класса с помощью __class __.species. Атрибуты класса одинаковы для всех его экземпляров. Точно так же мы получаем доступ к атрибутам экземпляра, используя blu.name и blu.age. Но атрибуты экземпляра уникальны для каждого экземпляра класса.
Методы — это функции, определенные внутри класса. Они используются для определения поведения объекта.
class Parrot: # атрибуты экземпляра def __init__(self, name, age): self.name = name self.age = age # метод экземпляра def sing(self, song): return "{} sings {}".format(self.name, song) def dance(self): return "{} is now dancing".format(self.name) # создаем экземпляр объекта blu = Parrot("Blu", 10) # вызываем методы экземпляра print(blu.sing("'Happy'")) print(blu.dance())
Результат работы программы:
Blu sings 'Happy' Blu is now dancing
В приведенном выше примере мы определяем два метода sing() и dance(). Их называют методами экземпляра, так как они вызываются для экземпляра объекта, то есть для blu.
Наследование — это способ создания нового класса на основе старого. Новый класс является производным классом (дочерним). Существующий класс является базовым классом (родительским).
# родительский класс class Bird: def __init__(self): print("Bird is ready") def whoisThis(self): print("Bird") def swim(self): print("Swim faster") # дочерний класс class Penguin(Bird): def __init__(self): # вызов функции super() super().__init__() print("Penguin is ready") def whoisThis(self): print("Penguin") def run(self): print("Run faster") peggy = Penguin() peggy.whoisThis() peggy.swim() peggy.run()
Результат работы программы:
Bird is ready Penguin is ready Penguin Swim faster Run faster
Сначала мы создали два класса: Bird (родительский класс) и Penguin (дочерний класс). Он наследует функции родительского класса. Это прослеживается в методе swim().
Дочерний класс изменил поведение родительского класса – метод whoisThis(). Также мы расширяем родительский класс, создав новый метод run().
Мы используем функцию super() перед методом __init__(), чтобы извлечь содержимое метода __init__() из родительского класса в дочерний.
Используя ООП в Python, мы можем ограничить доступ к методам и переменным. Это предотвращает изменение данных вне класса. Такой подход называется инкапсуляцией. В Python мы устанавливаем приватный модификатор доступа, используя в качестве префикса подчеркивание одинарное «_» или двойное «_ _» подчеркивание.
class Computer: def __init__(self): self.__maxprice = 900 def sell(self): print("Selling Price: {}".format(self.__maxprice)) def setMaxPrice(self, price): self.__maxprice = price c = Computer() c.sell() # изменяем цену c.__maxprice = 1000 c.sell() # используем функцию сеттера c.setMaxPrice(1000) c.sell()
Когда мы запустим эту программу, результат будет следующим:
Selling Price: 900 Selling Price: 900 Selling Price: 1000
Сначала мы определили класс Computer . Затем использовали метод __init__() для хранения значения максимальной стоимости продажи компьютера.
Мы попытались изменить цену, но не смогли, потому что Python рассматривает __maxprice, как приватные атрибуты. Чтобы изменить значение, мы использовали функцию сеттера. То есть, setMaxPrice(), которая принимает цену в качестве параметра.
Полиморфизм — это способность использовать в ООП общий интерфейс для нескольких форм (типов данных).
Предположим, что нужно раскрасить фигуру. Есть несколько вариантов фигуры (прямоугольник, квадрат, круг). Мы могли бы использовать тот же метод, чтобы закрасить любую форму. Эта концепция называется полиморфизмом.
class Parrot: def fly(self): print("Parrot can fly") def swim(self): print("Parrot can't swim") class Penguin: def fly(self): print("Penguin can't fly") def swim(self): print("Penguin can swim") # общий интерфейс def flying_test(bird): bird.fly() #создаем объекты blu = Parrot() peggy = Penguin() # передаем объекты flying_test(blu) flying_test(peggy)
Результат:
Parrot can fly Penguin can't fly
Мы определили два класса: Parrot и Penguin . У каждого из них есть общий метод fly(), но они разные.
Чтобы реализовать полиморфизм, мы создали общий интерфейс. То есть, функцию flying_test(), которая может принимать любой объект. Затем мы передали объекты blu и peggy в функцию flying_test().
- Программирование становится простым и эффективным.
- Класс общий, поэтому код можно использовать повторно.
- Производительность программистов увеличивается.
- Благодаря абстракции данные становятся безопасными и надежными.
Данная публикация является переводом статьи «Python Object Oriented Programming» , подготовленная редакцией проекта.
Объектно-ориентированное программирование на Python — это… Что такое Объектно-ориентированное программирование на Python?
Объектно-ориентированное программирование на Python — программирование на Python с использованием парадигмы ООП: с самого начала Python проектировался как объектно-ориентированный язык программирования[1].
Введение
Принципы ООП
Согласно Алану Кэю — автору языка программирования Smalltalk — объектно-ориентированным может называться язык, построенный с учетом следующих принципов[2]:
- Все данные представляются объектами
- Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
- Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
- Каждый объект имеет тип
- Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Объекты, типы и классы
Определение класса
Для ясности последующего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения класса используется оператор class
:
class имя_класса(надкласс1, надкласс2, ...): # определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
В терминологии Python члены класса называются атрибутами, функции класса — методами, а поля класса — свойствами (или просто атрибутами).
Определения методов аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению self
:
class A: def m1(self, x): # блок кода метода
Определения атрибутов — обычные операторы присваивания, которые связывают некоторые значения с именами атрибутов.
В языке Python класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A: pass def myMethod(self, x): return x * x A.m1 = myMethod A.attr1 = 2 * 2
Создание экземпляра
Для создания объекта — экземпляра класса (то есть, инстанциирования класса), достаточно вызвать класс по имени и задать параметры конструктора:
class Point: def __init__(self, x, y, z): self.coord = (x, y, z) def __repr__(self): return "Point(%s, %s, %s)" % self.coord
>>> p = Point(0.0, 1.0, 0.0) >>> p Point(0.0, 1.0, 0.0)
Переопределив классовый метод __new__
, можно контролировать процесс создания экземпляра. Этот метод вызывается до метода __init__
и должен вернуть новый экземпляр, либо None
(в последнем случае будет вызван __new__
родительского класса). Метод __new__
используется для управления созданием неизменчивых (immutable) объектов, управления созданием объектов в случаях, когда __init__
не вызывается, например, при десериализации (unpickle). Следующий код демонстрирует один из вариантов реализации шаблона Одиночка:
>>> class Singleton(object): obj = None # Атрибут для хранения единственного экземпляра def __new__(cls,*dt,**mp): # класса Singleton. if cls.obj is None: # Если он еще не создан, то cls.obj = object.__new__(cls,*dt,**mp) # вызовем __new__ родительского класса return cls.obj # вернем синглтон ... >>> obj = Singleton() >>> obj.attr = 12 >>> new_obj = Singleton() >>> new_obj.attr 12 >>> new_obj is obj # new_obj и obj - это один и тот же объект True
Конструктор и деструктор
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (деструктор). В языке Python реализовано автоматическое управление памятью, поэтому деструктор требуется достаточно редко, для ресурсов, требующих явного освобождения.
Следующий класс имеет конструктор и деструктор:
class Line: def __init__(self, p1, p2): self.line = (p1, p2) def __del__(self): print "Удаляется линия %s - %s" % self.line
>>> l = Line((0.0, 1.0), (0.0, 2.0)) >>> del l Удаляется линия (0.0, 1.0) - (0.0, 2.0) >>>
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно «истощенной», поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
Время жизни объекта
Без применения каких-либо особых средств время жизни объекта, определённого в программе на Python, не выходит за рамки времени выполнения процесса этой программы.
Для преодоления этого ограничения существуют различные возможности: от хранения объектов в простейшей базе данных (shelve), применения ORM (например, SQLAlchemy) до использования специализированных баз данных с развитыми возможностями (например, ZODB). Все эти средства позволяют делать объекты устойчивыми (англ. persistent). Как правило, при записи объекта производится его сериализация, а при чтении — десериализация.
>>> import shelve >>> s = shelve.open("somefile.db") >>> s['myobject'] = [1, 2, 3, 4, 'свечка'] >>> s.close() >>> import shelve >>> s = shelve.open("somefile.db") >>> print s['myobject'] [1, 2, 3, 4, '\xd1\x81\xd0\xb2\xd0\xb5\xd1\x87\xd0\xba\xd0\xb0']
Инкапсуляция и доступ к свойствам
Инкапсуляция является одним из ключевых понятий ООП. Все значения в Python являются объектами, инкапсулирующими код (методы) и данные и предоставляющими пользователям общедоступный интерфейс. Методы и данные объекта доступны через его атрибуты.
Сокрытие информации о внутреннем устройстве объекта выполняется в Python на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие — к его внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких языках как C++ или Java: атрибут остается доступным, но под именем вида _ИмяКласса__ИмяАтрибута
, а при каждом обращении Python
будет модифицировать имя в зависимости от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний классы могут иметь атрибут с именем, например, «__f», но не будут мешать друг другу.
>>> class parent(object): def __init__(self): self.__f = 2 def get(self): return self.__f .... >>> class child(parent): def __init__(self): self.__f = 1 parent.__init__(self) def cget(self): return self.__f .... >>> c = child() >>> c.get() 2 >>> c.cget() 1 >>> c.__dict__ {'_child__f': 1, '_parent__f': 2} # на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени атрибута. Они используются для специальных свойств и функций класса (например, для перегрузки операции). Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object): def __init__(self, x): # атрибут получает значение в конструкторе self.x = x a = A(5) print a.x a.x = 5
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута:
class A(object): def __init__(self, x): self._x = x def getx(self): # метод для получения значения return self._x def setx(self, value): # присваивания нового значения self._x = value def delx(self): # удаления атрибута del self._x x = property(getx, setx, delx, "Свойство x") # определяем x как свойство a = A(5) print a.x # Синтаксис доступа к атрибуту при этом прежний a.x = 5
Разумеется, первый способ хорош только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Существуют два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке методов __getattr__()
, __setattr__()
, __delattr__()
, а второй — метода __getattribute__()
. Второй метод помогает управлять чтением уже существующих атрибутов. Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы RPC для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
Полиморфизм
В компилируемых языках программирования полиморфизм достигается за счёт создания виртуальных методов, которые в отличие от невиртуальных можно перегрузить в потомке. В Python все методы являются виртуальными, что является естественным следствием разрешения доступа на этапе исполнения. (Следует отметить, что создание невиртуальных методов в компилируемых языках связано с меньшими накладными расходами на их поддержку и вызов).
>>> class Parent(object): def isParOrPChild(self) : return True def who(self) : return 'parent' >>> class Child(Parent): def who(self): return 'child' >>> x = Parent() >>> x.who(), x.isParOrPChild() ('parent', True) >>> x = Child() >>> x.who(), x.isParOrPChild() ('child', True)
Явно указав имя класса, можно обратиться к методу родителя (как впрочем и любого другого объекта).
>>> class Child(Parent): def __init__(self): Parent.__init__(self)
В общем случае для получения класса-предка применяется функция super
.
class Child(Parent): def __init__(self): super(Child, self).__init__(self)
Используя специально предусмотренное исключение NotImplementedError
, можно имитировать чисто виртуальные методы:
>>> class abstobj(object): def abstmeth(self): raise NotImplementedError('Method abstobj.abstmeth is pure virtual') >>> abstobj().abstmeth() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in method NotImplementedError: Method abstobj.abstmeth is pure virtual
Или, с использованием декоратора, так:
>>> def abstract(func): def closure(*dt, **mp): raise NotImplementedError("Method %s is pure virtual" % func.__name__) return closure >>> class abstobj(object): @abstract def abstmeth(self): pass
Изменяя атрибут __class__
, можно перемещать объект вверх или вниз по иерархии наследования (впрочем, как и к любому другому типу)
>>> c = child() >>> c.val = 10 >>> c.who() 'child' >>> c.__class__ = Parent >>> c.who() 'parent' >>> c.val 10
Однако, в этом случае никакие преобразования типов не делаются, поэтому забота о согласованности данных всецело лежит на программисте. Кроме того, присваивание атрибуту __class__
не должно применяться по поводу и без. Прежде чем решиться на его использование, необходимо рассмотреть менее радикальные варианты реализации изменения объекта, то есть по сути шаблона проектирования State.
Более того, полиморфизм в Python вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism)[3]. Например, чтобы экземпляру класса «прикинуться» файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно .read()
, .readlines()
, .close()
и т. п.).
Имитация встроенных типов
Встроенные типы и их методы имеют синтаксическую поддержку в языке Python или другие особые «привилегии». Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определённый пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример — имитировать функцию:
>>> class Add: ... def __call__(self, x, y): # определение метода, ... return x + y # который отвечает за операцию вызова функции ... >>> add = Add() >>> add(3, 4) # это эквивалентно add.__call__(3, 4) 7
Аналогично поддаются имитации все операции встроенных типов. Ещё один пример связан с вычислением длины объекта с помощью функции len()
. Оказывается, эта встроенная функция вызывает специальный метод:
>>> class wrongList(list): # определяем собственный класс для списка ... def __len__(self): # который всегда считает, что имеет нулевую длину ... return 0 ... >>> w = wrongList([1,2,3]) >>> len(w) # это эквивалентно w.__len__() 0
Методы __getitem__,__setitem__,__delitem__,__contains__
позволяют создать интерфейс для словаря или списка(dict
). Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию *
:
class Multiplyable: def __init__(self, value): self.value = value def __mul__(self, y): return self.value * y def __rmul__(self, x): return x * self.value def __imul__(self, y): return Multiplyable(self.value * y) def __str__(self): return "Multiplyable(%s)" % self.value >>> m = Multiplyable(1) >>> print m Multiplyable(1) >>> m *= 3 >>> print m Multiplyable(3)
Последний из методов — .__str__()
— отвечает за представление экземпляра класса при печати оператором print
и в других подобных случаях.
Аналогичные методы имеются и у соответствующих встроенных типов
>>> int.__add__ <slot wrapper '__add__' of 'int' objects> >>> [].__getitem__ <built-in method __getitem__ of list object at 0x00DA3D28> >>> class a(object):pass >>> a.__call__ <method-wrapper '__call__' of type object at 0x00DDC318>
Не все из них существуют на самом деле: большая часть имитируется интерпретатором Python для удобства программиста. Такое поведение позволяет экономить время при наиболее важных операциях (например, сложение целых не приводит к поиску и вызову метода __add__
у класса int
) и память, но приводит к невозможности изменения методов у встроенных классов.
Отношения между классами
Наследование и множественное наследование
При описании предметной области классы могут образовывать иерархию, в корне которой стоит базовый класс, а нижележащие классы (подклассы) наследуют свои атрибуты, уточняя и расширяя поведение вышележащего класса (надкласса). Обычно принципом построения классификации является отношение «IS-A» («есть» — между экземпляром и классом) и «AKO» («a kind of» — «разновидность» — между классом и суперклассом)[4].
Python поддерживает как одиночное наследование, так и множественное, позволяющее классу быть производным от любого количества базовых классов.
>>> class Par1(object): # наследуем один базовый класс - object def name1(self): return 'Par1' >>> class Par2(object): def name2(self): return 'Par2' >>> class Child(Par1, Par2): # создадим класс, наследующий Par1, Par2 (и, опосредованно, object) pass >>> x = Child() >>> x.name1(), x.name2() # экземпляру Child доступны методы из Par1 и Par2 'Par1','Par2'
В Python (из-за «утиной типизации») отсутствие наследования ещё не означает, что объект не может предоставлять тот же самый интерфейс.
Множественное наследование в Python применяется в основном для добавления примесей (mixins) — специальных классов, вносящих некоторую черту поведения или набор свойств[5].
Порядок разрешения доступа к методам и полям
За достаточно простым в использовании механизмом доступа к атрибутам в Python кроется довольно сложный алгоритм. Далее будет приведена последовательность действий, производимых интерпретатором при разрешении запроса object.field
(поиск прекращается после первого успешно завершённого шага, иначе происходит переход к следующему шагу).
- Если у
object
есть метод__getattribute__
, то будет вызван он с параметром'field'
(либо__setattr__
или__delattr__
в зависимости от действия над атрибутом) - Если у
object
есть поле__dict__
, то ищетсяobject.__dict__['field']
- Если у
object.__class__
есть поле__slots__
, то'field'
ищется вobject.__class__.__slots__
- Проверяется
object.__class__.__dict__['fields']
- Производится рекурсивный поиск по
__dict__
всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для «классических» и «новых» классов. - Если у
object
есть метод__getattr__
, то вызывается он с параметром'field'
- Возбуждается исключение
AttributeError
.
Если поиск окончен успешно, то проверяется, является ли атрибут классом «нового стиля». Если является, то проверяется наличие у него метода __get__
(либо __set__
или __delete__
, в зависимости от действия над атрибутом), если метод найден, то происходит следующий вызов object.field.__get__(object)
и возвращается его результат (такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) и используются, например, для создания свойств[6]).
Эта последовательность распространяется только на пользовательские атрибуты. Системные атрибуты, такие как __dict__
, __len__
, __add__
и другие, имеющие специальные поля в С-структуре описания класса находятся сразу.
«Новые» и «классические» классы
В версиях до 2.2 некоторые объектно-ориентированные возможности Python были заметно ограничены. Например, было невозможно наследовать встроенные классы и классы из модулей расширения. Свойства (property) не выделялись явно. Начиная с версии 2.2, объектная система Python была существенно переработана и дополнена. Однако для совместимости со старыми версиями Python было решено сделать две объектные модели: «классические» типы (полностью совместимые со старым кодом) и «новые»[7]. В версии Python3000 поддержка «старых» классов будет удалена.
Для построения «нового» класса достаточно унаследовать его от другого «нового». Если нужно создать «чистый» класс, то можно унаследоваться от object
— родительского типа для всех «новых» классов.
class OldStyleClass: pass # класс "старого" типа class NewStyleClass(object): pass # и "нового"
Все стандартные классы — классы «нового» типа.[8]
Агрегация. Контейнеры. Итераторы
Агрегация, когда один объект входит в состав другого, или отношение «HAS-A» («имеет»), реализуется в Python с помощью ссылок. Python имеет несколько встроенных типов контейнеров: список, словарь, множество. Можно определить собственные классы контейнеров со своей логикой доступа к хранимым объектам. (Следует заметить, что в Python агрегацию можно считать разновидностью ассоциации, так реально объекты не вложены друг в друга в памяти и, более того, время жизни элемента может не зависеть от времени жизни контейнера.)
Следующий класс из модуля utils.py среды web.py является примером контейнера-словаря, дополненного возможностью доступа к значениям при помощи синтаксиса доступа к атрибутам:
class Storage(dict): def __getattr__(self, key): try: return self[key] except KeyError, k: raise AttributeError, k def __setattr__(self, key, value): self[key] = value def __delattr__(self, key): try: del self[key] except KeyError, k: raise AttributeError, k def __repr__(self): return '<Storage ' + dict.__repr__(self) + '>'
Вот как он работает:
>>> v = Storage(a=5) >>> v.a 5 >>> v['a'] 5 >>> v.a = 12 >>> v['a'] 12 >>> del v.a
Для доступа к контейнерам очень удобно использовать итераторы:
>>> cont = dict(a=1, b=2, c=3) >>> for k in cont: ... print k, cont[k] ... a 1 c 3 b 2
Ассоциация и слабые ссылки
Отношение использования («USE-A») экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают циклические ссылки. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Python с помощью механизма подсчета ссылок. Удалением таких объектов занимается сборщик мусора.
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью слабых ссылок. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль weakref
.
Метаклассы
Обычных возможностей объектно-ориентированного программирования хватает далеко не всегда. В некоторых случаях требуется изменить сам характер системы классов: расширить язык новыми типами классов, изменить стиль взаимодействия между классами и окружением, добавить некоторые дополнительные аспекты, затрагивающие все используемые в приложении классы, и т. п.
При объявлении метакласса за основу можно взять класс type
. Пример:
# описание метакласса class myobject(type): # небольшое вмешательство в момент выделения памяти для класса def __new__(cls, name, bases, dict): print "NEW", cls.__name__, name, bases, dict return type.__new__(cls, name, bases, dict) # небольшое вмешательство в момент инициализации класса def __init__(cls, name, bases, dict): print "INIT", cls.__name__, name, bases, dict return super(myobject, cls).__init__(name, bases, dict) # порождение класса на основе метакласса (заменяет оператор class) MyObject = myobject("MyObject", (), {}) # обычное наследование другого класса из только что порожденного class MySubObject(MyObject): def __init__(self, param): print param # получение экземпляра класса myobj = MySubObject("parameter")
Разумеется, вместо оператора print
код метакласса может выполнять более полезные функции: регистрировать класс, передавать действия с классами на удаленную систему, использовать классы для других целей (например, как декларации или ограничения) и т. п.
Методы
Метод
Синтаксис описания метода ничем не отличается от описания функции, разве что его положением внутри класса и характерным первым формальным параметром self
, с помощью которого внутри метода можно ссылаться на сам экземпляр класса (название self является соглашением, которого придерживаются программисты на Python):
class MyClass(object): def mymethod(self, x): return x == self._x
Статический метод
Статические методы в Python являются синтаксическими аналогами статических функций в основных языках программирования. Они не получают ни экземпляр (self
), ни класс (cls
) первым параметром. Для создания статического метода (только «новые» классы могут иметь статические методы) используется декоратор staticmethod
>>> class D(object): @staticmethod def test(x): return x == 0 ... >>> D.test(1) # доступ к статическому методу можно получать и через класс False >>> f = D() >>> f.test(0) # и через экземпляр класса True
Статические методы реализованы с помощью свойств (property).
Метод класса
Классовые методы в Python занимают промежуточное положение между статическими и обычными. В то время как обычные методы получают первым параметром экземпляр класса, а статические не получают ничего, в классовые методы передается класс. Возможность создания классовых методов является одним из следствий того, что в Python классы также являются объектами. Для создания классового (только «новые» классы могут иметь классовые методы) метода можно использовать декоратор classmethod
>>> class A(object): def __init__(self, int_val): self.val = int_val + 1 @classmethod def fromString(cls, val): # вместо self принято использовать cls return cls(int(val)) ... >>> class B(A):pass ... >>> x = A.fromString("1") >>> print x.__class__.__name__ A >>> x = B.fromString("1") >>> print x.__class__.__name__ B
Классовые методы достаточно часто используются для перегрузки конструктора. Классовые методы, как и статические, реализуются через свойства (property).
Мультиметоды
Примером для иллюстрации сути мультиметода может служить функция add()
из модуля operator
:
>>> import operator as op >>> print op.add(2, 2), op.add(2.0, 2), op.add(2, 2.0), op.add(2j, 2) 4 4.0 4.0 (2+2j)
В языке Python достаточно легко реализовать и определённые пользователем мультиметоды[9]. Например, эмулировать мультиметоды можно с помощью модуля multimethods.py (из Gnosis Utils) :
from multimethods import Dispatch class Asteroid(object): pass class Spaceship(object): pass def asteroid_with_spaceship(a1, s1): print "A-><-S" def asteroid_with_asteroid(a1, a2): print "A-><-A" def spaceship_with_spaceship(s1, s2): print "S-><-S" collide = Dispatch() collide.add_rule((Asteroid, Spaceship), asteroid_with_spaceship) collide.add_rule((Asteroid, Asteroid), asteroid_with_asteroid) collide.add_rule((Spaceship, Spaceship), spaceship_with_spaceship) collide.add_rule((Spaceship, Asteroid), lambda x,y: asteroid_with_spaceship(y,x)) a, s1, s2 = Asteroid(), Spaceship(), Spaceship() collision1 = collide(a, s1)[0] collision2 = collide(s1, s2)[0]
Устойчивость объектов
Объекты всегда имеют своё представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или передавать их на другие компьютеры. Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов (сериализацией) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль pickle
является наиболее простым способом «консервирования» объектов в Python.
Следующий пример показывает как работает сериализация и десериализация:
# сериализация >>> import pickle >>> p = set([1, 2, 3, 5, 8]) >>> pickle.dumps(p) 'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' # де-сериализация >>> import pickle >>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') >>> print p set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже — прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных источников с помощью модуля pickle
, так как при этом возможны практически любые действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам или с ненадежными источниками можно воспользоваться другими модулями для сериализации.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта — это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы __getstate__
, __setstate__
и др.).
На стандартном для Python механизме сериализации построена работа модуля shelve
(shelve(англ. глаг.) — ставить на полку; сдавать в архив). Модуль предоставляет функцию open
. Объект, который она возвращает, работает аналогично словарю, но объекты сериализуются и сохраняются в файле:
>>> import shelve >>> s = shelve.open("myshelve.bin") >>> s['abc'] = [1, 2, 3] >>> s.close() # ..... >>> s = shelve.open("myshelve.bin") >>> s['abc'] [1, 2, 3]
Сериализация pickle
— не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML.
Примечания
Литература
- David M. Beazley Python Essential Reference. — 4th Edition. — Addison-Wesley Professional, 2009. — 717 с. — ISBN 978-0672329784
Ссылки
Python | Классы и объекты
Классы и объекты
Последнее обновление: 06.07.2018
Python поддерживает объектно-ориентированную парадигму программирования, а это значит, что мы можем определить компоненты программы в виде классов.
Класс является шаблоном или формальным описанием объекта, а объект представляет экземпляр этого класса, его реальное воплощение. Можно провести следующую аналогию: у всех у нас есть некоторое представление о человеке — наличие двух рук, двух ног, головы, пищеварительной, нервной системы, головного мозга и т.д. Есть некоторый шаблон — этот шаблон можно назвать классом. Реально же существующий человек (фактически экземпляр данного класса) является объектом этого класса.
С точки зрения кода класс объединяет набор функций и переменных, которые выполняют определенную задачу. Функции класса еще называют методами. Они определяют поведение класса. А переменные класса называют атрибутами — они хранят состояние класса
Класс определяется с помощью ключевого слова class:
class название_класса: методы_класса
Для создания объекта класса используется следующий синтаксис:
название_объекта = название_класса([параметры])
Например, определим простейший класс Person, который будет представлять человека:
class Person: name = "Tom" def display_info(self): print("Привет, меня зовут", self.name) person1 = Person() person1.display_info() # Привет, меня зовут Tom person2 = Person() person2.name = "Sam" person2.display_info() # Привет, меня зовут Sam
Класс Person определяет атрибут name, который хранит имя человека, и метод display_info, с помощью которого выводится информация о человеке.
При определении методов любого класса следует учитывать, что все они должны принимать в качестве первого параметра ссылку на текущий объект, который согласно условностям называется
self (в ряде языков программирования есть своего рода аналог — ключевое слово this). Через эту ссылку внутри класса мы можем обратиться к методам или атрибутам этого же класса.
В частности, через выражение self.name
можно получить имя пользователя.
После определения класс Person создаем пару его объектов — person1 и person2. Используя имя объекта, мы можем обратиться к его методам и атрибутам.
В данном случае у каждого из объектов вызываем метод display_info()
, который выводит строку на консоль, и у второго объекта также изменяем атрибут name.
При этом при вызове метода display_info не надо передавать значение для параметра self.
Конструкторы
Для создания объекта класса используется конструктор. Так, выше когда мы создавали объекты класса Person, мы использовали конструктор по умолчанию, который неявно имеют все классы:
person1 = Person() person2 = Person()
Однако мы можем явным образом определить в классах конструктор с помощью специального метода, который называется __init__(). К примеру, изменим класс Person, добавив в него конструктор:
class Person: # конструктор def __init__(self, name): self.name = name # устанавливаем имя def display_info(self): print("Привет, меня зовут", self.name) person1 = Person("Tom") person1.display_info() # Привет, меня зовут Tom person2 = Person("Sam") person2.display_info() # Привет, меня зовут Sam
В качестве первого параметра конструктор также принимает ссылку на текущий объект — self. Нередко в конструкторах устанавливаются атрибуты класса.
Так, в данном случае в качестве второго параметра в конструктор передается имя пользователя, которое устанавливается для атрибута self.name
.
Причем для атрибута необязательно определять в классе переменную name, как это было в предыдущей версии класса Person. Установка значения
self.name = name
уже неявно создает атрибут name.
person1 = Person("Tom") person2 = Person("Sam")
В итоге мы получим следующий консольный вывод:
Привет, меня зовут Tom Привет, меня зовут Sam
Деструктор
После окончания работы с объектом мы можем использовать оператор del для удаления его из памяти:
person1 = Person("Tom") del person1 # удаление из памяти # person1.display_info() # Этот метод работать не будет, так как person1 уже удален из памяти
Стоит отметить, что в принципе это необязательно делать, так как после окончания работы скрипта все объекты автоматически удаляются из памяти.
Кроме того, мы можем определить определить в классе деструктор, реализовав встроенную функцию __del__, который будет вызываться либо в результате вызова оператора del, либо при автоматическом удалении объекта. Например:
class Person: # конструктор def __init__(self, name): self.name = name # устанавливаем имя def __del__(self): print(self.name,"удален из памяти") def display_info(self): print("Привет, меня зовут", self.name) person1 = Person("Tom") person1.display_info() # Привет, меня зовут Tom del person1 # удаление из памяти person2 = Person("Sam") person2.display_info() # Привет, меня зовут Sam
Консольный вывод:
Привет, меня зовут Tom Tom удален из памяти Привет, меня зовут Sam Sam удален из памяти
Определение классов в модулях и подключение
Как правило, классы размещаются в отдельных модулях и затем уже импортируются в основой скрипт программы. Пусть у нас будет в проекте два файла: файл main.py (основной скрипт программы) и classes.py (скрипт с определением классов).
В файле classes.py определим два класса:
class Person: # конструктор def __init__(self, name): self.name = name # устанавливаем имя def display_info(self): print("Привет, меня зовут", self.name) class Auto: def __init__(self, name): self.name = name def move(self, speed): print(self.name, "едет со скоростью", speed, "км/ч")
В дополнение к классу Person здесь также определен класс Auto, который представляет машину и который имеет метод move и атрибут name. Подключим эти классы и используем их в скрипте main.py:
from classes import Person, Auto tom = Person("Tom") tom.display_info() bmw = Auto("BMW") bmw.move(65)
Подключение классов происходит точно также, как и функций из модуля. Мы можем подключить весь модуль выражением:
import classes
Либо подключить отдельные классы, как в примере выше.
В итоге мы получим следующий консольный вывод:
Привет, меня зовут Tom BMW едет со скоростью 65 км/ч
Объектно-ориентированное программирование. Общее представление — Питошка
В этой статье написано о объектно-ориентированном программировании и о его применении в python.
Объектно-ориентированное программирование (ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов.
Класс — тип, описывающий устройство объектов. Объект — это экземпляр класса. Класс можно сравнить с чертежом, по которому создаются объекты.
Python соответствует принципам объектно-ориентированного программирования. В python всё является объектами — и строки, и списки, и словари, и всё остальное.
Но возможности ООП в python этим не ограничены. Программист может написать свой тип данных (класс), определить в нём свои методы.
Это не является обязательным — мы можем пользоваться только встроенными объектами. Однако ООП полезно при долгосрочной разработке программы несколькими людьми, так как упрощает понимание кода.
Приступим теперь собственно к написанию своих классов на python. Попробуем определить собственный класс:
>>> # Пример самого простейшего класса ... class A: ... pass
Теперь мы можем создать несколько экземпляров этого класса:
>>> a = A() >>> b = A() >>> a.arg = 1 # у экземпляра a появился атрибут arg, равный 1 >>> b.arg = 2 # а у экземпляра b - атрибут arg, равный 2 >>> print(a.arg) 1
Классу возможно задать собственные методы:
>>> class A: ... def g(self): # self - обязательный аргумент, содержащий в себе экземпляр ... # класса, передающийся при вызове метода, ... # поэтому этот аргумент должен присутствовать ... # во всех методах класса. ... return 'hello world' ... >>> a = A() >>> a.g() 'hello world'
И напоследок еще один пример:
>>> class B: ... arg = 'Python' # Все экземпляры этого класса будут иметь атрибут arg, ... # равный "Python" ... # Но впоследствии мы его можем изменить ... def g(self): ... return self.arg ... >>> b = B() >>> b.g() 'Python' >>> B.g(b) 'Python' >>> b.arg = 'spam' >>> b.g() 'spam'
Как устроено ООП в Python? Что такое классы и объекты?
Объекты — это инкапсуляция переменных и функций в единый объект. Объекты получают свои переменные и функции от классов. Классы по сути являются шаблоном для создания ваших объектов.
Очень простой класс будет выглядеть примерно так:
class MyClass:
variable = "blah"
def function(self):
print("This is a message inside the class.")
Мы объясним, почему вы должны включить это «self» в качестве параметра чуть позже. Во-первых, чтобы присвоить объекту вышеупомянутый класс (шаблон), вы должны сделать следующее:
class MyClass:
variable = "blah"
def function(self):
print("This is a message inside the class.")
myobjectx = MyClass()
Теперь переменная «myobjectx» содержит объект класса «MyClass», который содержит переменную и функцию, определенную в классе «MyClass».
Доступ к объектным переменным
Чтобы получить доступ к переменной внутри вновь созданного объекта «myobjectx», вы должны сделать следующее:
class MyClass:
variable = "blah"
def function(self):
print("This is a message inside the class.")
myobjectx = MyClass()
myobjectx.variable
Так, например, ниже будет выводиться строка «blah»:
class MyClass:
variable = "blah"
def function(self):
print("This is a message inside the class.")
myobjectx = MyClass()
print(myobjectx.variable)
Вы можете создать несколько разных объектов одного и того же класса (с одинаковыми переменными и определенными функциями). Однако каждый объект содержит независимые копии переменных, определенных в классе. Например, если нам нужно определить другой объект с классом «MyClass», а затем изменить строку в переменной выше:
class MyClass:
variable = "blah"
def function(self):
print("This is a message inside the class.")
myobjectx = MyClass()
myobjecty = MyClass()
myobjecty.variable = "yackity"
# Then print out both values
print(myobjectx.variable)
print(myobjecty.variable)
Доступ к функциям объекта
Для доступа к функции внутри объекта вы используете нотацию, аналогичную доступу к переменной:
class MyClass:
variable = "blah"
def function(self):
print("This is a message inside the class.")
myobjectx = MyClass()
myobjectx.function()
Выражение выше будет выводить сообщение «Это сообщение внутри класса»
Упражнение
У нас есть класс, определенный для транспортных средств. Создайте два новых автомобиля с именами car1 и car2. Сделайте car1 красным кабриолетом стоимостью 60 000 долларов США с именем Fer, а car2 — синий фургон с именем Jump стоимостью 10 000 долларов США.
# define the Vehicle class
class Vehicle:
name = ""
kind = "car"
color = ""
value = 100.00
def description(self):
desc_str = "%s is a %s %s worth $%.2f." % (self.name, self.color, self.kind, self.value)
return desc_str
# your code goes here
# test code
print(car1.description())
print(car2.description())
# define the Vehicle class
class Vehicle:
name = ""
kind = "car"
color = ""
value = 100.00
def description(self):
desc_str = "%s is a %s %s worth $%.2f." % (self.name, self.color, self.kind, self.value)
return desc_str
# your code goes here
car1 = Vehicle()
car1.name = "Fer"
car1.color = "red"
car1.kind = "convertible"
car1.value = 60000.00
car2 = Vehicle()
car2.name = "Jump"
car2.color = "blue"
car2.kind = "van"
car2.value = 10000.00
# test code
print(car1.description())
print(car2.description())
#test_output_contains('Fer is a red convertible worth $60000.00.')
#test_output_contains('Jump is a blue van worth $10000.00.')
success_msg("Great job!")
8 советов по объектно-ориентированному программированию на Python | автор: Michał Oleszak
Чтобы сделать классы Python пуленепробиваемыми, выполните следующие действия.
Фото Джулиана Майлза на Unsplash. Объекты плохо спроектированы и рухнули.Объектно-ориентированное программирование или ООП - популярная парадигма кодирования, поскольку она обеспечивает возможность повторного использования кода и предотвращает избыточность. Его привлекательность сделала его популярным за пределами разработки программного обеспечения, в таких областях, как наука о данных и машинное обучение.Однако существует огромная разница между разработкой объекта, который просто работает, и написанием качественного кода, в полной мере использующего преимущества ООП-подхода. Давайте посмотрим на восемь шагов, которые ведут к пуленепробиваемым классам Python:
- Задайте атрибуты в конструкторе.
- Различают данные и методы уровня класса и уровня экземпляра.
- Определите, что равно.
- Обеспечивает строковое представление.
- Знайте, что такое статическое.
- Решите, что является внутренним, а что частным.
- Установить доступ к атрибутам.
- Используйте строки документации.
Объектно-ориентированное программирование
ООП основывается на идее, что код и данные, используемые этим кодом, должны храниться в одном месте. Отсюда понятие объектов , которые инкапсулируют данные и код вместе. Объект состоит из состояния и поведения. Состояние объекта сохраняется в данных, которые он содержит, также называемых атрибутами , , в то время как его поведение определяется методами .
объект = состояние (атрибуты) + поведение (методы)
В Python объекты определяются своими схемами, называемыми классами . Атрибуты класса представлены в виде переменных Python, а его методы воплощены в функциях Python. Рассмотрим следующий пример игрушечного класса, представляющего банковский счет.
Он имеет четыре метода, позволяющих, среди прочего, указать владельца счета или внести деньги. После установки владельца создается атрибут owner
.Этот класс можно считать полностью функциональным в том смысле, что мы можем с ним работать. Я могу создать для себя учетную запись без наличных денег и выполнять некоторые транзакции:
Michal 700
Однако этот код действительно плохо написан, так как он нарушает все восемь шагов к пуленепробиваемым классам. В результате он может выйти из строя так, как вы, возможно, не ожидали! Давайте посмотрим на каждый из восьми шагов один за другим.
# 1: Установить атрибуты в конструкторе
В нашем классе BankAccount есть специальные методы для установки атрибутов: чтобы настроить для себя учетную запись, я сначала создал пустую учетную запись, а затем использовал функцию set_owner ()
, чтобы присвоить мое имя атрибуту owner
.Это не идеально по двум причинам. Во-первых, некоторые атрибуты могут не существовать. Можно создать аккаунт без остатка. Как бы мы тогда вложили деньги? Во-вторых, атрибуты устанавливаются в разных местах. Если бы в этом классе было больше кода, кому-либо было бы трудно получить представление о том, какие атрибуты у него есть.
Задайте атрибуты в конструкторе, чтобы убедиться, что они существуют и легко обнаруживаются в одном месте.
Вот почему рекомендуется определять все атрибуты в одном месте, задав для них значения по умолчанию, чтобы они всегда существовали.Python предлагает удобный способ сделать это: конструктор __init __ ()
. Этот метод при добавлении в класс будет выполняться автоматически после создания экземпляра объекта. Конструктор также является хорошим местом для наложения различных проверок значений атрибутов, например. чтобы предотвратить создание новой учетной записи со слишком отрицательным балансом. Пока мы занимаемся этим, давайте добавим еще два атрибута, которые нам пригодятся позже: номер учетной записи и дату создания.
Теперь мы должны установить владельца и номер учетной записи при настройке учетной записи.В противном случае возникнет ошибка. Если мы не установим баланс, по умолчанию он будет равен нулю.
Michal 123 0
# 2: Различать данные и методы уровня класса и уровня экземпляра
Некоторые атрибуты и методы по своей природе присущи классу в целом, в то время как некоторые специфичны для экземпляров этого класса. Различие между ними жизненно важно, чтобы убедиться, что банковский счет человека не повлияет на работу банковских счетов в целом.
Данные уровня экземпляра не должны мешать работе класса в целом.Держите его отдельно от данных уровня класса.
Начнем с атрибутов. Такие переменные, как owner
, account_number
, created_at
или balance
, являются данными уровня экземпляра, поскольку они могут быть разными для каждой учетной записи. Однако порог баланса -10 000, с которым мы сравниваем баланс вновь созданной учетной записи, является уровнем класса: существует правило, согласно которому ни одна учетная запись не может быть создана с более низким балансом. Вместо того, чтобы жестко его кодировать, мы должны назначить его атрибуту в самом определении класса и получить доступ через self.
позже.
Что касается методов, deposit ()
и remove ()
работают одинаково для каждого экземпляра класса, то есть для каждой отдельной учетной записи, но они считаются методами уровня экземпляра. Это потому, что им нужен экземпляр, прежде чем их можно будет использовать. Нам необходимо настроить учетную запись, прежде чем мы будем вносить на нее деньги. Это отражено в аргументе self
, который принимает каждый из этих методов. self
обозначает экземпляр, конкретную учетную запись, на которую мы вносим депозит или с которой мы снимаем средства.
Методы уровня класса полезны, среди прочего, для создания объектов класса из внешних источников, таких как файлы csv.
В Python можно создавать методы класса, которые используют cls
, а не self
, то есть для существования им не нужен экземпляр. Их популярный вариант использования - фактически создать экземпляр из внешних источников. Нам может потребоваться метод, который создает экземпляр BankAccount из файла CSV с именем testfile.csv
, содержащий владельца и номер счета в формате, разделенном запятыми: Michal, 123
. Для этого мы можем добавить метод from_csv ()
. Мы украшаем его декоратором @classmethod
и используем ссылку на класс cls
в качестве первого аргумента. Мы можем использовать его в операторе return, чтобы метод возвращал экземпляр класса на основе содержимого файла CSV. Вот наш обновленный класс BankAccount:
А вот как мы можем использовать метод from_csv
:
Michal 123 0
# 3: Определить было равно
Теперь у нас есть код достойного качества! Пришло время использовать это на самом деле.Как вы думаете, что произойдет, если мы создадим две одинаковые учетные записи и сравним их с помощью оператора равенства?
Ложь
У двух учетных записей одинаковый владелец, номер учетной записи и дата создания, но все же сравнение их с оператором равенства дает Ложь
. Почему это?
При сравнении двух экземпляров объекта Python смотрит на блоки памяти, которые они занимают. Поскольку они были созданы независимо, они находятся в разных частях памяти моего компьютера.
При сравнении двух экземпляров объекта Python смотрит на блоки памяти, которые они занимают. Чтобы получить значимые сравнения, определите равенство явно.
Однако в нашем примере мы можем захотеть, чтобы две учетные записи с одинаковым номером и владельцем считались равными. Этого можно добиться с помощью специальной функции __eq __ ()
. Когда он определен внутри класса, он автоматически используется всякий раз, когда экземпляры класса сравниваются с помощью оператора ==
. Эта функция равенства принимает два аргумента, представляющих два объекта, обычно называемых self
и other
, и должна возвращать True
, когда объекты должны считаться равными, и False
в противном случае.Если мы хотим, чтобы две учетные записи были равны, если у них одинаковый номер учетной записи, достаточно добавить в наш класс следующее:
Таким же образом можно определить, когда один объект должен считаться большим, чем другой ( __gt __ ( )
), меньше или равно другому ( __le __ ()
) и так далее.
# 4: Предоставить строковое представление
После того, как мы определили банковский счет, мы можем захотеть распечатать его, чтобы увидеть, как он выглядит.
<__ main__.Объект BankAccount по адресу 0x7f9ec0716a10>
На самом деле не имеет смысла, не так ли? Эти выходные данные для печати показывают тип объекта, то есть BankAccount
, и шестнадцатеричное число, указывающее на блок памяти, где он хранится. Вместо этого мы могли бы захотеть, чтобы print ()
возвращала что-то более информативное и удобное для пользователя. Например, печать pandas DataFrame показывает нам данные в удобочитаемом формате. Как ребята из панд это сделали?
Определение строки и воспроизводимых представлений для объекта делает его удобочитаемым при печати и легким для воспроизведения, например.г. для отладки.
То, что печатается, можно переопределить, предоставив строковое представление для нашего объекта. Это делается путем определения специальной функции __str __ ()
. Его единственная задача - вывести строку, которую мы хотим напечатать. Еще одно родственное понятие - воспроизводимое представление или воспроизведение. Определение специальной функции __repr __ ()
, которая предоставляет точную строку, которая может использоваться для воссоздания объекта, является лучшей практикой, позволяющей легко понять, как был создан объект.Давайте добавим все это в наш код. Обратите внимание, что мы также добавляем в наш класс вспомогательную функцию для обработки дат синтаксического анализа. Об этом мы поговорим позже.
И вот что это дает нам:
Банковский счет:
Владелец счета: Михал
Номер счета: 123
Дата создания: 2021-05-24
Текущий баланс: 0 "BankAccount (owner = 'Michal', account_number = 123, balance = 0) "
Сначала мы получаем удобное строковое представление банковского счета. Во-вторых, вызывая repr, мы получаем строку, которая может быть оценена для воссоздания объекта в его текущем состоянии.
# 5: Знайте, что такое статическое
В предыдущем разделе мы добавили функцию to_dash_date ()
, которая обрабатывает простое форматирование даты. Он не имеет доступа ни к каким данным или методам класса. На самом деле, мы могли бы поместить его вне класса, но поскольку это служебная программа, используемая функцией __str __ ()
для получения удобочитаемого строкового представления, имеет смысл разместить ее внутри класса.
Пометка функций как статических экономит память и улучшает читаемость кода.
Такие функции называются статическими. Python позволяет явно отмечать статические функции как таковые с помощью декоратора @staticmethod
:
Это дает несколько преимуществ. Во-первых, это избавляет от необходимости использовать аргумент self
, поскольку методу не требуется доступ к экземпляру класса. Во-вторых, это уменьшает использование памяти (в этом случае Python не нужно создавать связанный метод). Наконец, пометка методов как статических улучшает читаемость кода, поскольку сразу же указывает на то, что этот метод является отдельным фрагментом кода, независимым от состояния экземпляра класса.
# 6: Определите, что является внутренним, а что частным
Некоторые методы и атрибуты в любом классе предназначены для явного использования пользователем кода, например методы remove ()
и deposit ()
в нашем классе BankAccount . Некоторые, однако, нет. Метод to_dash_date ()
- это вспомогательная утилита, вызываемая внутренним классом, но не предназначенная для ручного вызова. Такие методы и атрибуты называются внутренними , и лучше всего начинать их имена с подчеркивания, чтобы у нас было _to_dash_date ()
, self._owner
и так далее. Это соглашение об именах само по себе ничего не делает с для , но оно позволяет людям, просматривающим ваш код, сразу распознавать, какие методы не являются частью общедоступного API, и поэтому могут неожиданно измениться в будущих версиях кода.
Связанная тема - это методы и атрибуты private . Возможно, вы захотите скрыть какой-либо метод или атрибуты от внешнего мира, например чтобы убедиться, что они не перезаписаны. В Python нет механизма, чтобы сделать их полностью скрытыми, но мы можем пометить их как частные, обозначив их двойным подчеркиванием в начале.Например, мы можем захотеть, чтобы минимальный порог баланса был частным, и поэтому мы установили его следующим образом:
Верхнее подчеркивание используется для именования внутренних методов и атрибутов, а двойное подчеркивание отмечает частные.
Теперь, даже если он доступен внутри класса как обычно, вызов my_account .__ MIN_BALANCE
вызовет исключение. Это означает для пользователя, что этот атрибут является частным и не подлежит изменению. Тем не менее, к нему можно получить доступ благодаря уловке под названием name mangling .Python автоматически преобразует частные данные из объекта .__ privatestuff
в объект ._classname__privatestuff
, поэтому мы можем получить доступ к частному атрибуту как my_account._BankAccount__MIN_BALANCE
. Однако такой практики следует избегать.
# 7: Установить доступ к атрибутам
Рассмотрим этот фрагмент кода:
Банковский счет:
Владелец счета: Михал
Номер счета: 123
Дата создания: 1900-01-01
Текущий баланс: -999999
Как Как видите, довольно легко изменить дату создания в существующей учетной записи, а также установить баланс на большое отрицательное число.Напомним, что дата создания устанавливается автоматически при создании объекта, и мы не сможем создать учетную запись со слишком отрицательным балансом благодаря проверке ввода в методе __init __ ()
. Что мы можем сделать по этому поводу? В идеале дата создания должна быть атрибутом только для чтения, а баланс должен проверяться каждый раз при его обновлении. Это может быть достигнуто в Python с помощью свойств и установщиков.
Используйте свойства и установщики, чтобы сделать атрибуты доступными только для чтения и проверять их обновления.
Чтобы, скажем, сделать атрибут balance
доступным только для чтения, все, что нужно сделать, это добавить функцию в класс, вызываемый точно так же, как этот атрибут, украшенный декоратором @property
и возвращающий значение атрибута.
Теперь, однажды заданное при создании, значение атрибута не может быть изменено.
0 ----------------------------------------------- ---------------------
AttributeError Traceback (последний вызов последний)
в
2
3 печать (my_account.balance)
----> 4 my_account.balance = 100AttributeError: невозможно установить атрибут
Круто! Это сработает для номера учетной записи или даты создания, но для баланса мы действительно хотели бы, чтобы его можно было обновлять, но не со слишком отрицательным значением. Чтобы добиться этого, нам нужна еще одна вещь поверх метода с декорированием свойств. Нам нужна еще одна функция в классе, также называемая balance
и украшенная декоратором @ {attribute_name} .setter
, в данном случае: @balance.Сеттер
. Функция должна принять новый баланс в качестве аргумента и обновить значение атрибута в объекте. Пока он обновляется, мы можем добавить любую проверку, какую захотим. Например, чтобы гарантировать, что баланс никогда не опускается ниже минимального порога, мы можем сделать следующее.
Теперь каждый раз, когда устанавливается атрибут balance
, вызывается метод установки и запускается наша проверка. Следовательно, баланс не может опуститься ниже -10,000.
1000
-1000
-3000
-5000
-7000
-9000 -------------------------------- ------------------------------------
ValueError Traceback (последний вызов последний)
в
3 для i в диапазоне (10):
4 print (my_account.balance)
----> 5 my_account.balance - = 2000
in balance (self, new_balance)
19 def balance (self, new_balance):
20 if new_balance---> 21 повысить ValueError ("Баланс до малого!")
22 else:
23 self._balance = new_balanceValueError: Баланс до малого!
# 8: Используйте строки документации
И последнее, но не менее важное: документация! В отличие от внешней документации в форме readmes, строки документации не устаревают так быстро.Во-первых, потому что легче не забыть обновить их, пока мы обновляем код прямо под ним, а во-вторых, потому что большинство IDE будут предупреждать нас об устаревших строках документации. Они действительно помогают разобраться в коде при первой работе с ним. Итак, что должно быть включено в строки документации класса и как их записать?
PEP8, широко используемое руководство по стилю написания кода Python, утверждает, что следует ниже.
Напишите строки документации для всех общедоступных модулей, функций, классов и методов.Строки документации не нужны для закрытых методов, но у вас должен быть комментарий, описывающий, что делает метод. Этот комментарий должен появиться после строки «def».
Следовательно, мы должны включить общедоступные методы deposit ()
, remove ()
и from_csv ()
. Единственный закрытый метод, помимо всех свойств, установщиков и специальных методов, - это _to_dash_date ()
, поэтому он получит комментарий. Что касается атрибутов, нам не обязательно включать частный __MIN_BALANCE
, но его полезно задокументировать.Прокрутите вниз до нашей новой строки документации класса.
Наконец, вот наш класс BankAccount после всех доработок.
Классы Python: Учебное пособие по объектно-ориентированному программированию (ООП) с примерами кода
Объектно-ориентированное программирование - широко используемая концепция для написания мощных приложений. Как специалист по данным, вам, помимо прочего, потребуется писать приложения для обработки ваших данных. В этом руководстве вы познакомитесь с основами объектно-ориентированного программирования на Python.Вы узнаете следующее:
ООП: Введение
Объектно-ориентированное программирование имеет некоторые преимущества перед другими шаблонами проектирования. Разработка выполняется быстрее и дешевле, с улучшенной ремонтопригодностью программного обеспечения. Это, в свою очередь, приводит к более качественному программному обеспечению, которое также можно расширять с помощью новых методов и атрибутов. Однако кривая обучения более крутая. Концепция может быть слишком сложной для новичков. В вычислительном отношении программное обеспечение ООП работает медленнее и использует больше памяти, поскольку необходимо писать больше строк кода.
Объектно-ориентированное программирование основано на парадигме императивного программирования, которая использует операторы для изменения состояния программы. Он фокусируется на описании того, как должна работать программа. Примеры императивных языков программирования: C, C ++, Java, Go, Ruby и Python. Это контрастирует с декларативным программированием, которое фокусируется на том, что должна выполнять компьютерная программа, без указания того, как. Примерами являются языки запросов к базам данных, такие как SQL и XQuery, где один только сообщает компьютеру, какие данные и откуда запрашивать, а теперь как это сделать.
ООП использует концепцию объектов и классов. Класс можно рассматривать как «план» для объектов. Они могут иметь свои собственные атрибуты (характеристики, которыми они обладают) и методы (действия, которые они выполняют).
Пример в ООП
Примером класса является класс Dog
. Не думайте об этом как о конкретной собаке или своей собственной собаке. Мы описываем, что такое собака , и может делать в целом. Собаки обычно имеют имя
и возраст
; это атрибуты экземпляра.Собаки также могут лаять
; это метод.
Когда вы говорите о конкретной собаке, у вас будет объект в программировании: объект является экземпляром класса. Это основной принцип, на котором основано объектно-ориентированное программирование. Так что моя собака Оззи, например, принадлежит к классу Dog
. Его атрибуты: name = 'Ozzy'
и age = '2'
. У разных собак будут разные атрибуты.
ООП в Python
Python - отличный язык программирования, поддерживающий ООП.Вы будете использовать его для определения класса с атрибутами и методами, которые затем будете вызывать. Python предлагает ряд преимуществ по сравнению с другими языками программирования, такими как Java, C ++ или R. Это динамический язык с типами данных высокого уровня. Это означает, что разработка происходит намного быстрее, чем на Java или C ++. От программиста не требуется объявлять типы переменных и аргументов. Это также упрощает понимание и изучение Python для начинающих, а его код становится более читаемым и интуитивно понятным.
Если вы новичок в Python, обязательно ознакомьтесь с курсом DataCamp Intro to Python for Data Science.
Как создать класс
Чтобы определить класс в Python, вы можете использовать ключевое слово class
, за которым следует имя класса и двоеточие. Внутри класса должен быть определен метод __init__
с def
. Это инициализатор, который позже можно использовать для создания экземпляров объектов. Он похож на конструктор в Java. __init__
должен присутствовать всегда! Он принимает один аргумент: self
, который относится к самому объекту.Внутри метода сейчас используется ключевое слово pass
, потому что Python ожидает, что вы что-то там напечатаете. Не забывайте использовать правильный отступ!
класс Собака:
def __init __ (сам):
проходить
Примечание : self
в Python эквивалентно , это
в C ++ или Java.
В этом случае у вас есть (в основном пустой) класс Dog
, но еще нет объекта. Создадим один!
Создание экземпляров объектов
Чтобы создать экземпляр объекта, введите имя класса и поставьте его в две скобки.Вы можете присвоить это переменной, чтобы отслеживать объект.
Оззи = Собака ()
И распечатайте это:
печать (Оззи)
<__ main__.Dog объект в 0x111f47278>
Добавление атрибутов в класс
После печати ozzy
становится ясно, что это собака. Но вы еще не добавили никаких атрибутов. Давайте дадим классу Dog
имя и возраст, переписав его:
класс Собака:
def __init __ (я, имя, возраст):
себя.name = имя
self.age = возраст
Вы можете видеть, что функция теперь принимает два аргумента после self
: name
и age
. Затем им присваиваются значения self.name
и self.age
соответственно. Теперь вы можете создать новый объект ozzy
с именем и возрастом:
ozzy = Собака ("Оззи", 2)
Для доступа к атрибутам объекта в Python вы можете использовать точечную нотацию.Это делается путем ввода имени объекта, точки и имени атрибута.
печать (ozzy.name)
печать (ozzy.age)
Оззи
2
Это также можно объединить в более сложное предложение:
print (ozzy.name + "is" + str (ozzy.age) + "год (лет).")
Оззи 2 года (лет).
Здесь используется функция str ()
для преобразования атрибута age
, который является целым числом, в строку, чтобы вы могли использовать его в функции print ()
.
Определите методы в классе
Теперь, когда у вас есть класс Dog
, у него есть имя и возраст, которые вы можете отслеживать, но на самом деле он ничего не делает. Вот тут-то и пригодятся методы экземпляра. Вы можете переписать класс, включив в него метод bark ()
. Обратите внимание, как снова используется ключевое слово def
, а также аргумент self
.
класс Собака:
def __init __ (я, имя, возраст):
себя.name = имя
self.age = возраст
def кора (сам):
печать («кора кора!»)
Метод bark
теперь может быть вызван с использованием точечной записи после создания нового объекта ozzy
. Метод должен напечатать «кора, кора!» к экрану. Обратите внимание на круглые скобки (фигурные скобки) в .bark ()
. Они всегда используются при вызове метода. В этом случае они пусты, поскольку метод bark ()
не принимает никаких аргументов.
ozzy = Собака ("Оззи", 2)
ozzy.bark ()
кора кора!
Вспомните, как вы раньше печатали ozzy
? Приведенный ниже код теперь реализует эту функциональность в классе Dog
с методом doginfo ()
. Затем вы создаете экземпляры некоторых объектов с разными свойствами и вызываете для них метод.
класс Собака:
def __init __ (я, имя, возраст):
self.name = имя
себя.age = возраст
def кора (сам):
печать («кора кора!»)
def doginfo (сам):
print (self.name + "is" + str (self.age) + "year (s) old.")
ozzy = Собака ("Оззи", 2)
skippy = Dog ("Скиппи", 12)
filou = Собака ("Filou", 8)
ozzy.doginfo ()
skippy.doginfo ()
filou.doginfo ()
Оззи 2 года (лет).
Скиппи 12 лет.
Филу 8 лет.
Как видите, вы можете вызывать метод doginfo ()
для объектов с точечной нотацией. Теперь ответ зависит от того, для какого объекта Dog
вы вызываете метод.
Поскольку собаки стареют, было бы неплохо, если бы вы могли соответствующим образом скорректировать их возраст. Оззи только что исполнилось 3 года, так что давайте изменим его возраст.
ozzy.age = 3
печать (ozzy.age)
3
Это так же просто, как присвоить атрибуту новое значение.Вы также можете реализовать это как метод birthday ()
в классе Dog
:
класс Собака:
def __init __ (я, имя, возраст):
self.name = имя
self.age = возраст
def кора (сам):
печать («кора кора!»)
def doginfo (сам):
print (self.name + "is" + str (self.age) + "year (s) old.")
def день рождения (сам):
self.age + = 1
ozzy = Собака ("Оззи", 2)
печать (ozzy.возраст)
2
оззи.день рождения ()
печать (ozzy.age)
3
Теперь вам не нужно вручную изменять возраст собаки. всякий раз, когда у него день рождения, вы можете просто вызвать метод birthday ()
.
Передача аргументов методам
Вы бы хотели, чтобы у наших собак был приятель. Это должно быть необязательно, поскольку не все собаки так общительны.Взгляните на метод setBuddy ()
ниже. Он принимает self
, как обычно, и buddy
в качестве аргументов. В этом случае buddy
будет другим объектом Dog
. Установите для атрибута self.buddy
значение buddy
и для атрибута buddy.buddy
значение self
. Это означает, что отношения взаимны; ты приятель твоего приятеля. В этом случае Филу будет приятелем Оззи, а это означает, что Оззи автоматически становится приятелем Филу.Вы также можете установить эти атрибуты вручную, вместо определения метода, но это потребует больше работы (написание 2 строк кода вместо 1) каждый раз, когда вы захотите установить друга. Обратите внимание, что в Python вам не нужно указывать тип аргумента. Если бы это была Java, это потребовалось бы.
класс Собака:
def __init __ (я, имя, возраст):
self.name = имя
self.age = возраст
def кора (сам):
печать («кора кора!»)
def doginfo (сам):
печать (сам.name + "is" + str (self.age) + "год (лет)")
def день рождения (сам):
self.age + = 1
def setBuddy (я, приятель):
self.buddy = приятель
buddy.buddy = self
Теперь вы можете вызвать метод с точечной нотацией и передать ему другой объект Dog
. В этом случае приятелем Оззи будет Филу:
ozzy = Собака ("Оззи", 2)
filou = Собака ("Filou", 8)
ozzy.setBuddy (filou)
Если теперь вы хотите получить некоторую информацию о приятеле Оззи, вы можете дважды использовать точечную нотацию :.Во-первых, чтобы сослаться на приятеля Оззи, а во второй раз - на его атрибут.
печать (ozzy.buddy.name)
печать (ozzy.buddy.age)
Filou
8
Обратите внимание, как это можно сделать и для Filou.
печать (filou.buddy.name)
печать (filou.buddy.age)
Оззи
2
Методы приятеля также могут быть вызваны. Аргумент self
, который передается в doginfo ()
, теперь равен ozzy.приятель
, то есть filou
.
ozzy.buddy.doginfo ()
Филу 8 лет (лет).
Пример: ООП в Python для финансов
Примером, где может пригодиться объектно-ориентированное программирование на Python, является наш учебник Python For Finance: Algorithmic Trading. В нем Карлин объясняет, как настроить торговую стратегию для портфеля акций. Торговая стратегия основана на скользящей средней цены акции.Если выполняется сигналов ['short_mavg'] [short_window:]> сигналов ['long_mavg'] [short_window:]
, создается сигнал. Этот сигнал является предсказанием будущего изменения цены акции. В приведенном ниже коде вы увидите, что сначала выполняется инициализация, а затем вычисление скользящего среднего и генерация сигнала. Поскольку это не объектно-ориентированный код, это всего лишь один большой фрагмент, который выполняется сразу. Обратите внимание, что в примере мы используем aapl
, тикер акций Apple.Если вы захотите сделать это для другой акции, вам придется переписать код.
# Инициализировать
short_window = 40
long_window = 100
сигналы = pd.DataFrame (индекс = aapl.index)
сигналы ['signal'] = 0,0
# Создайте короткую простую скользящую среднюю по короткому окну
сигналы ['short_mavg'] = aapl ['Close']. Rolling (window = short_window, min_periods = 1, center = False) .mean ()
# Создать длинную простую скользящую среднюю по длинному окну
сигналы ['long_mavg'] = aapl ['Закрыть'].скользящее (окно = длинное_окно, min_periods = 1, center = False) .mean ()
# Создать сигналы
сигналы ['сигнал'] [short_window:] = np.where (сигналы ['short_mavg'] [short_window:]> сигналы ['long_mavg'] [short_window:], 1.0, 0.0)
# Генерация торговых приказов
сигналы ['позиции'] = сигналы ['сигнал']. diff ()
# Вывести `сигналы`
печать (сигналы)
При объектно-ориентированном подходе вам нужно всего один раз написать код инициализации и генерации сигнала.Затем вы можете создать новый объект для каждой акции, для которой хотите рассчитать стратегию, и вызвать для него метод generate_signals ()
. Обратите внимание, что код ООП очень похож на код выше, с добавлением self
.
класс MovingAverage ():
def __init __ (self, symbol, bars, short_window, long_window):
self.symbol = символ
self.bars = бары
self.short_window = короткое_окно
self.long_window = длинное_окно
def generate_signals (сам):
сигналы = pd.DataFrame (index = self.bars.index)
сигналы ['signal'] = 0,0
сигналы ['short_mavg'] = бары ['Close']. Rolling (window = self.short_window, min_periods = 1, center = False) .mean ()
сигналы ['long_mavg'] = бары ['Close']. Rolling (window = self.long_window, min_periods = 1, center = False) .mean ()
сигналы ['сигнал'] [self.short_window:] = np.where (сигналы ['short_mavg'] [self.short_window:]> сигналы ['long_mavg'] [self.short_window:], 1.0, 0.0)
сигналы ['позиции'] = сигналы ['сигнал'].diff ()
ответные сигналы
Теперь вы можете просто создать экземпляр объекта с нужными параметрами и сгенерировать для него сигналы.
яблоко = MovingAverage ('aapl', aapl, 40, 100)
печать (apple.generate_signals ())
Сделать это для другой акции становится очень просто. Это просто вопрос создания экземпляра нового объекта с другим символом акций.
microsoft = MovingAverage ('msft', msft, 40, 100)
печать (microsoft.generate_signals ())
Поздравляем!
Теперь вы знаете, как объявлять классы и методы, создавать экземпляры объектов, устанавливать их атрибуты и вызывать методы экземпляров. Эти навыки пригодятся вам в вашей будущей карьере специалиста по данным. Если вы хотите расширить ключевые концепции, необходимые для дальнейшей работы с Python, обязательно ознакомьтесь с нашим курсом «Промежуточный Python для науки о данных».
С ООП ваш код будет усложняться по мере того, как ваша программа становится больше.У вас будут разные классы, подклассы, объекты, наследование, методы экземпляра и многое другое. Вы захотите, чтобы ваш код был правильно структурирован и читабельным. Для этого рекомендуется следовать шаблонам проектирования. Это принципы дизайна, которые представляют собой набор рекомендаций, позволяющих избежать плохого дизайна. Каждый из них представляет конкретную проблему, которая часто повторяется в ООП, и описывает решение этой проблемы, которое затем можно использовать повторно. Эти шаблоны проектирования ООП можно разделить на несколько категорий: шаблоны создания, структурные шаблоны и поведенческие шаблоны.Примером шаблона создания является синглтон, который следует использовать, если вы хотите убедиться, что может быть создан только один экземпляр класса. Итератор, который используется для перебора всех объектов в коллекции, является примером поведенческого шаблона. Отличный ресурс для шаблонов проектирования - oodesign.com. Если вам больше нравятся книги, я бы порекомендовал вам прочитать Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения.
объектно-ориентированного программирования на Python | DataCamp
Описание курса
Объектно-ориентированное программирование (ООП) - это широко используемая парадигма программирования, которая сокращает время разработки, упрощая чтение, повторное использование и поддержку вашего кода.ООП смещает фокус с размышления о коде как о последовательности действий на рассмотрение вашей программы как набора объектов, которые взаимодействуют друг с другом. В этом курсе вы узнаете, как создавать классы, которые действуют как схемы для каждого объекта в Python. Затем вы примените принципы, называемые наследованием и полиморфизмом, для повторного использования и оптимизации кода. Погрузитесь в игру и узнайте, как создавать красивый, чистый и эффективный код!
В этой главе вы узнаете, что такое объектно-ориентированное программирование (ООП), чем оно отличается от процедурного программирования и как его можно применить.Затем вы определите свои собственные классы и узнаете, как создавать методы, атрибуты и конструкторы.
Наследование и полиморфизм
Наследование и полиморфизм являются основными концепциями ООП, которые обеспечивают эффективное и согласованное повторное использование кода. Узнайте, как наследовать от класса, настраивать и переопределять методы, а также изучать различия между данными уровня класса и данными уровня экземпляра.
Интеграция со стандартным Python
В этой главе вы узнаете, как убедиться, что объекты, в которых хранятся одни и те же данные, считаются равными, как определять и настраивать строковые представления объектов и даже как создавать новую ошибку типы.С помощью интерактивных упражнений вы узнаете, как дополнительно настроить классы, чтобы они работали как стандартные типы данных Python.
Лучшие практики проектирования классов
Как вы проектируете классы для наследования? Есть ли у Python частные атрибуты? Можно ли контролировать доступ к атрибутам? Вы найдете ответы на эти (и другие) вопросы по мере изучения передовых методов проектирования классов.
Предварительные требования
Написание функций на PythonАлекс Ярош
Разработчик учебных программ @ Cockroach Labs
Алекс - инженер-программист и математик.В настоящее время они работают в Cockroach Labs - компании, стоящей за CockroachDB, - где разрабатывают онлайн-тренинги по продуктам. Им нравятся кошки, обучение и дрессировки.
ПодробнееPython OOP - Основное руководство по объектно-ориентированному программированию на Python
В этом объектно-ориентированном программировании Python ясно объясняется, что такое объектно-ориентированное программирование Python, чтобы вы могли применять его для более эффективной разработки программного обеспечения.
К концу этого модуля Python ООП вы будете хорошо знакомы с принципами объектно-ориентированного программирования.И вы будете знать, как использовать синтаксис Python для создания надежных и надежных программных приложений.
Что вы узнаете
- Создавайте объекты в Python, определяя классы и методы.
- Расширить классы с помощью наследования.
- Поймите, когда использовать объектно-ориентированные функции и, что более важно, когда их не использовать.
Для кого предназначен этот учебник
Если вы новичок в объектно-ориентированном программировании или если у вас есть базовые навыки Python и вы хотите подробно изучить, как и когда правильно применять ООП в Python, это руководство для ты.
Раздел 1. Классы и объекты
- Классы и объекты - узнайте, как определять класс и создавать новые объекты из класса.
- Метод __init __ () - покажет, как определить конструктор для класса с помощью метода __init __ ().
- Атрибуты экземпляра и класса - понимайте атрибуты экземпляра и атрибуты класса и, что более важно, когда следует использовать атрибуты класса.
- Частные атрибуты - узнайте о частных атрибутах и о том, как их эффективно использовать.
- Свойства - покажет, как использовать декоратор @property для определения свойств класса.
- Статические методы - объяснят вам статические методы и покажут, как их использовать для группировки связанных функций в классе.
Раздел 2. Строковое представление объектов
- Метод __str__ - покажет, как использовать метод __str__ dunder для возврата строкового представления объекта.
- Метод __repr__ - узнайте, как использовать метод __repr__ и основные различия между методами __str__ и __repr__.
Раздел 3. Сравнение объектов
- Метод __eq__ - узнайте, как определить логику равенства для сравнения объектов по значениям.
Раздел 4. Наследование
- Наследование - объясните вам концепцию наследования и способы определения класса, который наследуется от другого класса.
- Абстрактные классы - покажут, как определять абстрактные классы и как использовать их для создания схем для других классов.
Вы нашли это руководство полезным?
Объектно-ориентированное программирование Python | Концепции ООП в Python
Объектно-ориентированное программирование как дисциплина приобрело всеобщее признание среди разработчиков.Python, востребованный язык программирования, также следует парадигме объектно-ориентированного программирования. Он занимается объявлением классов и объектов Python, которые закладывают основу концепций ООП. Эта статья о «Python объектно-ориентированного программирования» проведет вас через объявление классов python, создание экземпляров объектов из них вместе с четырьмя методологиями ООП.
В этой статье будут подробно рассмотрены следующие аспекты:
Приступим.
Что такое объектно-ориентированное программирование? (Концепции ООП в Python)
Объектно-ориентированное программирование - это способ компьютерного программирования, использующий идею «объектов» для представления данных и методов.Это также подход, используемый для создания аккуратного и многоразового кода вместо избыточного. программа разбита на автономные объекты или несколько мини-программ. Каждый индивидуальный объект представляет собой отдельную часть приложения, имеющую собственную логику и данные для взаимодействия внутри себя.
Теперь, чтобы получить более ясное представление о том, почему мы используем oops вместо pop, я перечислил ниже различия.
Разница между объектно-ориентированным и процедурно-ориентированным программированиемОбъектно-ориентированное Программирование (ООП) | Процедурное программирование Это восходящий подход | Нисходящий подход |
Программа разделена на объекты | Программа разделена на функции | |
Использует Access модификаторы 'public', private ', protected' | Не использует Модификаторы доступа | |
Это более безопасно | Это менее безопасно | |
Данные могут свободно перемещаться из функции n для работы в программах | ||
Он поддерживает наследование | Он не поддерживает наследование |
Это все о различиях, продвигаясь вперед, давайте разберемся с Python OOP Conceots.
Что такое концепции ООП Python?Основные концепции ООП (объектно-ориентированного программирования) в Python включают класс, объект, метод, наследование, полиморфизм, абстракцию данных и инкапсуляцию.
Вот и все о различиях, забегая вперед, давайте разберемся с классами и объектами.
Что такое классы и объекты?
Класс - это набор объектов, или можно сказать, что это план объектов, определяющих общие атрибуты и поведение.Возникает вопрос, как это сделать?
Что ж, он логически группирует данные таким образом, что повторное использование кода становится простым. Я могу привести вам пример из реальной жизни: представьте, что офис работает как «сотрудник» как класс и все атрибуты, связанные с ним, такие как 'emp_name', 'emp_age', 'emp_salary', 'emp_id', как объекты в Python. Давайте посмотрим с точки зрения кодирования, как создать экземпляр класса и объекта.
Класс определяется ключевым словом «Класс».
Пример:
class class1 (): // class 1 - это имя класса
Примечание. Python не чувствителен к регистру.
Объекты:Объекты являются экземпляром класса. Это сущность, у которой есть состояние и поведение. В двух словах, это экземпляр класса, который может получить доступ к данным.
Синтаксис: obj = class1 ()
Здесь obj - это «объект» class1.
Создание объекта и класса в python:Пример:
class employee (): def __init __ (self, name, age, id, salary): // создание функции себя.name = name // self - это экземпляр класса self.age = возраст self.salary = зарплата self.id = id emp1 = employee ("harshit", 22,1000,1234) // создание объектов emp2 = сотрудник ("арджун", 23,2000,2234) print (emp1 .__ dict __) // Распечатывает словарь
Объяснение: 'emp1' и 'emp2' - это объекты, экземпляры которых создаются для класса «employee». Здесь слово (__dict__) представляет собой «словарь», который печатает все значения объекта «emp1» против заданный параметр (имя, возраст, зарплата).(__init__) действует как конструктор, который вызывается всякий раз, когда создается объект.
Надеюсь, теперь вы, ребята, не столкнетесь с какими-либо проблемами при работе с «классами» и «объектами» в будущем.
Итак, позвольте мне познакомить вас с методологиями объектно-ориентированного программирования:
Методологии объектно-ориентированного программирования:Методологии объектно-ориентированного программирования имеют дело со следующими концепциями.
- Наследование
- Полиморфизм
- Инкапсуляция
- Абстракция
Давайте подробно разберемся с первой концепцией наследования.
Наследование:Когда-либо слышали об этом диалоге от родственников «вы выглядите точно так же, как ваш отец / мать». Причина этого называется «наследование». С точки зрения программирования это обычно означает «наследование или передачу характеристик от родительского к дочернему классу без каких-либо изменений». Новый класс называется производным / дочерним классом , а тот, от которого он является производным, называется родительским / базовым классом.
Давайте разберемся с каждой из подтем подробно.
Одиночное наследование:Одноуровневое наследование позволяет производному классу наследовать характеристики от единственного родительского класса.
Пример:
class employee1 (): // Это родительский класс def __init __ (я, имя, возраст, зарплата): self.name = имя self.age = возраст self.salary = зарплата class childemployee (employee1): // Это дочерний класс def __init __ (я, имя, возраст, зарплата, идентификатор): self.name = имя self.age = возраст self.salary = зарплата себя.id = id emp1 = employee1 ('суровый', 22,1000) печать (emp1.age)
Выход : 22
Объяснение:
Я беру родительский класс и создал конструктор (__init__), сам класс инициализирует атрибуты параметрами ('name', 'age' и 'salary ').
Создан дочерний класс childemployee, который наследует свойства от родительского класса, и, наконец, созданы экземпляры объектов emp1 и emp2 в соответствии с параметрами.
Наконец, я напечатал возраст emp1. Что ж, вы можете делать кучу всего, например, распечатать весь словарь, или имя, или зарплату.
Многоуровневое наследование позволяет производному классу наследовать свойства от непосредственного родительского класса, который, в свою очередь, наследует свойства от своего родительского класса.
Пример:
class employee (): // Суперкласс def __init __ (я, имя, возраст, зарплата): себя.name = имя self.age = возраст self.salary = зарплата class childemployee1 (employee): // Первый дочерний класс def __init __ (я, имя, возраст, зарплата): self.name = имя self.age = возраст self.salary = зарплата class childemployee2 (childemployee1): // Второй дочерний класс def __init __ (я, имя, возраст, зарплата): self.name = имя self.age = возраст self.salary = зарплата emp1 = сотрудник ('суровый', 22,1000) emp2 = childemployee1 ('арджун', 23,2000) печать (emp1.age) печать (emp2.age)
Выход: 22,23
Объяснение:
Это ясно объяснено в коде, написанном выше. Здесь я определил суперкласс как сотрудник, а дочерний класс как childemployee1.Теперь childemployee1 действует как родитель для childemployee2.
Я создал два объекта emp1 и emp2, где я передаю параметры «имя», «возраст», «зарплата» для emp1 из суперкласса «сотрудник» и «имя», «возраст,« зарплата ». »И« id »из родительского класса« childemployee1 »
Наследование на иерархическом уровне позволяет более чем одному производному классу наследовать свойства от родительского класса.
Пример:
класс сотрудник (): def __init __ (я, имя, возраст, зарплата): // Иерархическое наследование self.name = имя self.age = возраст self.salary = зарплата класс childemployee1 (сотрудник): def __init __ (я, имя, возраст, зарплата): self.name = имя self.age = возраст self.salary = зарплата класс childemployee2 (сотрудник): def __init __ (я, имя, возраст, зарплата): self.name = имя self.age = возраст self.salary = зарплата emp1 = сотрудник ('суровый', 22,1000) emp2 = сотрудник ('арджун', 23,2000) печать (emp1.возраст) печать (emp2.age)
Выход: 22,23
Объяснение:В приведенном выше примере вы можете ясно видеть, что существует два дочерних класса «childemployee1» и «childemployee2». Они наследуют функции от общего родительского класса, который называется «служащий».
Объекты emp1 и emp2 создаются по параметрам «имя», «возраст», «зарплата».
Множественное наследование позволяет одному производному классу наследовать свойства более чем одного базового класса.
Пример:
class employee1 (): // Родительский класс def __init __ (я, имя, возраст, зарплата): self.name = имя self.age = возраст self.salary = зарплата class employee2 (): // Родительский класс def __init __ (я, имя, возраст, зарплата, идентификатор): self.name = имя self.age = возраст self.salary = зарплата self.id = id класс childemployee (сотрудник1, сотрудник2): def __init __ (я, имя, возраст, зарплата, идентификатор): self.name = имя self.age = возраст себя.зарплата = зарплата self.id = id emp1 = employee1 ('суровый', 22,1000) emp2 = сотрудник2 ('арджун', 23,2000,1234) печать (emp1.age) печать (emp2.id)
Выход: 22,1234
Объяснение: В приведенном выше примере я взял два родительских класса «employee1» и «employee2». И дочерний класс «childemployee», который наследует оба родительских класса, создавая экземпляры объектов. 'emp1' и 'emp2' против параметров родительских классов.
Речь шла о наследовании, продвижении вперед в объектно-ориентированном программировании Python, давайте углубимся в «полиморфизм».
Полиморфизм:
Вы все должны были использовать GPS для навигации по маршруту. Разве не удивительно, сколько разных маршрутов вы встретите для одного и того же пункта назначения в зависимости от трафика, с точки зрения программирования это называется ' полиморфизм'. Это одна из таких методологий ООП, в которой одну задачу можно выполнять несколькими способами. Проще говоря, - это свойство объекта, которое позволяет ему принимать несколько форм .
Полиморфизм бывает двух типов:
- Полиморфизм времени компиляции
- Полиморфизм времени выполнения
также статический полиморфизм времени компиляции полиморфизм, который разрешается во время компиляции программы.Один из распространенных примеров - «перегрузка метода». Позвольте мне показать вам быстрый пример того же.
Пример:
класс employee1 (): def имя (сам): print ("Жестокое его имя") def зарплата (самостоятельно): print («3000 - его зарплата») def age (self): print («ему 22 года») класс employee2 (): def имя (сам): print («Рахул - его имя») def зарплата (самостоятельно): print («4000 - его зарплата») def age (self): print («ему 23 года») def func (obj): // Перегрузка метода obj.name () obj.salary () объектвозраст() obj_emp1 = сотрудник1 () obj_emp2 = сотрудник2 () func (obj_emp1) func (obj_emp2)
Вывод:
Харшит - его имя
3000 - его зарплата
22 - его возраст
Рахул - его имя
4000 - его зарплата
23 - его возраст
Пояснение:
Программа, я создал два класса «employee1» и «employee2» и создал функции для «имени», «зарплаты» и «возраста» и распечатал их значения, не принимая их у пользователя.
Теперь добро пожаловать в основную часть, где я создал функцию с параметром «obj» и вызвал все три функции, то есть «имя», «возраст» и «зарплата».
Позже были созданы экземпляры объектов emp_1 и emp_2 для двух классов и просто вызвана функция. Такой тип называется перегрузкой метода, который позволяет классу иметь более одного метода с одним и тем же именем.
Полиморфизм во время выполнения также называется динамическим полиморфизмом, где он разрешается во время выполнения.Одним из распространенных примеров полиморфизма времени выполнения является «переопределение метода». Позвольте мне показать вам пример для лучшего понимания.
Пример:
класс сотрудник (): def __init __ (я, имя, возраст, идентификатор, зарплата): self.name = имя self.age = возраст self.salary = зарплата self.id = id def зарабатывать (самостоятельно): проходить класс childemployee1 (сотрудник): def заработать (self): // полиморфизм времени выполнения print ("денег нет") класс childemployee2 (сотрудник): def зарабатывать (самостоятельно): print ("есть деньги") c = childemployee1 c.заработать (сотрудник) d = childemployee2 d.earn (сотрудник)
Вывод: нет денег, есть деньги
Объяснение: В приведенном выше примере я создал два класса childemployee1 и childemployee2, которые являются производными от одного и того же базового класса employee. не получил денег, тогда как другой получает. Теперь главный вопрос: как это произошло? Итак, если вы присмотритесь, я создал пустую функцию и использовал Pass (оператор, который используется, когда вы не хотите выполнять какую-либо команду или код).Теперь, в двух производных классах, я использовал одну и ту же пустую функцию и использовал оператор печати как «нет денег» и «имеет деньги». Наконец, создал два объекта и вызвал функцию.
Переходя к следующей методологии объектно-ориентированного программирования Python, я расскажу об инкапсуляции.
Инкапсуляция:В необработанном виде инкапсуляция в основном означает связывание данных в одном классе. В Python, в отличие от Java, нет закрытых ключевых слов. К классу нельзя обращаться напрямую, его следует ставить перед ним в виде символа подчеркивания.
Позвольте мне показать вам пример для лучшего понимания.
Пример:
классный сотрудник (объект): def __init __ (сам): self.name = 1234 self._age = 1234 self .__ salary = 1234 объект1 = сотрудник () печать (object1.name) печать (объект1._age) печать (объект1 .__ зарплата)
Вывод:
1234
Отслеживание (последний вызов последним):
1234
Файл «C: /Users/Harshit_Kant/PycharmProjects/test1/venv/encapsu.py», строка 10, в
print (object1.__salary)
AttributeError: Объект «employee» не имеет атрибута «__salary»
Explanation: Вы получите этот вопрос, что такое подчеркивание и ошибка? Ну, класс python обрабатывает частные переменные как (__ salary), к которым нельзя получить прямой доступ.
Итак, в моем следующем примере я использовал метод установки, который обеспечивает косвенный доступ к ним.
Пример:
класс сотрудник (): def __init __ (сам): self .__ maxearn = 1000000 def зарабатывать (самостоятельно): print ("заработок: {}".формат (self .__ maxearn)) def setmaxearn (self, заработать): // метод установки, используемый для доступа к закрытому классу self .__ maxearn = зарабатывать emp1 = сотрудник () emp1.earn () emp1 .__ maxearn = 10000 emp1.earn () emp1.setmaxearn (10000) emp1.earn ()
Выход:
доход: 1000000, доход: 1000000, доход: 10000
Пояснение: Использование метода установки обеспечивает косвенный доступ к методу частного класса . Здесь я определил класс сотрудника и использовал (__maxearn), который представляет собой метод установки, используемый здесь для хранения максимального заработка сотрудника, и функцию установки setmaxearn (), которая принимает цену в качестве параметра.
Это наглядный пример инкапсуляции, когда мы ограничиваем доступ к методу частного класса, а затем используем метод установки для предоставления доступа.
Далее в методологии объектно-ориентированного программирования Python рассказывает об одной из ключевых концепций, называемой абстракцией.
Абстракция:
Предположим, вы заказали билет в кино на букмекерской конторе с помощью сетевого банкинга или любого другого процесса. Вы не знаете, как создается пин-код или как выполняется проверка.С точки зрения программирования это называется «абстракцией». В основном это означает, что вы показываете только детали реализации конкретного процесса и скрываете детали от пользователя. Он используется для упрощения сложных проблем путем моделирования классов, соответствующих проблеме.
Невозможно создать экземпляр абстрактного класса, что просто означает, что вы не можете создавать объекты для этого типа класса. Его можно использовать только для наследования функциональных возможностей.
Пример:
из abc import ABC, abstractmethod класс сотрудника (ABC): def emp_id (self, id, name, age, salary): // Абстракция проходить класс childemployee1 (сотрудник): def emp_id (self, id): print ("emp_id - 12345") emp1 = childemployee1 () emp1.emp_id (идентификатор)
Выходные данные : emp_id - 12345
Объяснение: Как вы можете видеть в приведенном выше примере, мы импортировали абстрактный метод, а остальная часть программы имеет родительский и производный классы. Создается экземпляр объекта для базового класса «childemployee», и используются функциональные возможности абстрактного.
Является ли Python на 100% объектно-ориентированным?Python не имеет спецификаторов доступа, таких как «частный», как в java. Он поддерживает большинство терминов, связанных с «объектно-ориентированным» языком программирования, за исключением строгой инкапсуляции.Следовательно, он не полностью объектно-ориентирован.
На этом мы подошли к концу нашей статьи «Объектно-ориентированное программирование на Python». Надеюсь, вы ознакомились со всеми концепциями, связанными с классом Python, объектами и объектно-ориентированными концепциями в Python. Убедитесь, что вы как можно больше тренируетесь, и вернитесь к своему опыту.
Есть вопросы? Пожалуйста, укажите это в разделе комментариев этого блога «Объектно-ориентированное программирование на Python», и мы свяжемся с вами как можно скорее. Чтобы получить глубокие знания о Python и его различных приложениях, вы можете записаться прямо сейчас на наш курс обучения Python с круглосуточной поддержкой и пожизненным доступом.
Руководство для начинающих по объектно-ориентированному программированию (ООП) Python
Программирование - это искусство. Как и в искусстве, для создания лучших работ очень важен выбор подходящих кистей и красок. Объектно-ориентированное программирование на Python - один из таких навыков.
Выбор правильного языка программирования является важной частью любого проекта, и он может привести либо к плавной и приятной разработке, либо к полному кошмару.Следовательно, было бы лучше, если бы вы использовали язык, наиболее подходящий для вашего случая использования.
Это основная причина изучать объектно-ориентированное программирование на Python, который также является одним из самых популярных языков программирования.
Давайте учиться!
Пример программы на Python
Прежде чем углубиться в этот вопрос, позвольте задать вопрос: писали ли вы когда-нибудь программу на Python, подобную той, что приведена ниже?
secret_number = 20
в то время как True:
number = input ('Угадай число:')
пытаться:
число = int (число)
Кроме:
print ('Извините, это не число')
Продолжать
если число! = secret_number:
если число> secret_number:
print (число, 'больше секретного числа')
elif number
Этот код позволяет отгадывать простые числа.Попробуйте скопировать его в файл Python и запустить в своей системе. Он отлично выполняет свое предназначение.
Но здесь возникает огромная проблема: что, если бы мы попросили вас реализовать новую функцию? Это может быть что-то простое - например:
«Если введенное число кратно секретному числу, дайте пользователю подсказку».
Программа быстро станет сложной и тяжелой по мере увеличения количества функций и, следовательно, общего количества вложенных условных операторов.
Это именно та проблема, которую пытается решить объектно-ориентированное программирование.
Программирование - это искусство, требующее правильных инструментов для создания чего-то красивого 🎨 Узнайте больше об объектно-ориентированном программировании Python здесь 👇Нажмите, чтобы написать твитТребования для изучения Python OOP
Прежде чем приступить к объектно-ориентированному программированию, мы настоятельно рекомендуем вам хорошо усвоить основы Python.
Классификация тем, считающихся «базовыми», может быть трудной. По этой причине мы разработали шпаргалку со всеми основными концепциями, необходимыми для изучения объектно-ориентированного программирования на Python.
- Переменная: Символьное имя, указывающее на конкретный объект (мы увидим, что означает объектов в статье).
- Арифметические операторы: Сложение (+), вычитание (-), умножение (*), деление (/), целочисленное деление (//), по модулю (%).
- Встроенные типы данных: Числовые (целые числа, числа с плавающей запятой, комплексные), последовательности (строки, списки, кортежи), логические (True, False), словари и наборы.
- Логические выражения: Выражения, в которых результатом является Истина, или Ложь.
- Условный: Оценивает логическое выражение и выполняет некоторые операции в зависимости от результата. Обрабатывается операторами if / else .
- Цикл: Повторное выполнение кодовых блоков. Это может быть для или и петель.
- Функции: Блок организованного и многоразового кода.Вы создаете их с помощью ключевого слова def .
- Аргументы: Объекты, переданные функции. Например:
сумма ([1, 2, 4])
- Запуск сценария Python : Откройте терминал или командную строку и введите «python <имя файла>».
- Откройте оболочку Python : откройте терминал и введите
python
илиpython3
в зависимости от вашей системы.
Теперь, когда эти концепции кристально ясны, вы можете двигаться дальше в понимании объектно-ориентированного программирования.
Что такое объектно-ориентированное программирование в Python?
Объектно-ориентированное программирование (ООП) - это парадигма программирования, в которой мы можем рассматривать сложные проблемы как объекты.
Парадигма - это теория, которая дает основу для решения проблем.
Итак, когда мы говорим об ООП, мы имеем в виду набор концепций и шаблонов, которые мы используем для решения проблем с объектами.
Объект в Python - это единый набор данных (атрибутов) и поведения (методов).Вы можете думать об объектах как о реальных вещах вокруг вас. Например, рассмотрим калькуляторы:
Калькулятор может быть объектом.
Как вы могли заметить, данные (атрибуты) всегда являются существительными, а поведение (метод) - всегда глаголами.
Это разделение является центральной концепцией объектно-ориентированного программирования. Вы создаете объекты, которые хранят данные и содержат определенные виды функций.
Почему мы используем объектно-ориентированное программирование в Python?
ООП позволяет создавать безопасное и надежное программное обеспечение.Многие фреймворки и библиотеки Python используют эту парадигму для создания своей кодовой базы. Некоторые примеры: Django, Kivy, pandas, NumPy и TensorFlow.
Давайте посмотрим на основные преимущества использования ООП в Python.
Преимущества Python OOP
Следующие причины заставят вас выбрать объектно-ориентированное программирование на Python.
Все современные языки программирования используют ООП
Эта парадигма не зависит от языка. Если вы изучите ООП на Python, вы сможете использовать его в следующих случаях:
Все эти языки либо изначально объектно-ориентированы, либо включают опции для объектно-ориентированной функциональности.Если вы захотите изучить какой-либо из них после Python, будет проще - вы найдете много общего между языками, работающими с объектами.
ООП позволяет быстрее кодировать
Быстрее писать код не означает писать меньше строк кода. Это означает, что вы можете реализовать больше функций за меньшее время без ущерба для стабильности проекта.
Объектно-ориентированное программирование позволяет повторно использовать код путем реализации абстракции. Этот принцип делает ваш код более лаконичным и разборчивым.
Как вы знаете, программисты тратят гораздо больше времени на чтение кода, чем на его написание. По этой причине удобочитаемость всегда важнее, чем как можно быстрее выявлять особенности.
Снижается производительность при нечитаемом коде
Подробнее о принципе абстракции вы узнаете позже.
ООП помогает избежать спагетти Код
Вы помните программу для угадывания чисел в начале этой статьи?
Если вы продолжите добавлять функции, в будущем у вас будет много вложенных операторов , если .Этот клубок бесконечных строк кода называется спагетти-кодом, и вам следует избегать его как можно чаще.
ООП дает нам возможность сжимать всю логику в объектах, избегая, таким образом, длинных фрагментов вложенных if’s .
ООП улучшает анализ любой ситуации
Когда вы приобретете некоторый опыт работы с ООП, вы сможете думать о проблемах как о небольших и конкретных объектах.
Это понимание приводит к быстрой инициализации проекта.
Структурное программирование и объектно-ориентированное программирование
Структурированное программирование - наиболее часто используемая парадигма новичками, потому что это самый простой способ построить небольшую программу.
Он включает в себя последовательное выполнение программы Python. Это означает, что вы даете компьютеру список задач, а затем выполняете их сверху вниз.
Давайте посмотрим на пример структурированного программирования с программой кофейни.
малый = 2
обычный = 5
большой = 6
user_budget = input ('Каков ваш бюджет?')
пытаться:
user_budget = int (user_budget)
Кроме:
print ('Пожалуйста, введите число')
выход()
если user_budget> 0:
если user_budget> = big:
print ('Вы можете позволить себе большой кофе')
если user_budget == big:
print ('Готово')
еще:
print ('Ваша сдача', user_budget - большой)
elif user_budget == обычный:
print ('Вы можете позволить себе обычный кофе')
print ('Готово')
elif user_budget> = small:
print ('Вы можете купить маленький кофе')
если user_budget == small:
print ('Готово')
еще:
print ('Ваша сдача', user_budget - small)
Приведенный выше код действует как продавец в кофейне.Он попросит вас указать бюджет, а затем «продаст» вам самый большой кофе, который вы можете купить.
Попробуйте запустить в терминале. Он будет выполняться шаг за шагом, в зависимости от вашего ввода.
Этот код работает отлично, но у нас есть три проблемы:
- В нем много повторяющейся логики.
- Он использует много вложенных условных выражений , если .
- Будет сложно читать и изменять.
ООП было изобретено как решение всех этих проблем.
Давайте посмотрим, как описанная выше программа реализована с помощью ООП.Не волнуйтесь, если вы этого еще не поняли. Это только для сравнения структурного программирования и объектно-ориентированного программирования.
класс Кофе:
# Конструктор
def __init __ (я, имя, цена):
self.name = имя
self.price = float (цена)
def check_budget (self, budget):
# Проверяем, действителен ли бюджет
если не isinstance (budget, (int, float)):
print ('Введите число с плавающей запятой или целое число')
выход()
если бюджет <0:
print ('Извините, у вас нет денег')
выход()
def get_change (self, budget):
бюджет возврата - самовывоз.цена
def sell (self, budget):
self.check_budget (бюджет)
если бюджет> = self.price:
print (f'Вы можете купить кофе {self.name} ')
если бюджет == self.price:
print ('Готово')
еще:
print (f'Вот ваше изменение {self.get_change (budget)} $ ')
exit ("Спасибо за транзакцию")
Примечание: Все следующие концепции будут объяснены более подробно в этой статье.
Приведенный выше код представляет класс с именем «Кофе». У него есть два атрибута - «имя» и «цена», и оба они используются в методах. Основным методом является «продажа», который обрабатывает всю логику, необходимую для завершения процесса продажи.
Если вы попытаетесь запустить этот класс, вы не получите никаких результатов. В первую очередь это происходит потому, что мы просто объявляем «шаблон» для кофе, а не сам кофе.
Давайте реализуем этот класс с помощью следующего кода:
small = Кофе ('Маленький', 2)
регулярный = Кофе ('Обычный'; 5)
big = Кофе ('Большой', 6)
пытаться:
user_budget = float (input ('Каков ваш бюджет?'))
кроме ValueError:
exit ('Пожалуйста, введите число')
для кофе в [большой, обычный, маленький]:
кофе.продать (user_budget)
Здесь мы создаем экземпляров, или объекты кофе, класса «Кофе», а затем вызываем метод «продажи» для каждого кофе, пока пользователь не сможет позволить себе какой-либо вариант.
Мы получим тот же результат при использовании обоих подходов, но мы можем значительно расширить функциональность программы с помощью ООП.
Ниже приведена таблица сравнения объектно-ориентированного программирования и структурного программирования:
ООП | Структурированное программирование |
Легче в обслуживании | Сложно поддерживать |
Подход «Не повторяйся» (СУХОЙ) | Повторный код во многих местах |
Небольшие фрагменты кода, повторно используемые во многих местах | Большой объем кода в нескольких местах |
Объектный подход | Подход блочного кода |
Легче отлаживать | Сложнее отладить |
Большая кривая обучения | Более простая кривая обучения |
Используется в крупных проектах | Оптимизирован для простых программ |
В заключение сравнения парадигм:
- Ни одна из парадигм не идеальна (использование ООП в простых проектах затруднительно).
- Это всего лишь два способа решения проблемы; есть и другие.
- ООП используется в больших базах кода, а структурное программирование - в основном для простых проектов.
Перейдем к встроенным объектам в Python.
Все является объектом в Python
Открою вам секрет: вы все время использовали ООП, даже не замечая этого.
Даже при использовании других парадигм в Python вы все равно используете объекты почти для всего.
Это потому, что в Python все является объектом.
Запомните определение объекта: объект в Python - это единый набор данных (атрибутов) и поведения (методов).
Соответствует любому типу данных в Python.
Строка - это набор данных (символов) и поведения ( верхний () , нижний () и т. Д.). То же самое применимо к целым числам , с плавающей запятой, , логическим, , спискам, и словарям.
Прежде чем продолжить, давайте рассмотрим значение атрибутов и методов.
Атрибуты и методы
Атрибуты - это внутренние переменных, внутри объектов, а методы - это функций, , которые производят некоторое поведение.
Давайте выполним простое упражнение в оболочке Python. Вы можете открыть его, набрав в терминале python
или python3
.
Оболочка Python
Теперь давайте поработаем с оболочкой Python, чтобы обнаружить методы и типы.
>>> kinsta = 'Kinsta, Премиум хостинг WordPress'
>>> kinsta.upper ()
«КИНСТА, ПРЕМИУМ ВОРДПРЕСС ХОСТИНГ»
Во второй строке мы вызываем строковый метод upper () . Он возвращает содержимое строки в верхнем регистре. Однако это не меняет исходную переменную.
>>> кинста
'Kinsta, Премиальный хостинг WordPress'
Давайте углубимся в полезные функции при работе с объектами.
Функция type () позволяет получить тип объекта. «Тип» - это класс, к которому принадлежит объект.
>>> тип (кинста)
# класс 'str'
Функция dir () возвращает все атрибуты и методы объекта. Давайте проверим это с помощью переменной kinsta .
>>> реж (кинста)
['__add__', '__class__', ........... 'верхний', 'zfill']
Теперь попробуйте распечатать некоторые скрытые атрибуты этого объекта.
>>> kinsta .__ class__ # class ‘str’ e>
Это выведет класс, к которому принадлежит объект kinsta . Таким образом, мы можем сказать, что единственное, что возвращает функция типа , - это атрибут объекта __class__ .
Вы можете экспериментировать со всеми типами данных, обнаруживая все их атрибуты и методы прямо на терминале. Вы можете узнать больше о встроенных типах данных в официальной документации.
Ваш первый объект на Python
Класс похож на шаблон . Он позволяет создавать собственные объекты на основе определенных вами атрибутов и методов.
Подпишитесь на информационный бюллетень
Хотите узнать, как мы увеличили трафик более чем на 1000%?
Присоединяйтесь к 20 000+ другим пользователям, которые получают нашу еженедельную рассылку с инсайдерскими советами по WordPress!
Подпишитесь сейчасВы можете думать об этом как о для формочки для печенья , которую вы модифицируете, чтобы выпекать идеальные куки (объекты, а не куки для отслеживания) с определенными характеристиками: форма, размер и т. Д.
С другой стороны, у нас экземпляров. Экземпляр - это отдельный объект класса, который имеет уникальный адрес в памяти.
экземпляров в Python
Теперь, когда вы знаете, что такое классы и экземпляры, давайте определимся с ними!
Чтобы определить класс в Python, вы используете ключевое слово class , за которым следует его имя. В этом случае вы создадите класс с именем Cookie .
Примечание: В Python мы используем соглашение об именах верблюжьих имен для именования классов.
класс Cookie:
проходить
Откройте оболочку Python и введите приведенный выше код. Чтобы создать экземпляр класса, просто введите его имя и скобки после него. Это тот же процесс, что и вызов функции.
cookie1 = Cookie ()
Поздравляем - вы только что создали свой первый объект на Python! Вы можете проверить его идентификатор и ввести следующий код:
идентификатор (cookie1)
140130610977040 # Уникальный идентификатор объекта
тип (cookie1)
<класс '__main__.Cookie '>
Как видите, этот файл cookie имеет уникальный идентификатор в памяти, и его тип - Cookie .
Вы также можете проверить, является ли объект экземпляром класса с помощью функции isinstance () .
isinstance (cookie1, Cookie)
# Истинный
isinstance (cookie1, интервал)
# Ложь
isinstance ('строка', Cookie)
# Ложь
Метод конструктора
Метод __init __ () также называется «конструктор.«Он называется Python каждый раз, когда мы создаем экземпляр объекта.
Конструктор создает начальное состояние объекта с минимальным набором параметров, которые ему необходимы. Давайте изменим класс Cookie , чтобы он принимал параметры в своем конструкторе.
класс Cookie:
# Конструктор
def __init __ (я, имя, форма, фишки = 'Шоколад'):
# Атрибуты экземпляра
self.name = имя
self.shape = shape
self.chips = фишки
В классе Cookie каждый файл cookie должен иметь имя, форму и чип.Последнюю мы определили как «Шоколад».
С другой стороны, self относится к экземпляру класса (самому объекту).
Попробуйте вставить класс в оболочку и создать экземпляр cookie как обычно.
cookie2 = Cookie ()
# TypeError
Вы получите сообщение об ошибке. Это связано с тем, что вы должны предоставить минимальный набор данных, необходимых для существования объекта - в данном случае назовем и образуют , поскольку мы уже установили фишек на «Шоколад.”
cookie2 = Cookie ('Отличное печенье', 'Звезда')
Для доступа к атрибутам экземпляра необходимо использовать точечную нотацию.
cookie2.name
# 'Потрясающее печенье'
cookie2.shape
# 'Звезда'
cookie2.chips
# 'Шоколад'
На данный момент в классе Cookie нет ничего слишком пикантного. Давайте добавим образец метода bake () , чтобы сделать вещи более интересными.
класс Cookie:
# Конструктор
def __init __ (я, имя, форма, фишки = 'Шоколад'):
# Атрибуты экземпляра
себя.name = имя
self.shape = shape
self.chips = фишки
# Объект передает себя как параметр
def bake (self):
print (f'Это {self.name}, запекается с формой {self.shape} и фишками {self.chips} ')
print ('Наслаждайся печеньем!')
Для вызова метода используйте точечную нотацию и вызывайте его как функцию.
cookie3 = Cookie ('Запеченное печенье', 'Дерево')
cookie3.bake ()
# Это печенье выпекается в форме дерева и шоколадной крошки.
Наслаждайтесь печеньем!
Четыре столпа ООП в Python
Объектно-ориентированное программирование включает четыре основных направления:
1.Абстракция
Abstraction скрывает от пользователя внутреннюю функциональность приложения. Пользователь может быть либо конечным клиентом, либо другими разработчиками.
Мы можем найти абстракций в нашей повседневной жизни. Например, вы умеете пользоваться телефоном, но, вероятно, не знаете точно, что внутри него происходит каждый раз, когда вы открываете приложение.
Другой пример - сам Python. Вы знаете, как использовать его для создания функционального программного обеспечения, и вы можете делать это, даже если не понимаете внутреннего устройства Python.
Применение того же самого к коду позволяет собрать все объекты проблемы и абстрагировать стандартных функций в классы.
2. Наследование
Наследование позволяет нам определять несколько подклассов из уже определенного класса.
Основная цель - следовать принципу СУХОЙ. Вы сможете повторно использовать большой объем кода, реализовав все совместно используемые компоненты в суперклассах .
Вы можете думать об этом как о реальной концепции генетической наследственности .Потомки (подклассы) являются результатом наследования между двумя родителями (суперклассами). Они наследуют все физические характеристики (атрибуты) и некоторые общие модели поведения (методы).
3. Полиморфизм
Полиморфизмпозволяет нам немного изменить методы и атрибуты подклассов , ранее определенных в суперклассе .
Буквальное значение: « много форм ». Это потому, что мы создаем методы с одинаковыми именами, но с разными функциями.
Возвращаясь к предыдущей идее, дети также являются прекрасным примером полиморфизма. Они могут унаследовать определенное поведение get_hungry () , но немного по-другому, например, проголодаться каждые 4 часа, а не каждые 6.
4. Инкапсуляция
Инкапсуляция - это процесс, в котором мы защищаем внутреннюю целостность данных в классе.
Хотя в Python нет частного оператора , вы можете применить инкапсуляцию, используя искажение в Python.Существуют специальные методы с именами , геттеры и , сеттеры , которые позволяют нам получить доступ к уникальным атрибутам и методам.
Давайте представим класс Human , который имеет уникальный атрибут с именем _height . Вы можете изменять этот атрибут только в пределах определенных ограничений (почти невозможно быть выше 3 метров).
Калькулятор построения преобразователя формы площади
Одна из лучших особенностей Python заключается в том, что он позволяет нам создавать широкий спектр программного обеспечения, от программы CLI (интерфейса командной строки) до сложного веб-приложения.
Теперь, когда вы изучили основные концепции ООП, пора применить их к реальному проекту.
Примечание: Весь следующий код будет доступен в этом репозитории GitHub. Инструмент для редактирования кода, который помогает нам управлять версиями кода с помощью Git.
Ваша задача создать калькулятор площади следующих форм:
- Площадь
- Прямоугольник
- Треугольник
- Круг
- Шестиугольник
Форма Базовый класс
Сначала создайте файл калькулятора.py и откройте его. Поскольку у нас уже есть объекты, с которыми можно работать, будет легко абстрагировать их в классе.
Вы можете проанализировать общие характеристики и выяснить, что все это 2D-форм . Следовательно, лучший вариант - создать класс Shape с методом get_area () , от которого будет наследовать каждая форма.
Примечание: Все методы должны быть глаголами. Это потому, что этот метод называется get_area () , а не area () .
класс Форма:
def __init __ (сам):
проходить
def get_area (сам):
проходить
Приведенный выше код определяет класс; Однако пока в этом нет ничего интересного.
Давайте реализуем стандартные функции большинства этих фигур.
класс Форма:
def __init __ (self, side1, side2):
self.side1 = side1
self.side2 = side2
def get_area (сам):
вернуть self.side1 * self.сторона2
def __str __ (сам):
return f 'Область этого {self .__ class __.__ name__}: {self.get_area ()}'
Давайте разберемся, что мы делаем с этим кодом:
- В методе __init__ мы запрашиваем два параметра: side1 и side2 . Они останутся атрибутами экземпляра .
- Функция get_area () возвращает площадь фигуры. В данном случае используется формула площади прямоугольника, так как ее будет легче реализовать с другими формами.
- Метод __str __ () является «волшебным методом», точно так же, как __init __ (). Он позволяет вам изменить способ печати экземпляра.
- Скрытый атрибут self .__ class __.__ name__ относится к имени класса. Если бы вы работали с классом Triangle , этот атрибут был бы «Triangle».
Класс прямоугольника
Поскольку мы реализовали формулу площади прямоугольника, мы могли создать простой класс Rectangle , который ничего не делает, кроме как наследовать от класса Shape .
Чтобы применить наследование в Python, вы создадите класс как обычно и заключите суперкласс , от которого вы хотите наследовать, круглыми скобками.
# Сложенный базовый класс
класс Форма: ...
class Rectangle (Shape): # Суперкласс в скобках
проходить
Квадратный класс
Мы можем использовать отличный подход к полиморфизму с классом Square .
Помните, что квадрат - это просто прямоугольник, у которого все четыре стороны равны.Это означает, что мы можем использовать ту же формулу, чтобы получить площадь.
Мы можем сделать это, изменив метод init , приняв в качестве параметра только сторону и передав значение этой стороны конструктору класса Rectangle .
# Сложенные классы
класс Форма: ...
класс Прямоугольник (Форма): ...
класс Квадрат (Прямоугольник):
def __init __ (я, сторона):
super () .__ init __ (сторона, сторона)
Как вы можете видеть, суперфункция дважды передает параметр со стороны суперклассу .Другими словами, он передает сторона как сторона1 и сторона2 в ранее определенный конструктор.
Класс треугольника
Треугольник в два раза меньше окружающего его прямоугольника.
Связь между треугольниками и прямоугольниками (Источник изображения: репетиторы Университета).
Таким образом, мы можем унаследовать от класса Rectangle и изменить метод get_area , чтобы он соответствовал формуле площади треугольника, которая составляет половину основания, умноженного на высоту.
# Сложенные классы
класс Форма: ...
класс Прямоугольник (Форма): ...
класс Квадрат (Прямоугольник): ...
класс Треугольник (Прямоугольник):
def __init __ (self, base, height):
super () .__ init __ (основание, высота)
def get_area (сам):
площадь = супер (). get_area ()
зона возврата / 2
Другой вариант использования функции super () - вызвать метод, определенный в суперклассе , и сохранить результат как переменную.Вот что происходит внутри метода get_area () .
Класс Круг
Площадь круга можно найти по формуле πr² , где r - радиус круга. Это означает, что мы должны изменить метод get_area () для реализации этой формулы.
Примечание: Мы можем импортировать приблизительное значение π из математического модуля
# Сложенные классы
класс Форма: ...
класс Прямоугольник (Форма):...
класс Квадрат (Прямоугольник): ...
class Triangle (Прямоугольник):…
# В начале файла
из математического импорта пи
класс Круг (Форма):
def __init __ (self, radius):
self.radius = радиус
def get_area (сам):
return pi * (собственный радиус ** 2)
Приведенный выше код определяет класс Circle , который использует другой конструктор и методы get_area () .
Хотя Circle унаследован от класса Shape , вы можете переопределить каждый метод и назначить его по своему вкусу.
Обычный шестигранник класса
Нам нужна только длина стороны правильного шестиугольника, чтобы вычислить его площадь. Он похож на класс Square , где мы передаем конструктору только аргумент.
Формула площади шестиугольника (Источник изображения: BYJU’S)
Однако формула совершенно другая, и она подразумевает использование квадратного корня. Вот почему вы будете использовать функцию sqrt () из математического модуля.
# Сложенные классы
класс Shape:...
класс Прямоугольник (Форма): ...
класс Квадрат (Прямоугольник): ...
class Triangle (Прямоугольник):…
class Circle (Shape):…
# Импортировать квадратный корень
из математического импорта sqrt
класс Шестиугольник (Прямоугольник):
def get_area (сам):
возврат (3 * sqrt (3) * self.side1 ** 2) / 2
Тестирование наших классов
Вы можете войти в интерактивный режим при запуске файла Python с помощью отладчика. Самый простой способ сделать это - использовать встроенную функцию точки останова.
Примечание: Эта функция доступна только в Python 3.7 или новее.
из math import pi, sqrt
# Сложенные классы
класс Форма: ...
класс Прямоугольник (Форма): ...
класс Квадрат (Прямоугольник): ...
class Triangle (Прямоугольник):…
class Circle (Shape):…
class Hexagon (Прямоугольник):…
точка останова ()
Теперь запустите файл Python и поэкспериментируйте с созданными вами классами.
$ Калькулятор на питоне.ру
(Pdb) rec = Прямоугольник (1, 2) (Pdb) print (rec)
Площадь этого прямоугольника: 2
(Pdb) sqr = Квадрат (4)
(PDB) печать (sqr)
Площадь этой площади: 16
(Pdb) tri = Треугольник (2, 3)
(PDB) печать (три)
Площадь этого Треугольника: 3,0.
(Pdb) cir = Круг (4)
(PDB) печать (cir)
Площадь этого круга: 50.26548245743669.
(Pdb) hex = шестиугольник (3)
(PDB) печать (шестнадцатеричный)
Площадь этого шестиугольника: 23.3826859844
Вызов
Создайте класс с помощью метода run , где пользователь может выбрать форму и вычислить ее площадь.
Когда вы выполнили задание, вы можете отправить запрос на перенос в репозиторий GitHub или опубликовать свое решение в разделе комментариев.
Готовы начать изучение объектно-ориентированного программирования на Python? ✅ Вы попали в нужное место 😄Нажмите, чтобы твитнутьСводка
Объектно-ориентированное программирование - это парадигма, в которой мы решаем проблемы, думая о них как о объектах . Если вы понимаете Python OOP, вы также можете легко применить его на таких языках, как Java, PHP, Javascript и C #.
Из этой статьи вы узнали о:
- Концепция объектно-ориентированного программирования в Python
- Преимущества объектно-ориентированного программирования перед структурным
- Основы объектно-ориентированного программирования на Python
- Концепция классов и их использование в Python
- Конструктор класса в Python
- Методы и атрибуты в Python
- Четыре столпа ООП
- Реализация абстракции , , наследования , полиморфизма и в проекте
Теперь дело за вами!
Дайте нам знать ваше решение проблемы ниже в комментариях! И не забудьте ознакомиться с нашим руководством по сравнению Python и PHP.
Если вам понравилась эта статья, то вам понравится хостинговая платформа Kinsta WordPress. Ускорьте свой сайт и получите круглосуточную поддержку от нашей опытной команды WordPress. Наша инфраструктура на базе Google Cloud ориентирована на масштабируемость, производительность и безопасность. Позвольте нам показать вам разницу в Kinsta! Ознакомьтесь с нашими тарифами
классов Python
Классы / объекты Python
Python - это объектно-ориентированный язык программирования.
Почти все в Python представляет собой объект со своими свойствами и методами.
Класс похож на конструктор объекта или «план» для создания объектов.
Создать класс
Чтобы создать класс, используйте ключевое слово class
:
Пример
Создайте класс MyClass со свойством x:
класс MyClass:
x = 5
Создать объект
Теперь мы можем использовать класс MyClass для создания объектов:
Пример
Создайте объект с именем p1 и распечатайте значение x:
p1 = MyClass ()
печать (p1.х)
Функция __init __ ()
Приведенные выше примеры представляют собой классы и объекты в их простейшей форме и являются не очень полезен в реальных приложениях.
Чтобы понять значение классов, мы должны понимать встроенный __init __ () функция.
Все классы имеют функцию __init __ (), которая всегда выполняется, когда занятие инициируется.
Используйте функцию __init __ () для присвоения значений свойствам объекта или другим операции, которые необходимо выполнить, когда объект создается:
Пример
Создайте класс с именем Person, используйте функцию __init __ () для присвоения значений на имя и возраст:
class Person:def __init __ (я, имя, возраст):
себя.name = name
self.age = age
p1 = Person ("Джон", 36)
print (p1.name)
print (p1.age)
Примечание: Функция __init __ ()
вызывается автоматически каждый раз, когда класс используется для создания нового объекта.
Методы объекта
Объекты также могут содержать методы. Методы в объектах - это функции, которые принадлежат объекту.
Создадим метод в классе Person:
Пример
Вставьте функцию, которая печатает приветствие, и выполните ее для объекта p1:
class Person:def __init __ (я, имя, возраст):
себя.name = name
self.age = age
def myfunc (self):
print («Привет, меня зовут» + self.name)
p1 = Person («Джон»,
36)
p1.myfunc ()
Примечание: Параметр self
является ссылкой на текущий экземпляр класса и используется для доступа к переменным, принадлежащим этому классу.
Самостоятельный параметр
Параметр self
является ссылкой на
текущий экземпляр класса и используется для доступа к переменным, принадлежащим этому классу.
Необязательно называть self
, вы можете
назовите его как хотите, но он должен быть первым параметром любой функции
в классе:
Пример
Используйте слова mysillyobject и abc вместо self :
class Person:def __init __ (mysillyobject, name, age):
mysillyobject.