Статьи

Python с нуля: объектно-ориентированное программирование

Добро пожаловать на четвертый урок из серии « Питон с нуля ». В этом руководстве предполагается наличие некоторых предварительных знаний о переменных, типах данных, функциях и выводе на печать. Если вы не в курсе, посмотрите предыдущие три статьи в серии, чтобы наверстать упущенное.

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




Python в первую очередь разработан как объектно-ориентированный язык программирования — но что на самом деле означает «объектно-ориентированный»?

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

В широком смысле, объектно-ориентированное программирование — это концепция, согласно которой в программировании объекты, которыми мы манипулируем, важнее, чем логика, необходимая для управления этими объектами. Традиционно программа рассматривалась как рецепт — набор инструкций, которым вы следуете от начала до конца, чтобы выполнить задачу. Это все еще может быть правдой, и для многих простых программ это все, что требуется. Этот подход иногда называют процедурным программированием.

ООП ставит объекты в центр процесса.

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

Когда мы говорим об объектно-ориентированных подходах, мы делаем объекты в центре процесса, а не просто используем их в качестве необходимых контейнеров для информации как части наших процедурных инструкций. Сначала мы определяем объекты, которыми мы хотим манипулировать, и как они связаны друг с другом, а затем мы начинаем дополнять их логикой, чтобы программа действительно работала.

Когда я говорю об «объектах», я могу говорить о самых разных вещах. «Объект» может представлять человека (как определено свойствами, такими как имя, возраст, адрес и т. Д.), Или компанию (как определено такими вещами, как количество сотрудников и т. Д.), Или даже нечто гораздо более абстрактное, например кнопка в интерфейсе компьютера.

В этом введении мы не собираемся охватывать все концепции в этой теме, потому что мы будем здесь всю ночь, но к концу урока я надеюсь, что у вас будет четкое понимание принципов, которые вам нужны начать сразу, используя несколько простых объектно-ориентированных методов в ваших программах на Python. Более того, эти концепции довольно похожи во многих средах программирования. Знания очень хорошо переносятся с языка на язык.


Ранее я упоминал, что первое, что мы должны сделать, когда будем использовать подход ООП, — это определить объекты, которые мы будем использовать. То, как мы это делаем, — это сначала определить свойства, которыми он обладает, используя класс. Вы можете думать о классе как о шаблоне; руководство о том, как объект должен быть структурирован. Каждый объект принадлежит классу и наследует свойства этого класса, но действует индивидуально для других объектов этого класса.

Объект иногда называют «экземпляром» класса.

В качестве простого примера у вас может быть класс с именем person, скажем, со свойством age и name, и экземпляром этого класса (объекта) будет один человек. Этот человек может иметь имя «Энди» и ему 23 года, но вы можете одновременно иметь другого человека, принадлежащего к тому же классу, с именем «Люси» и 18 лет.

Трудно понять это, не видя этого на практике, поэтому давайте приступим к реальному коду.

Чтобы определить класс типичным простым языком Python, мы используем слово «класс», за которым следует имя вашего нового класса. Я собираюсь сделать новый класс здесь, под названием «домашнее животное». Мы используем двоеточие после имени, а затем все, что содержится в определении класса, имеет отступ. Тем не менее, с классом, нет круглых скобок:

Итак, теперь у нас есть класс, но он довольно бесполезен без чего-либо в нем. Для начала давайте дадим ему пару свойств. Чтобы сделать это, вы просто определяете некоторые переменные внутри класса — я начну с количества ног для начала. Как обычно, вы всегда должны называть свои переменные, чтобы было легко понять, что они из себя представляют. Давайте будем оригинальны и назовем его «number_of_legs». Нам нужно определить значение, иначе мы получим ошибку. Я буду использовать 0 здесь (это не имеет большого значения в этом случае, так как количество ног будет индивидуальным для каждого экземпляра класса — у рыбы нет такого же количества ног, как у собаки или утки, и т. д. — так или иначе, нам придется изменить это значение для каждого объекта).

Сам по себе класс — это не то, чем вы можете напрямую манипулировать; Во-первых, нам нужно создать экземпляр класса для игры. Мы можем сохранить этот экземпляр в переменной. Вне класса (без отступов) давайте создадим экземпляр класса и сохраним его в переменной «doug». Чтобы создать новый экземпляр класса, вы просто вводите имя класса, а затем пару скобок. На этом этапе вам не нужно беспокоиться о скобках, но позже вы увидите, что они есть, потому что, как и функция, есть способ передачи переменной для использования классом при первом создании экземпляра. ,

Сам по себе класс — это не то, чем вы можете напрямую манипулировать.

Теперь, когда у нас есть экземпляр класса, как мы можем получить доступ к его свойствам и управлять ими? Чтобы сослаться на свойство объекта, сначала мы должны сказать Python, о каком объекте (или о каком экземпляре класса) мы говорим, поэтому мы начнем с ‘doug’. Затем мы напишем точку, чтобы указать, что мы ссылаемся на то, что содержится в нашем экземпляре doug. После периода мы добавляем имя нашей переменной. Если мы обращаемся к переменной number_of_legs , она будет выглядеть так:

Теперь мы можем трактовать это точно так же, как и любую другую переменную — здесь я собираюсь предположить, что Даг — собака, и даст этой переменной значение 4.

Чтобы получить доступ к этой переменной, мы собираемся использовать ее снова точно так же, как мы будем обращаться с любой другой переменной, но используя это свойство doug.number_of_legs вместо обычного имени переменной. Давайте вставим строку, чтобы распечатать, сколько ног у Дуга, чтобы мы могли показать, что он работает так, как должен:

Если вы запустите приведенный выше код, вы увидите, что он распечатан для нас. Он определил наш класс ‘pet’, создал новый экземпляр этого класса и сохранил его в переменной ‘doug’, а затем внутри этого экземпляра ему присвоили значение 4 переменной number_of_legs которую он унаследовал от своего класса.

Таким образом, вы можете увидеть из этого очень упрощенного примера, как вы можете начать создавать красивые, модульные структуры данных, которые понятны и просты в использовании, и могут начать довольно хорошо масштабироваться.


Итак, это основа классов и объектов, но на данный момент мы можем реально использовать классы только как структуры данных или контейнеры для переменных. Это все хорошо, но если мы хотим начать выполнять более сложные задачи с данными, которыми мы манипулируем, нам нужен способ ввести некоторую логику в эти объекты. Мы делаем это с помощью методов.

Методы, по сути, являются функциями, содержащимися в классе. Вы определяете его точно так же, как и функцию, но разница в том, что вы помещаете его в класс, и он принадлежит этому классу. Если вы когда-нибудь захотите вызвать этот метод, вы должны сначала обратиться к объекту этого класса, как переменные, которые мы рассматривали ранее.

Методы, по сути, являются функциями, содержащимися в классе.

Я собираюсь написать быстрый пример здесь, в нашем классе домашних животных, чтобы продемонстрировать; давайте создадим метод с именем ‘sleep’, который будет печатать сообщение при первом его вызове. Как функция, я собираюсь поставить def для define, а затем напишу имя метода, который я хочу создать. Затем мы собираемся поставить наши скобки и точку с запятой, а затем начать новую строку. Как обычно, все, что включено в этот метод, будет иметь дополнительный уровень.

Теперь есть еще одно различие между методом и функцией: у метода всегда, всегда, всегда должен быть аргумент, называемый «я» в скобках. Когда Python вызывает метод, он передает текущий объект этому методу в качестве первого аргумента. Другими словами, когда мы вызываем doug.sleep() , Python фактически передает объект ‘doug’ в качестве аргумента методу sleep.

Мы увидим, почему это происходит позже, но сейчас вам нужно знать, что с помощью метода вы всегда должны сначала включать в список аргумент «self» (если вы хотите добавить больше аргументов, вы можете добавить их). впоследствии, точно так же, как если бы вы передавали несколько аргументов в функцию). Если вы не включите этот аргумент, при запуске кода вы получите ошибку, потому что Python передает аргумент (этот объект ‘self’), а метод говорит: «Эй, чувак, Я не беру никаких аргументов, о чем ты говоришь? Это так же, как если бы вы пытались передать аргумент в функцию, которая не принимает никаких аргументов.

Итак, вот что мы имеем:

Внутри этого метода мы напишем оператор print следующим образом:

Теперь, если мы хотим использовать этот метод, мы просто используем экземпляр класса pet для ссылки на него. Как и переменная number_of_legs , мы записываем имя экземпляра (у нас есть один с именем doug), затем точку, затем имя метода, включая скобки. Обратите внимание, что мы вызываем sleep без аргументов, но Python собирается добавить этот аргумент сам по себе, поэтому в итоге мы получим правильное количество аргументов.

Если вы запустите этот код, вы увидите, что он печатает написанное нами сообщение.

Отлично, теперь, как насчет того, чтобы написать новый метод, чтобы распечатать, сколько ног у питомца, чтобы продемонстрировать, как вы можете использовать методы, чтобы начать манипулировать данными в классе, и продемонстрировать, почему нам нужно включить это запутанное «я» аргумент. Давайте count_legs новый метод, называемый count_legs .

Вот тут и вступает аргумент «self». Помните, когда мы number_of_legs к number_of_legs извне класса, и нам пришлось использовать «doug.number_of_legs» вместо просто «number_of_legs»? Применяется тот же принцип; если мы хотим знать, что содержится в этой переменной, мы должны сослаться на нее, сначала указав экземпляр, содержащий эту переменную.

Однако мы не знаем, как будет вызываться экземпляр, когда будем писать класс, поэтому мы обходим это, используя переменную self. «Я» — это просто ссылка на объект, которым в данный момент манипулируют. Таким образом, чтобы получить доступ к переменной в текущем классе, вам просто нужно предвосхитить ее «self», а затем точку, например:

На практике это означает, что везде, где вы пишете «self» в своем методе, когда вы запускаете метод, «self» заменяется именем объекта, поэтому, когда мы вызываем «doug.count_legs ()», «self» заменено на «Дуг». Чтобы продемонстрировать, как это работает с несколькими экземплярами, давайте добавим второй экземпляр, представляющий другого питомца, называемого ‘nemo’:

Это выведет сообщение для 4, а затем 0 ног, как мы и хотели, потому что когда мы вызываем ‘nemo.count_legs (),’ self ‘заменяется на’ nemo ‘вместо’ doug ‘.

Таким образом, наш метод будет работать точно так, как задумано, потому что ссылка на себя будет динамически изменяться в зависимости от контекста и позволит нам манипулировать данными только внутри текущего объекта.

Главное, что вы должны помнить о методах, это то, что они в точности похожи на функции, за исключением того, что первый аргумент должен быть ‘self’ и что для ссылки на внутреннюю переменную вы должны предварять имя переменной ‘self’.

Как примечание: вы можете использовать любое имя вместо «я» для своих методов. — Методы здесь будут работать так же хорошо, если мы переименуем переменную «self» в любое слово. Использование имени «self» — это просто соглашение, которое полезно для программистов на Python, потому что оно делает код намного более стандартным и простым для понимания, даже если он написан кем-то другим. Мой совет будет придерживаться конвенций.


Теперь, когда мы ознакомились с основами, давайте рассмотрим некоторые более сложные функции классов и то, как они могут помочь упростить структурирование вашего программирования.

Следующая вещь, о которой мы поговорим, это наследование. Как может указывать его имя, наследование — это процесс создания нового класса, основанного на родительском классе, и позволяющий новому классу наследовать функции родительского класса. Новый класс может принимать все методы и переменные из родительского класса (часто называемого «базовым» классом).

Наследование — это процесс создания нового класса, основанного на родительском классе.

Давайте расширим наш пример с питомцами, чтобы увидеть, как это может быть полезно. Если мы используем «домашнее животное» в качестве родительского класса, мы можем создать дочерний класс, который унаследован от домашнего животного. Дочерний класс может быть чем-то вроде «собака» или «рыба» — то, что все еще является «домашним животным», но является более конкретным, чем это. Собака является домашним животным и делает то же самое, что и все домашние животные — например, она ест и спит, имеет возраст и количество ног — но она делает другие вещи, которые являются специфическими для собаки, или, по крайней мере, более конкретными чем быть домашним животным: у собак есть мех, но не у всех домашних животных есть. Собака может лаять или приносить палку, но не все домашние животные.

Возвращаясь к делу, скажем, мы хотели создать в нашей программе класс для представления собаки. Мы могли бы использовать наследование для наследования методов и переменных, содержащихся в «питомцах», чтобы у нашей собаки было «количество ножек» и способность «спать», в дополнение ко всем специфическим для собаки вещам, которые мы могли бы хранить или делать.

Теперь вам может быть интересно, почему мы не помещаем эти методы и переменные в класс собаки и не избавляемся от класса питомца полностью? Ну, наследование дает нам два явных преимущества перед этим подходом: во-первых, если нам нужен объект, который является домашним животным, но не собакой — обычным домашним животным, если хотите, — мы все равно можем это сделать. Два, возможно, позже мы хотим добавить второго типа домашних животных — может быть, рыба. Мы можем сделать так, чтобы второй класс также наследовал от pet, и поэтому оба класса могут совместно использовать все в pet, но в то же время имеют свои более конкретные методы и переменные, которые применяются только к этому типу объектов.

Здесь мы немного увязли в теории, поэтому давайте напишем кое-что, чтобы сделать ее немного более понятной. Во-первых, мы собираемся написать новый класс, который называется «собака», но на этот раз между именем класса и двоеточием мы будем ставить несколько скобок, а в них мы будем писать имя класса, от которого мы хотим наследовать, вроде как мы передаем этому новому классу аргумент, как если бы мы были функцией.

Далее, давайте дадим этому классу простой метод, чтобы продемонстрировать, как он работает. Я собираюсь добавить метод « bark », который будет печатать «гав»:

Итак, теперь давайте посмотрим, что произойдет, если мы сделаем экземпляр этого класса. Я снова буду называть нашу новую собаку «Даг». Теперь, если мы вызовем doug.bark() :

Как и ожидалось, Даг лает. Это здорово, но мы еще не видели ничего нового — просто класс с методом. Однако наследование сделало для нас все функции и переменные pet доступными для нас через наш объект ‘doug’, поэтому, если я сделаю что-то вроде этого:

Тогда метод сна также будет выполняться правильно. По сути, наш объект doug принадлежит как к классу ‘pet’, так и к классу ‘dog’. Чтобы убедиться, что переменные работают так же, как методы, давайте попробуем это:

Вы можете видеть, что Даг действует точно так же, как и раньше, показывая, что наши переменные наследуются. Наш новый дочерний класс является просто специализированной версией родительского класса, с некоторыми дополнительными функциями, но сохраняющими все предыдущие функции.


Итак, у вас есть краткое введение в объектно-ориентированное программирование. Следите за обновлениями следующей части этой серии, где мы будем работать с Python в Интернете!