Питон для нормальных. Учебник Москва Базальт спо макс пресс 2018
Скачать 2.54 Mb.
|
во-вторых, компилятор, имея возможность анализировать всю программу цели- ком, всё-таки может произвести ряд оптимизаций, увеличивая таким образом скорость исполнения по сравнению с простым пошаговым интерпретированием. Хотя языки виртуальных машин ближе к компилируемым, чем интерпрети- руемые языки, они появились позже и их условно можно назвать пятым поколе- нием языков программирования. 1 Некоторые языки могут и компилироваться, и интерпретироваться, и компилироваться в байткод, например, OCaml. Программа «Привет мир» на Java: l a s s H e l l o W o r l d { p u b l i s t a t i v o i d main ( String args []) { System . out . println ( " Hello Ђ World " ); } } Таблица 1.1. Поколения языков программирования I поколение Машинные языки II поколtение Транслируемые языки (ассемблеры) III поколение Компилируемые языки IV поколение Интерпертируемые языки V поколение Языки виртуальных машин 1 Многие авторы до сих пор выделяют только 3 поколения языков программирования, сов- мещая компилируемые, интерпретируемые и компилируемые в байткод языки в рамках одного последнего. Хотя такая классификация является «классическою», в наше время с нею труд- но согласиться, так как большинство программистов никогда не имели дело с первыми двумя поколениями и, следовательно, для них от такой классификации вовсе нет толка. 12 Глава 1. Введение С начала 90-х на смену обычным языкам программирования в области вы- числений стали приходить различные специализированные математические па- кеты. В настоящее время наибольшею популярностью пользуется MatLab. Кро- ме него часто используются также другие коммерческие пакеты: Mathematica, MathCad, STATISTICA, а также свободные аналоги: SciLab и, особенно, ста- тистический пакет R. Пакеты существенно упростили разработку приложений, внеся два ключевых усовершенствования: • большая доступная встроенная библиотека алгоритмов, которая может быть расширена средствами, как самого пакета, так и с подключением мо- дулей на Fortran и C; • встроенные средства для построения графиков, позволяющие визуализиро- вать данные на экране компьютера в интерактивном режиме и сохранять результаты построения в файлы основных форматов. 1.2 Парадигмы программирования Парадигма программирования — это совокупность идей и понятий, определя- ющих стиль написания компьютерных программ (подход к программированию). Важно отметить, что парадигма программирования не определяется одно- значно языком программирования; практически все современные языки про- граммирования в той или иной мере допускают использование различных пара- дигм (мультипарадигмальное программирование). Также важно отметить, что существующие парадигмы зачастую пересекаются друг с другом в деталях, по- этому можно встретить ситуации, когда разные авторы употребляют названия из разных парадигм, говоря при этом, по сути, об одном и том же явлении. Счита- ется, что все парадигмы делятся на две большие части: императивное и деклара- тивное программирование. Императивное программирование — это парадигма программирования, которая описывает процесс вычисления в виде инструкций, изменяющих состояние данных. Подразделы императивного программирования — структурное и объектно-ориентированное. Декларативное программирова- ние — это парадигма программирования, в которой вместо пошагового алгорит- ма решения задачи (что делает императивное программирование, описывающее как решить задачу) задаётся спецификация решения задачи, т. е. описывается, что собой представляет проблема и что требуется получить в качестве результа- та. Декларативные программы не используют понятия состояния и, в частности, не содержат переменных и операторов присваивания. К декларативной парадиг- ме относится функциональное программирование. Структурное программирование — методология разработки программно- го обеспечения, в основе которой лежит представление программы в виде иерар- хической структуры блоков. Языками-первопроходцами в этой парадигме были Fortran, Algol и B, позже их приемниками стали Pascal и C. В соответствии с данной методологией любая программа состоит из трёх базовых управляющих 1.2. Парадигмы программирования 13 структур: последовательность, ветвление, цикл; кроме того, используются под- программы. При этом разработка программы ведётся пошагово, методом «сверху вниз». Следование принципам структурного программирования сделало тексты про- грамм, даже довольно крупных, нормально читаемыми. Серьёзно облегчилось понимание программ, появилась возможность разработки программ в нормаль- ном промышленном режиме, когда программу может без особых затруднений понять не только её автор, но и другие программисты. Python использует эту парадигму как вспомогательную. Объектно-ориентированное программирование (ООП) — это методоло- гия программирования, основанная на представлении программы в виде совокуп- ности объектов, каждый из которых является экземпляром определенного клас- са, а классы образуют иерархию наследования. В основе концепции объектно- ориентированного программирования лежит понятие объекта — некой сущно- сти, которая объединяет в себе поля (данные) и методы (выполняемые объектом действия). Например, объект ЧЕЛОВЕК может иметь поля ИМЯ, ФАМИЛИЯ и мето- ды КУШАТЬ, СПАТЬ. Соответственно, в программе можем использовать операторы ЧЕЛОВЕК.ИМЯ:=’Иван’ и ЧЕЛОВЕК.КУШАТЬ(пища). С самого начала Python проектировался как объектно-ориентированный язык программирования: все данные представляются объектами Python, программа является набором взаимодействующих объектов, посылающих друг другу сооб- щения, каждый объект имеет собственную часть памяти и может иметь в составе другие объекты, каждый объект имеет тип, объекты одного типа могут прини- мать одни и те же сообщения (и выполнять одни и те же действия). Функциональное программирование — парадигма программирования, в ко- торой процесс вычисления трактуется как вычисление значений функций в ма- тематическом понимании последних (в отличие от функций как подпрограмм в структурном программировании). Наиболее известные LISP, Haskell, семейство языков ML. Противопоставляется парадигме императивного программирования, которая описывает процесс вычислений как последовательное изменение состояний (в значении, подобном таковому в теории автоматов). При необходимости, в функ- циональном программировании вся совокупность последовательных состояний вычислительного процесса представляется явным образом, например, как спи- сок. Функциональное программирование предполагает обходиться вычислением результатов функций от исходных данных и результатов других функций, и не предполагает явного хранения состояния программы. Соответственно, не пред- полагает оно и изменяемость этого состояния (в отличие от императивного, где одной из базовых концепций является переменная, хранящая своё значение и позволяющая менять его по мере выполнения алгоритма). Функциональное программирование является одной из парадигм, поддержи- ваемых языком программирования Python. Основными предпосылками для пол- ноценного функционального программирования в Python являются: функции 14 Глава 1. Введение Рис. 1.1. Парадигмы программирования высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений. Элементы функционального программирова- ния в Python могут быть полезны любому программисту, так как позволяют гармонично сочетать выразительную мощность этого подхода с другими подхо- дами. Визуальное программирование — способ создания программы для ЭВМ путём манипулирования графическими объектами вместо написания её текста. Визуальное программирование часто представляют как следующий этап разви- тия текстовых языков программирования. Наглядным примером может служить среда разработки Delphi, сделанная для языка Object Pascal, где редактируют- ся графические объекты: форма, кнопки, метки. В последнее время визуальному программированию стали уделять больше внимания в связи с развитием мобиль- ных сенсорных устройств (КПК, планшеты). С Python поставляется библиотека tkinter на основе Tcl/Tk для создания кроссплатформенных программ с графическим интерфейсом. Существуют рас- ширения, позволяющие использовать все основные библиотеки графических ин- терфейсов: wxPython, основанное на библиотеке wxWidgets, PyGTK для Gtk, PyQt и PySide для Qt и другие. Некоторые из них также предоставляют широ- кие возможности по работе с базами данных, графикой и сетями, используя все возможности библиотеки, на которой основаны. 1.3 Типизация в языках программирования Все данные в компьютере хранятся в виде последовательностей нулей и еди- ниц подряд. Для удобства эти последовательности группируют по 8 цифр подряд и такую группу называют байтом (два байта называются машинным словом). Од- нако оперировать последовательностями битов напрямую при написании боль- ших программ неудобно, поэтому вводят дополнительные договорёности о спо- 1.3. Типизация в языках программирования 15 собе интерпретации отдельных байтов в памяти. Эти договорённости и можно назвать типами данных. Все языки программирования можно разделить на: • нетипизированные (бестиповые), • типизированные. Нетипизированными являются языки ассемблера, а также язык програм- мирования встраиваемых устройств Forth. По сути, бестиповые — это наиболее низкоуровневые языки, предоставляющие прямой доступ к манипулированию отдельными битами прямо в регистрах процессора. Все компилируемые и интер- претируемые широко используемые языки, такие как Pascal, C, Python, PHP и другие, являются типизированными. У отсутствия типизации есть некоторые преимущества: • Полный контроль над действиями компьютера. Компилятор или интерпре- татор не будет мешать какими-либо проверками типов, запрещая те или иные действия. • Получаемый код обычно имеет высокую эффективность, которая, правда, зависит в первую очередь от квалификации программиста. • Прозрачность инструкций. При знании языка обычно нет сомнений, что из себя представляет тот или иной код. Недостатки отсутствия типизации: • Сложность написания программы быстро растёт с ростом необходимой аб- стракции и общности. Даже операции с такими, казалось бы, несложными объектами, как списки, строки или записи, уже требуют существенных уси- лий. • Отсутствие проверок и как следствие огромное число трудноуловимых оши- бок на этапе компиляции. Любые бессмысленные действия, например вы- читание указателя на массив из символа будут считаться совершенно нор- мальными. • Высокие требования к квалификации программиста и фактическим знани- ям об архитектуре целевой ЭВМ. Типизированные языки делятся ещё на несколько пересекающихся катего- рий. 1. Сильная/слабая типизация (также иногда говорят строгая / нестрогая). Сильная типизация означает, что язык не позволяет смешивать в выраже- ниях различные типы и не выполняет автоматические неявные преобразо- вания, например, нельзя вычесть из строки множество. Языки со слабой ти- пизацией выполняют множество неявных преобразований автоматически, 16 Глава 1. Введение даже если может произойти потеря точности или преобразование неодно- значно. В действительности почти все популярные языки: C, Java, Python, Pascal и другие имеют условно сильную типизацию, позволяя некоторые автоматические преобразования типов. Самые распространённые примеры: автоматическое приведение целых чисел к действительным и действитель- ных к комплексным, а также символов к строкам. Крайний случай слабой типизации — отсутствие типов вообще. Преимущества сильной типизации: • Надежность — вместо неправильного поведения вы получите исклю- чение или ошибку компиляции. • Скорость — преобразования типов могут быть довольно затратными, с сильной типизацией необходимо писать их явно, что заставляет про- граммиста как минимум знать, что этот участок кода может быть мед- ленным, или избегать их. • Понимание работы программы — опять же, вместо неявного приведе- ния типов программист пишет все сам, а, значит, примерно понимает, что сравнение строки и числа происходит не само собой и не по вол- шебству, а использовать действительнозначную переменную в качестве счётчика цикла опасно из-за ошибок округления. • Определенность — когда вы пишете преобразования вручную, вы точ- но знаете, что вы преобразуете и во что. Также вы всегда будете по- нимать, что такие преобразования могут привести к потере точности, затратам машинного времени или стать причиною логической ошибки. Преимущества слабой типизации: • Удобство использования смешанных выражений (например, комбини- рование целых и вещественных чисел). • Скорость разработки: не нужно тратить время на написание большого числа явных преобразований типов. • Краткость записи. 2. Явная/неявная типизация. Явно-типизированные языки отличаются тем, что тип новых переменных/функций/их аргументов нужно писать яв- но. Соответственно, языки с неявной типизацией перекладывают эту задачу на компилятор/интерпретатор, такой способ называется автоматическим выведением типов. Все компилируемые языки — наследники ALGOL 60 – имеют явную типизацию. Это C, C++, D, Java, C#, Pascal, Modula 2, Ada и другие. Напротив, языки семейства ML (Standard ML и Ocaml), Haskell, почти все интепретируемые языки: Pyhton, Ruby, Perl, PHP, JavaScript, Lua имеют неявную. Преимущества явной типизации: 1.3. Типизация в языках программирования 17 • Многие логические ошибки, ведущие к неверному приведению типов, можно отловить на этапе компиляции. Либо эти ошибки вовсе не воз- никают, поскольку попытка выписать тип выражения приводит к мыс- ли, что само выражение неверно. • Знание того, какого типа значения могут храниться в конкретной пере- менной, снимает необходимость помнить это при отладке и дальнейшей модификации программы. • Существенно упрощается написание компиляторов, поскольку компи- лятор не должен уметь определять тип переменной. Как следствие, ча- сто можно произвести ряд дополнительных оптимизаций уже на этапе компиляции автоматически. Преимущества неявной типизации: • Сокращение записи (сравните Python и Pascal): def add(x, y) : fun tion add(x: real ; y: integer ): real ; • Полиморфизм (универсальность). Одна и та же функция может быть написана для переменных разных типов, если используемые в ней опе- рации определены для них. В языках с явною типизацией в такой си- туации приходится писать много одинаковых функций, отличающихся только типом аргументов и результата (это называется перегрузкою функции), либо эмулировать неявную типизацию за счёт шаблонов и генериков. 3. Статическая/динамическая типизация. Статическая типизация опре- деляется тем, что конечные типы переменных и функций устанавливаются на этапе компиляции. Т.е. уже компилятор на 100% уверен, какой тип, где находится. В динамической типизации все типы выясняются уже во время выполнения программы. Примеры языков со статическою типизацией: C, Java, C#, Ada, С++, D, Pascal. Примеры языков с динамическою типиза- цией: Python, JavaScript, Ruby, PHP, Perl, JavaScript, Lisp. При статической типизации параметр подпрограммы и возвращаемое зна- чение функции связывается с типом в момент объявления и тип не мо- жет быть изменён позже (переменная или параметр будут принимать, а функция — возвращать значения только этого типа). Некоторые статически типизированные языки позже получили возможность также использовать динамическую типизацию при помощи специальных подсистем. Например, тип Variant в Delphi, Data.Dynamic в Haskell, C# поддерживает псевдо-тип dynamic Преимущества статической типизации: • Статическая типизация даёт самый простой машинный код. 18 Глава 1. Введение • Многие ошибки исключаются уже на стадии компиляции. • Статическая типизация хороша для написания сложного, но быстрого кода. • В интегрированной среде разработки осуществимо автодополнение (среда разработки сама догадывается и дописывает часть кода за про- граммиста), особенно если типизация — строгая статическая: множе- ство вариантов можно отбросить как не подходящие по типу. • Чем больше и сложнее проект, тем большее преимущество дает стати- ческая типизация, и наоборот. Недостатки статической типизации: • Языки с недостаточно проработанной математической базой оказы- ваются довольно многословными: каждый раз надо указывать, какой тип будет иметь переменная. В некоторых языках есть автоматиче- ское выведение типа, однако оно может привести к трудноуловимым ошибкам. • Тяжело работать с данными из внешних источников (например, в ре- ляционных СУБД/ десериализация данных). При динамической типизации переменная связывается с типом в момент присваивания значения, а не в момент её объявления (как правило, она вообще не объявляется нигде до момента первого использования). Таким образом, в различных участках программы одна и та же переменная может принимать значения разных типов. Преимущества динамической типизации: • Упрощается написание несложных программ. • Облегчается работа прикладного программиста с СУБД, которые принципиально возвращают информацию в «динамически типизиро- ванном» виде. Поэтому динамические языки ценны, например, для программирования веб-служб. • Иногда требуется работать с данными переменного типа. Например, может понадобиться выдать массив или одно число, или вернуть спе- циальное значение типа «ничто». В языке со статическою типизацией такое поведение приходится эмулировать: одно число заменять масси- вом размером в 1 элемент; при возможности появления особого зна- чения — вводить так называемые «вариантные типы», как сделано в OCaml. Недостатки динамической типизации: • Статическая типизация позволяет уже при компиляции заметить про- стые ошибки «по недосмотру». Для динамической типизации требует- ся как минимум выполнить данный участок кода. 1.3. Типизация в языках программирования 19 • Особенно коварны в динамическом языке программирования опечат- ки: разработчик может несколько раз просмотреть неработающий код и ничего не увидеть, пока наконец не найдёт набранный с ошибкой идентификатор. • Не действует либо действует с ограничениями автодополнение в среде разработки: трудно или невозможно понять, к какому типу относится переменная, и вывести набор её полей и методов. |