Сам_себе_программист._Как_научиться_программировать_и_устроиться. Guide to Programming Professionally
Скачать 3.94 Mb.
|
Практика не приводит к совершенству. Практика приводит к образованию миелина, а миелин приводит к совершенству. Дэниел Койл Если это ваша первая книга по программированию, рекомендую потратить вре- мя на поиск дополнительной информации, прежде чем переходить к следующе- му разделу. Ниже приведены некоторые ресурсы для исследования — на них так- же можно получить советы, если у вас возникли трудности. Для прочтения 1. softwareengineering.stackexchange.com/questions/44177/what-is-the- single-most-effective-thing-you-did-to-improve-your-programming-skil Другие ресурсы Документация к Python — docs.python.org/3/. Краткий справочник Python — cloud.github.com/downloads/kroger/python- quick-ref/python-quick-ref.pdf. Получение помощи Если вы у вас возникли трудности, у меня есть для вас несколько предло- жений. В первую очередь, опубликуйте свой вопрос в группе Self-Taught Programmers в социальной сети Facebook по адресу www.facebook.com/groups/ selftaughtprogrammers. Это сообщество дружелюбных и целеустремленных программистов, которые помогут найти ответ на любой ваш вопрос. Я также советую посетить ресурс www.stackoverflow.com, на котором можно публиковать вопросы по программированию и получать ответы участников со- общества. Для меня важным уроком было научиться рассчитывать на помощь других людей. Попытки в чем-то разобраться — это основная часть учебного процесса, но на определенном этапе они могут стать контрпродуктивными. В прошлом, когда я работал над проектами, мои усилия были ниже точки продуктивности. Если подобное случается сегодня, и я не нахожу ответ быстро, то публикую во- прос онлайн. Каждый раз, когда я задавал вопрос в Интернете, на него обяза- тельно отвечали. В этой связи я не могу в полной мере описать то, насколько дружелюбным и стремящимся помочь является программистское сообщество. 108 Глава 12. Парадигмы программирования Существуют лишь два вида языков программирования: те, которые постоянно ругают, и те, которыми никто не пользуется. Бьерн Страуструп Парадигма программирования — это стиль программирования. Существует множество различных парадигм программирования. Для того чтобы програм- мировать профессионально, вам нужно изучить парадигмы либо объектно-ори- ентированного, либо функционального программирования. В этой главе вы узнаете о процедурном, функциональном и объектно-ориентированном про- граммировании, а больше всего внимания будет уделено объектно-ориентиро- ванному программированию. Состояние Одним из фундаментальных различий между разными парадигмами программи- рования является управление состоянием. Состояние — это значение перемен- ных в программе при ее работе. Глобальное состояние — значение глобальных переменных в программе при ее работе. Процедурное программирование В части I вы программировали, используя парадигму процедурного програм- мирования — стиль программирования, в котором пишется последовательность шагов по направлению к решению, и каждый шаг изменяет состояние програм- мы. В процедурном программировании вы пишете код, чтобы «сделать это, за- тем то». ЧАСТЬ II Введение в объектно-ориентированное программирование 109 Введение в объектно-ориентированное программирование Python_ex233.py 1 x = 2 2 y = 4 3 z = 8 4 xyz = x + y + z 5 xyz >> 14 Каждая строка кода в этом примере изменяет состояние программы. Снача- ла вы определяете x, затем y, затем z. В конце вы определяете значение xyz. Когда вы используете процедурное программирование, то сохраняете дан- ные в глобальных переменных и управляете ими при помощи функций. Python_ex234.py 1 rock = [] 2 country = [] 3 def collect_songs(): 4 song = "| & W." 5 ask = " ( ) ( ). G " 6 while True : 7 genre = input(ask) 8 if genre == "": 9 break 10 if genre == "": 11 rk = input(song) 12 rock.append(rk) 13 elif genre ==(""): 14 cy = input(song) 15 country.append(cy) 16 else: 17 print("$ .") 18 print(rock) 19 print(country) 20 collect_songs() >> ( ) ( ). G 110 Часть II Процедурное программирование подходит для написания небольших программ вроде этой, однако из-за того, что все состояния программы сохра- няются в глобальных переменных, когда код становится больше, появляются проблемы. Проблема с использованием глобальных переменных заключает- ся в том, что они вызывают непредвиденные ошибки. Когда код вашей про- граммы увеличивается в размере, вы используете глобальные переменные в большом количестве функций, и невозможно отследить все места, в которых глобальная переменная изменяется. Например, функция может изменить зна- чение глобальной переменной, а позже в программе другая функция может изменить ту же глобальную переменную, потому что программист, написав- ший вторую функцию, забыл, что глобальная переменная уже была изменена первой функцией. Подобные ситуации возникают довольно часто, искажая код программы. По мере того, как ваша программа усложняется, количество глобальных пе- ременных в ней возрастает. Когда это возрастание совмещается с увеличением числа функций, необходимых программе для обработки новой функциональ- ности, каждая из которых изменяет глобальные переменные, код вашей про- граммы быстро становится непригодным для обслуживания. Более того, этот подход к программированию опирается на побочные эффекты. Побочный эффект — это изменение состояния глобальной переменной. При процедур- ном программировании вы будете часто сталкиваться с непреднамеренными побочными эффектами, такими как случайное двукратное увеличение пере- менной. Эта проблема привела к развитию парадигм объектно-ориентированного и функционального программирования, и эти парадигмы используют разные под- ходы к ее решению. Функциональное программирование Функциональное программирование происходит от лямбда-исчисления — наименьшего в мире универсального языка программирования (созданного ма- тематиком Алонзо Черчем). Функциональное программирование решает про- блемы процедурного программирования с помощью устранения глобального состояния. Функциональный программист полагается на функции, которые не используют и не изменяют глобальное состояние; единственное используемое ими состояние — параметры, которые вы передаете в функцию. Результат, воз- вращаемый функцией, обычно передается в другую функцию. Таким образом, выполняя передачу из функции в функцию, функциональный программист из- бегает глобального состояния. Отказ от глобального состояния избавляет от побочных эффектов и сопутствующих им проблем. Программист из Великобритании, Мэри Роуз Кук, дает функционально- му программированию такое определение: «Функциональный код отличается одним свойством: отсутствием побочных эффектов. Он не полагается на дан- ные вне текущей функции, и не меняет данные, находящиеся вне функции» 7 Свое определение она продолжает примером функции, имеющей побочные эффекты. 7 maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming 111 Введение в объектно-ориентированное программирование Python_ex235.py 1 a = 0 2 def increment(): 3 global a 4 a += 1 И примером функции без побочных эффектов. Python_ex236.py 1 def increment(a): 2 return a + 1 У первой функции есть побочные эффекты, поскольку она полагается на дан- ные за ее пределами и изменяет данные вне текущей функции, увеличивая значе- ние глобальной переменной. У второй функции нет побочных эффектов, так как она не полагается на данные за ее пределами и не изменяет эти данные. Преимущество функционального программирования заключается в том, что оно устраняет целую категорию ошибок, вызванных глобальным состоянием (в функциональном программировании нет глобального состояния). Его недоста- ток заключается в том, что некоторые проблемы легче осмыслить при помощи состояния. К примеру, проектирование пользовательского интерфейса с гло- бальным состоянием легче концептуализировать, чем интерфейс без него. Если вы хотите написать программу, где будет кнопка, нажатие которой переключает режим изображения между видимым и скрытым, такую кнопку проще создать в программе с глобальным состоянием. Можно создать глобальную переменную, принимающую значение True или False и в зависимости от своего те кущего со- стояния скрывающую или показывающую это изображение. Без глобального со- стояния создать такую кнопку сложнее. Объектно-ориентированное программирование Парадигма объектно-ориентированного программирования также решает проблемы, возникающие в процедурном программировании путем устранения глобального состояния, но здесь состояние сохраняется не в функциях а в объ- ектах. В объектно-ориентированном программировании классы определяют на- бор объектов, которые могут взаимодействовать между собой. Классы являются механизмом, позволяющим программисту классифицировать и сгруппировы- вать похожие объекты. Представьте пакет апельсинов. Каждый апельсин — это объект. Все апельсины обладают схожими свойствами, такими как цвет и вес, но значения этих свойств разные у каждого апельсина. Вы можете использовать класс, чтобы смоделировать апельсины и создать объекты апельсинов с разны- ми значениями. Например, можно определить класс , позволяющий создать объ- ект апельсина темного цвета, весящего 300 грамм, и объект светлого апельсина весом 350 грамм. Каждый объект — это экземпляр класса. Если вы определите класс Orange и создадите два объекта Orange, каждый из них будет экземпляром класса Orange, 112 Часть II и у них будет одинаковый тип данных — Orange. Термины «объект» и «экзем- пляр» взаимозаменяемы. При определении класса экземпляры класса будут по- хожими — они все будут иметь свойства, определенные в их классе, как цвет или вес для класса, представляющего апельсин, — но свойства каждого экземпляра будут иметь разные значения. В Python класс является составной инструкцией с заголовком и телом . Класс определяется при помощи синтаксиса class : , где — это имя класса, — определяемое вами тело класса. По соглашению , имена классов в Python всегда начинаются с прописной буквы и записываются в горбатом ре- гистре, то есть при наличии в имени класса больше одного слова первые буквы всех слов нужно сделать прописными (LikeThis), а не добавлять между слова- ми нижние подчеркивания , как с именами функций. Тело в классе может быть простой или составной инструкцией, называемой методом. Методы напомина- ют функции, только их определяют внутри класса и вызывают в объекте, создан- ном классом (как в части I, когда вы вызывали методы вроде "hello".upper() в строках). Имена методов, как и функций, должны указываться строчными бук- вами, а слова должны быть отделены нижними подчеркиваниями. Методы определяются с помощью такого же синтаксиса , что и функции, с двумя отличиями: нужно определить метод как тело в классе, и он должен при- нимать, по меньшей мере, один параметр (за исключением особых случаев). По соглашению, первый параметр метода всегда называется self . При создании метода вы должны всегда определять хотя бы один параметр, поскольку, когда метод вызывается в объекте, Python автоматически передает вызвавший мето д объект в этот метод в качестве параметра. Python_ex237.py 1 class Orange: 2 def __init__(self): 3 print("% B !") Вы можете использовать параметр self, чтобы определить переменную экземпляра — переменную, принадлежащую объекту. Если вы создадите несколько объектов, у них всех будут разные значения переменных экземпляра. Перемен- ные экземпляра объявляются с помощью синтаксиса self. _ = _ . Обычно переменные экземпляра определяются вну- три специального метода __init__ (от англ. слова initialize — инициализиро- вать), который вызывается Python при создании объекта. Ниже приведен пример класса, представляющего апельсин. Python_ex238.py 1 class Orange: 2 def __init__(self, w, c): 3 self.weight = w 4 self.color = c 5 print("% B !") 113 Введение в объектно-ориентированное программирование Код в __init__ выполняется при создании объекта Orange (чего в этом примере не происходит) и создает две переменные экземпляра: weight и color . Их можно использовать как обычные переменные, в любом методе ва- шего класса. Когда вы создаете объект Orange, код в __init__ выводит стро- ку % B !. Любой метод, окруженный двойными нижними подчеркиваниями (как __init__), является магическим методом, который Python использует для особых целей, таких как создание объекта. Вы можете создать новый объект Orange с помощью того же синтаксиса, что вы использовали для вызова функции — _ ( ) — толь- ко замените _ именем класса, который вы хотите использовать для создания объекта, а слово — параметрами, которые принимает __ init__ . Не нужно передавать self , Python сделает это автоматически. Созда- ние нового объекта называется созданием экземпляра класса . Python_ex239.py 1 class Orange: 2 def __init__( self , w, c): 3 self .weight = w 4 self .color = c 5 print("% B !") 6 or1 = Orange(10, " ") 7 print(or1) >> % B ! После определения класса, вы создаете экземпляр класса Orange при помо- щи кода Orange(10, " "), в этом случае выводится строка % B ! . Затем вы выводите сам объект Orange, Python сообщает вам, что это объект Orange, и выдает его местонахождение в памяти (в вашем случае, распо- ложение в памяти не будет совпадать с указанным в данном примере). Как только вы создали объект, можно получить значение его переменных эк- земпляра с помощью синтаксиса _ . _ Python_ex240.py 1 class Orange: 2 def __init__( self , w, c): 3 self .weight = w 4 self .color = c 5 print("% B !") 6 or1 = Orange(10, " ") 7 print(or1.weight) 8 print(or1.color) 114 Часть II >> % B ! >> 10 >> Значение переменной экземпляра можно изменить с помощью синтаксиса _ . _ = _ . Python_ex241.py 1 class Orange: 2 def __init__( self , w, c): 3 self .weight = w 4 self .color = c 5 print("% B !") 6 or1 = Orange(10, " ") 7 or1.weight = 100 8 or1.color = " " 9 print(or1.weight) 10 print(or1.color) >> % B ! >> 100 >> Несмотря на то что значения переменных экземпляра color и weight были " " и 10, соответственно, вы смогли их изменить, присвоив им значения " " и 100. Используя класс Orange, вы можете создать множество апельсинов. Python_ex242.py 1 class Orange: 2 def __init__( self , w, c): 3 self .weight = w 4 self .color = c 5 print("% B !") 6 or1 = Orange(4, " ") 7 or2 = Orange(8, " ") 8 or3 = Orange(14, "& ") >> % B ! >> % B ! >> % B ! 115 Введение в объектно-ориентированное программирование Апельсин не определяется одними только физическими свойствами вроде цвета и веса. Апельсины делают разные вещи — например, гниют — и вы можете смоделировать их с помощью методов. Ниже показано, как можно наделить объ- ект Orange возможностью «гнить». Python_ex243.py 1 class Orange(): 2 def __init__( self , w, c): 3 """ G""" 4 self .weight = w 5 self .color = c 6 self .mold = 0 7 print("% B !") 8 def rot( self , days, temp): 9 self .mold = days * temp 10 orange = Orange(6, " ") 11 print(orange.mold) 12 orange.rot(10, 33) 13 print(orange.mold) >> % B ! >> 0 >> 330 Метод rot принимает два параметра: число дней, прошедших с тех пор как кто-то сорвал апельсин, и среднюю температуру за этот период. Когда вы вы- зываете метод, он использует формулу для увеличения переменной экземпляра mold — это работает, поскольку вы можете изменять значение любой перемен- ной экземпляра внутри любого метода. Теперь апельсин может гнить. В классе можно определять множество методов. Ниже приведен пример по- строения модели прямоугольника при помощи метода для расчета его площади и другого метода для изменения его размера. Python_ex244.py 1 class Rectangle(): 2 def __init__( self , w, l): 3 self .width = w 4 self .len = l 5 def area( self ): 6 return self .width * self .len 116 Часть II 7 def change_size( self , w, l): 8 self .width = w 9 self .len = l 10 rectangle = Rectangle(10, 20) 11 print(rectangle.area()) 12 rectangle.change_size(20, 40) 13 print(rectangle.area()) >> 200 >> 800 В этом примере объекты Rectangle имеют две переменные экземпляра: len и width. Метод area возвращает площадь объекта Rectangle, перемножая между собой переменные экземпляра, а метод change_size изменяет перемен- ные, присваивая им числа, которые передаются в качестве параметров. У объектно-ориентированного программирования есть несколько преиму- ществ. Эта парадигма способствует повторному использованию кода и вслед- ствие этого сокращает количество времени, необходимое на разработку и об- служивание кода. Проблемы разбиваются на множество фрагментов, благодаря чему код становится легче поддерживать. Недостатком объектно-ориентирован- ного программирования можно считать то, что создание программ требует боль- ших усилий, поскольку их разработка включает огромный объем планирования. Словарь терминов Глобальное состояние: значение глобальных переменных в программе при ее работе. Классы: механизм, позволяющий программисту классифицировать и сгруппи- ровывать похожие объекты. Магический метод: метод, который Python использует в разных ситуациях, на- пример, при создании объекта. Методы: тело в классах. Методы похожи на функции, только их определяют вну- три класса и вызывают только в объекте, созданном классом. Объектно-ориентированное программирование: парадигма программирова- ния, где вы определяете объекты, которые взаимодействуют друг с другом. Парадигма программирования: стиль программирования. Переменные экземпляра: переменные, которые принадлежат объекту. Побочный эффект: изменение состояния глобальной переменной. Процедурное программирование: стиль программирования, в котором пишет- ся последовательность шагов по направлению к решению, и каждый шаг изменя- ет состояние программы. Создание экземпляра класса: создание нового объекта при помощи класса. Состояние: это значение переменных в программе во время ее работы. Функциональное программирование: решает проблемы процедурного про- граммирования с помощью устранения глобального состояния, передавая его от функции к функции. 117 Введение в объектно-ориентированное программирование Экземпляр: каждый объект это экземпляр класса. Каждый экземпляр класса имеет тот же тип, что и все остальные экземпляры этого класса. Практикум 1. Определите класс Apple с четырьмя переменными экземпляра, представля- ющими четыре свойства яблока. 2. Создайте класс Circle с методом area, подсчитывающим и возвращающим площадь круга. Затем создайте объект Circle, вызовите в нем метод area и выведите результат. Воспользуйтесь функцией pi из встроенного в Python модуля math. 3. Создайте класс Triangle с методом area, подсчитывающим и возвращаю- щим площадь треугольника. Затем создайте объект Triangle, вызовите в нем area и выведите результат. 4. Создайте класс Hexagon с методом calculate_perimeter, подсчитыва- ющим и возвращающим периметр шестиугольника. Затем создайте объект Hexagon , вызовите в нем calculate_perimeter и выведите результат. Решения: chall_1.py — chall_4.py. |