Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Python. Учебное пособие.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆

Функциональное программирование

Функциональное программирование – еще одна популярная парадигма программирования. Оно включает в себя написание функций, которые при од­них и тех же входных данных всегда возвращают одни и те же выходные дан­ные. В функциональном программировании программируют только с помощью функций, не используя классы – особенность объектно-ориентированного про­граммирования. В функциональном программировании много жаргона, и про­граммист Мэри Роуз Кук дает ему следующее определение: “Функциональный код характеризуется одним свойством: отсутствием побочных эффектов. Он не полагается на данные вне текущей функции и не изменяет данные, существую­щие вне текущей функции”. Она подкрепляет свое определение примером функции, имеющей побочные эффекты:
a = 0
def increment(): global a
a += 1
И примером функции, не имеющей побочных эффектов.
def increment(a): return a + 1
Первая функция имеет побочные эффекты, поскольку она полагается на данные за своими пределами и изменяет данные вне текущей функции путем увеличения глобальной переменной. Вторая функция не имеет побочных эф­фектов, потому что она не полагается ни на какие данные за своими пределами, и не изменяет эти данные.
В функциональном программировании программисты пишут функции таким образом, чтобы устранить побочные эффекты – непреднамеренные последствия, которые возникают, когда постоянно меняют состояние своей программы.

Объектно-ориентированное программирование

Парадигма объектно-ориентированного программирования (ОПП) вклю- чает в себя написание программ, в которых определяются и создаются объекты, взаимодействующие друг с другом. До сих пор вы оперировали такими объек­тами, как строки, целые числа и числа с плавающей запятой. Но также можно
81
определить свои собственные объекты с помощью классов. Классы – это схе­ма, используемая для создания объектов. Можно думать о классе как об идее объекта, например об апельсине. Апельсин – это объект. Плод весом от 200 до 300 грамм – это идея апельсина, то есть соответствующего класса.
Можно смоделировать апельсины в Python, определив класс, который за­тем использовать для создания объектов orange, определить класс, используя ключевое слово class, за которым следует имя, которое присваивается классу. Класс – это составная инструкция с заголовком и телом класса. Тело пишется после заголовка. Телом могут быть как простые, так и составные операторы, называемые методами. Методы похожи на функции, но они определены внутри класса и могут быть вызваны только в объекте, созданном классом. В главе 3 приводились примеры, которые вызывали различные методы для строк вроде "Hello".upper(). Вот пример того, как можно представить апельсин с по­мощью класса в Python:
class Orange: print("Orange created!")
За ключевым словом class следует имя класса – в данном случае Orange, потому что моделируются апельсины. По соглашению классы
в Python всегда начинаются с заглавной буквы и пишутся в стиле CamelCase (с англ. – «ВерблюжийРегистр», также «ГорбатыйРегистр») – это означает, что
если имя класса состоит более чем из одного слова, слова не должны разделять­ся символом подчеркивания (как имя функции), вместо этого каждое слово должно быть написано с заглавной буквы, как это – LikeThis.
После определения класса в теле класса присутствует простая инструк­ция
– print("Orange created!"). Этот код будет выполняться, когда бу-
дет создан объект orange. С помощью этого определения класса можно со­здать сколько угодно объектов Orange:
orange = Orange() print(type (orange))
print(orange) >> Orange created! >> <class '__main__.Orange'> >> <__main__.Orange object at 0x101a787b8>
82
При создании нового объекта Orange используется тот же синтаксис для вызова функции – [classname](). Это называется созданием экземпляра объ­екта, что означает создание нового объекта. "Orange created!" печата­ется, как только создался экземпляр объекта Orange. При печати слова type(orange)функция type сообщает, что объект Orange является экзем­пляром класса Orange, который только что был создан. При печати объекта Orange Python сообщает, что это объект Orange, а затем указывает его ме- стоположение в памяти. Когда печатается такой объект, расположение в памя­ти, напечатанное на компьютере, не будет таким же, как в примере, поскольку местоположение объекта в памяти меняется при каждом запуске программы.
Заметим, что термины «объект» и «экземпляр» взаимозаменяемы.
Добавим метод в класс Orange. Методы определяются таким же синтак­сисом, что и функции. Есть только два отличия: метод должен быть определен как тело в классе и метод должен принимать, по крайней мере, один параметр (кроме особых случаев). Можно произвольно назвать первый параметр метода, но по соглашению, которое никогда не нарушалось, в методе он всегда называ­ется self.
Причина, по которой каждый метод должен иметь первый параметр, за­ключается в том, что всякий раз, когда метод вызывается для объекта, Python автоматически передает методу объект, который его вызвал. Эта концепция существует в большинстве языков программирования, поддерживающих ООП, однако Python делает передачу self в качестве параметра явной, в то время как многие другие языки делают ее неявной. Python заставляет явно определять self в каждом создаваемом методе, тогда как в других языках self просто подразумевается, что он был передан объекту. Если определили метод и напе­чатали self, то видно, что это объект Orange, для которого вызывали метод:
class Orange: print("Orange created!") def print_orange(self): print(self)
Orange().print_orange() >> Orange created! >> <__main__.Orange object at 0x101a787b8>
83
Параметр self полезен для определения и доступа к переменным, при­надлежащим объекту Orange. Делается это для определения специального ме­тода под названием __init__, который означает инициализацию. Когда со­здается экземпляр объекта, если определили метод с именем __init__, Python автоматически вызывает метод __init__ при создании объекта. Внутри
__init__ можно указать переменные объекта Orange с синтаксисом self.[variable_name] = [variable_value].
Пример:
class Orange: print("Orange created!") def __init__ (self): self.color = "orange" self.weight = 300 def print_orange(self): print(self) print(self.color) print(self.weight) orange = Orange()
orange.print_orange() >> Orange created! >> <__main__.Orange object at 0x10564dba8> >> orange >> 300
Используя __init__, теперь можно создавать апельсины, которые полу-
чают цвет и вес при инициализации, и использовать и изменять эти переменные в любом из методов, как и обычные переменные. В Python любой метод, окру­женный с обеих сторон двойными нижними подчеркивания, называется маги­ческим методом (это означает, что он делает что-то особенное). Метод print_orange был использован для иллюстрации примера, он не будет мето-
дом в нашем orange, поскольку продолжаем его моделировать.
Можно изменить определение нашего класса, чтобы программист, созда-
ющий объект (создание нового объекта называется созданием экземпляра класса), мог передавать свои собственные переменные при создании нового
84
объекта orange вместо веса и цвета, которые были по умолчанию. Вот новое определение класса:
class Orange: def __init__ (self, weight, color, mold):
self.weight = weight self.color = color
Теперь можно создавать более разнообразные объекты orange:
orange = Orange(300, 'orange')
Таким образом был создан 300-граммовый объект orange оранжевого цвета. Теперь можно получить доступ к переменным объекта orange, исполь­зуя точечную нотацию:
print(orange.weight)
>> 300
print(orange.color)
>> "orange"
Также можно изменить любой из параметров объекта orange:
orange.weight = 400 print(orange.weight)
>> 400
Моделируя апельсин, отметим, что апельсин – это нечто большее, чем про­сто его физические свойства, такие как цвет и вес. С апельсином тоже может что-то происходить; нужно уметь моделировать и это. Что может произойти с апельсином? Ну, во-первых, апельсин может гнить. Можно смоделировать процесс гниения апельсина, добавив соответствующую переменную mold
в класс Orange и создав метод, который увеличивает переменную плесени при ее вызове:
class Orange(): def __init__ (self):
self.weight = 200 self.color = 'orange' self.mold = 0
85
def rot(self, days, temperature): self.mold = days * (temperature * .1) orange = Orange() print(orange.mold) orange.rot(10, 37)
print(orange.mold) >> 0 >> 37.0
Определили метод, который принимает в качестве параметров количество
дней, прошедших с момента сбора апельсина, и среднюю температуру за это вре­мя. Из этой формулы можно увеличивать количество плесени в апельсине каж­дый раз, когда вызывается метод гниения rot (увеличение плесени отражает пе-
ременная экземпляра mold); и теперь апельсин обладает свойством гнить.
В ООП используются классы для моделирования объектов. Эти объекты объединяют переменные (состояние) и методы (для изменения состояния) в единое целое – объект. У классов есть специальный метод __init__, ко- торый Python вызывает, когда объект создается с использованием синтак­сиса [имя_класса](). Это пример одного из магических методов Python.
Первый аргумент любого метода по соглашению называется self и сущест­вует потому, что Python автоматически передает объект, вызвавший метод, в метод.
Особо подчеркнем, что в классе можно определить несколько методов. Это продемонстрировано на примере построения геометрической фигуры – прямоугольника с помощью двух методов: 1) расчета площади и 2) изменения размера.
class Rectangle(): def __init__ (self, w, l):
self.width = w self.len = l def area(self): return self.width * self.len def change_size(self, w, l): self.width = w self.len = l
86
rectangle = Rectangle(10, 20) print(rectangle.area()) rectangle.change_size(20, 40) print(rectangle.area())
>> 200 >> 800
Объекты Rectangle имеют две переменные экземпляра: len и width.
Метод area возвращает площадь объекта Rectangle, а метод change_size изменяет переменные, присваивая им числа, которые передаются в качестве па­раметров.
У ООП имеются как преимущества, так и недостатки. Преимущества.
• Парадигма ООП способствует повторному использованию кода и вслед-
ствие этого сокращает количество времени, необходимое на разработку и об­служивание кода.
• Проблемы разбиваются на множество фрагментов, благодаря чему код
становится легче поддерживать.
Недостатки.
• Создание программ требует больших усилий, поскольку их разработка
включает огромный объем планирования.

10. Базовые понятия ООП

В ООП есть четыре основных понятия, которые часто называют его че­тырьмя столпами: абстракция, инкапсуляция, наследование и полиморфизм. Все они должны присутствовать в языке программирования, чтобы его можно было считать объектно-ориентированным. Python является объектно­ориентированным языком. Не все языки программирования поддерживают ООП, например, Haskell – это функциональный язык программирования, кото­рый не поддерживает ООП. Рассмотрим более подробно ООП, исследуя каж­дый из четырех упомянутых столпов.
87

Наследование

Наследование в программировании аналогично генетическому наследова-
нию. При генетическом наследовании можно унаследовать атрибуты от своих родителей, например цвет глаз. Аналогично, когда создаете класс, он может наследовать от другого класса (который называется родительским классом или суперклассом), предоставляя новому созданному классу (класс, который наследует, называется дочерним или подклассом) переменные и методы ро­дительского класса. Смоделируйте геометрические фигуры, используя наследо­вание. Определите класс для представления фигуры:
class Shape(): def __init__(self, w, l):
self.width = w self.len = l def print_size(self): print("""{} на
{}""".format(self.width,self.len))
my_shape = Shape(20, 25) my_shape.print_size()
>> 20 на 25
Этот класс позволяет создавать объекты фигур Shape с двумя параметра-
ми: шириной width и длиной len. Вдобавок объекты класса Shape имеют метод print_size, выводящий эти параметры.
Теперь создайте дочерний класс Square, наследующийся от родительско-
го класса Shape. Наследование осуществляется передачей имени родительско­го класса в качестве параметра дочернему классу при его создании.
class Shape(): def __init__(self, w, l):
self.width = w self.len = l def print_size(self): print("""{} на
{}""".format(self.width,self.len))
88
class Square(Shape): pass
a_square = Square(20,20) a_square.print_size()
>> 20 на 20
Так как класс Shape передан классу Square как параметр, то он наследу­ет переменные и методы родительского класса Shape, а также может вызывать метод print_size. Тело класса Square состоит только из оператора pass, в ходе выполнения которого ничего не происходит, в связи с чем он может ис-
пользоваться в качестве заглушки в тех местах, где это синтаксически необхо­димо, например в инструкциях, где тело является обязательным, таких как def, except и прочих. Иногда pass используют там, где код пока отсутствует, но планируется, а также используют при отладке, разместив на строчке с ним точ­ку остановки.
Благодаря наследованию можно создавать класс Square без необходимо­сти писать еще какой-либо код (за исключением pass) в классе Square, кроме того, что особенно важно, достигается существенное сокращение кода.
Дочерний класс абсолютно аналогичен любому другому классу, в нем можно определять собственные методы и переменные, не затрагивая родитель­ский класс.
class Shape(): def __init__(self, w, l):
self.width = w self.len = l
def print_size(self): print("""{} на
{}""".format(self.width,self.len))
class Square(Shape): def area(self): return self.width * self.len a_square = Square(20, 20) print(a_square.area())
>> 400
89
Наследуя метод от суперкласса, подкласс может переопределить его, то
есть определить новый метод с таким же именем, как у метода, который был унаследован. Эта процедура называется переопределением метода.
class Shape(): def __init__(self, w, l):
self.width = w self.len = l
def print_size(self): print("""{} by {}""".format(self.width,self.len)) class Square(Shape): def area(self): return self.width * self.len def print_size(self):
print("""Я {} на {}""".format(self.width,self.len)) a_square = Square(20, 20) a_square.print_size()
>> Я 20 на 20
Новый метод print_size переопределяет родительский метод с таким
же именем и выводит новое сообщение.

Полиморфизм

Буквальный перевод слова полиморфизм (polymorphism) означает «много
форм». Лучшее определение полиморфизма звучит так: «полиморфизм – это спо­собность (в программировании) представлять один и тот же интерфейс для раз­личных базовых форм (типов данных)». Интерфейсом называют набор функций или методов, не зависящих от конкретной реализации. Рассмотрим ситуацию, в которой один и тот же интерфейс используется для разных типов данных:
print('Hello World!') print(10)
print(15.2) >> "Hello World!" >> 10 >> 15.2
90
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]