Общие понятия
Python (в русском языке распространено название пито́н или па́йтон) — кроссплатформенный высокоуровневый язык программирования общего назначения, ориентированный на повышение производительности разработчика и читаемости кода.
Эту фразу видели все, кто хоть раз в жизни открывал статью про Python на Википедии. Вроде бы всё просто и ясно, но как-то не очень. Уже здесь встречаются непонятные термины: кроссплатформенный, высокоуровневый… Дать ёмкое и понятное определение чему бы то ни было – это великое искусство. Поэтому, я думаю, стоит пояснить всё вышесказанное простыми словами. А простыми словами это выглядит примерно так: Пайтон – это язык программирования, который можно использовать на любой известной стандартной операционной системе (Windows, MacOs, Linux) и при помощи которого можно написать практически любую программу (от «Привет, мир!» до новой операционной системы или нового языка программирования). Звучит заманчиво, не так ли?
Пайтон был придуман в 1991 году голландцем Гвидо ван Россумом, также известным как «великодушный пожизненный диктатор». Именно он по сей день принимает окончательные решения о том, как именно будет развиваться язык. Кстати, последняя версия питона (3.9.0) вышла буквально на днях – 05.10.2020. Её главная особенность для новичков, на мой взгляд, в том, что для её установки требуется минимум Windows 10 (на семёрку она не инсталлируется). Всё, чему будут посвящены мои статьи, гарантированно будет актуальным для версий 3.6.0-3.9.0. Версии питона 2.Х здесь рассматриваться не будут, т.к. для разработки новых приложений они уже не используется, а те, кто всё ещё стоически поддерживает приложения, написанные на них, вряд ли будут всё это читать.
Объектно-ссылочная модель
В своих интервью Гвидо подчёркивал, что никогда не видел Python в качестве языка сугубо функционального программирования. С самого начала питон позиционировался как язык объектно-ориентированного программирования (ООП), однако, он на нём можно писать в абсолютно любой парадигме - структурное, обобщенное, объектно-ориентированное, функциональное и аспектно-ориентированное программирование. Не буду заострять внимание на том, что означает каждый из них. Если интересно - это легко гуглится. Что же касается ООП, то пайтон спроектирован таким образом, что абсолютно всё, что в нём есть, т.е. все типы данных, функции, методы, классы, переменные – всё это является объектами, то есть некоторыми сущностями, обладающими определённым состоянием и поведением, имеющими определённые свойства (атрибуты) и операции или действия над ними (методы). Что это означает на практике, мы увидим уже достаточно скоро, а пока что просто примите это и уверуйте, что это именно так.
Переменных (в привычном понимании – «контейнеров») в питоне не существует. Когда в коде встречается нечто вроде «а = 5», серьёзные бородатые программисты в свитерах вам скажут, что «а» - это переменная, в которую записано значение «целое число пять». В случае с питоном это утверждение правда и неправда одновременно. В питоне «целое число пять» – это самостоятельный неизменяемый объект, который лежит где-то в оперативной памяти вашего компьютера. Например, выполним в консоли интерпретатора следующую команду
>>> id(5)
1609562080
Этой командой мы узнаём адрес той ячейки памяти, где у нас лежит число 5 – это ячейка 1609562080 (у вас может быть другое значение – это неважно).
Следом выполним ещё пару команд:
>>> a = 5
>>> id(a)
1609562080
Оказывается, у переменной «а» тот же адрес, что и у самой пятёрки. Но ведь если в питоне всё является объектом, то переменная – это контейнер, а значит тоже объект, то у неё должен быть свой адрес? Ответ – нет. В питоне нет переменных-контейнеров. Вместо этого в питоне есть ссылки на объекты. В данном случае «а» - это ссылка на число 5. Как это ещё проверить? Очень просто. Выполним ещё одну команду
>>> b = 5
>>> id(b)
1609562080
То же самое?! Да, именно так. Переменная «b» ссылается всё на ту же самую пятёрку. Последняя проверка этой теории:
>>> a is b
True
Переменная «а» - это то же самое, что и переменная «b», и любая другая «переменная», которая будет ссылаться на пятёрку, какое бы имя вы ей не придумали.
Возникает вопрос: а что же тогда такое знак «=» в данном случае? В питоне знак «=» не знак равенства, а оператор присваивания. Равенство в питоне выражается через оператор «==», который используется исключительно в булевой логике, т.е. для сравнения значений и/или содержимого разных объектов.
Итак, вывод: в питоне переменных нет, знак «=» не приравнивает значение к переменной и не записывает значение в переменную, а присваивает какому-либо объекту именную ссылку для удобства обращения.
Динамическая типизация
Динамическая или как принято в питонической среде - «утиная типизация». Это очень простая концепция: «Если что-то выглядит как утка, плавает как утка и крякает как утка, то это наверняка и есть утка».
Существуют языки со статической и динамической типизацией данных. Статическая типизация – это когда мы до того, как записать в нашу переменную «а» пятёрку, сначала должны создать эту самую переменную и объявить, что в эту переменную мы будем складывать только целые числа. Никакой другой тип данных, в том числе дробные числа, в эту переменную после этого положить уже не получится.
Прелесть и одновременно боль питона состоит в том, что в нём в одну и ту же «переменную» можно положить что угодно. А если вспомнить слова из предыдущего блока, то надо понимать, что «переменные» тут вообще ни при чём, так как это всего лишь ссылки, а вот сами объекты могут свободно менять своё состояние из одного в другое на лету. Например, было у нас целое число 5. Не хотим мы его в виде целого числа, мы хотим строку. Пожалуйста, без проблем:
>>> a = 5
>>> type(a)
<class 'int'>
>>> a = str(a)
>>> type(a)
<class 'str'>
>>> a
'5'
Вот и всё, теперь переменная «а» ссылается уже не на целочисленный объект 5, а на строковый объект 5. Для наглядности я сразу запросил тип каждого из объектов, и оснований не верить результатам лично у меня нет: вначале пятёрка – это число, в конце – строка. Об этом говорит не только type(a), но и само значение, которое раньше было голой пятёркой, а теперь обрамлено кавычками.
Подобным образом тип любого объекта можно менять очень быстро в зависимости от нужд разработчика, и в этом прелесть. Почему это одновременно и боль? Потому что динамическая типизация к огромному сожалению отбирает большое количество ресурсов, что сказывается на производительности и быстродействии. Да, это какие-то там микросекунды, но они очень важны. Кроме того, неопытные разработчики очень любят использовать эту прелесть без особой нужды, что, во-первых, отъедает ресурсы, а во-вторых, даёт подчас внезапные неожиданные результаты. Например, вы бы хотели ожидать на выходе работы программы целое число, а получаете строку. Казалось бы, чего проще – ну переделай её обратно в число и живи счастливо? Но согласитесь, что это как минимум неудобно, когда вам нужно неожиданный результат потом как-то обрабатывать и приводить к нужному типу. Это дополнительный код, а значит дополнительное время на его написание, на его работу и на его поддержку. А время – деньги.
Стиль кодирования. PEP8
У каждого языка программирования, как и любого другого языка, есть свои законы, которым он подчинён. Если русском языке мы пишем названия месяцев со строчной буквы (9 мая, 8 марта), то в английском, например, месяцы всегда пишутся с прописной (January, 4th of July). Питон также имеет свою семантику, грамматику и свой синтаксис. Основным сводом законов, определяющих требования к красивому и качественному стилю кодирования определены в документе, который называется Python Enhancement Protocol 8 или PEP8. Основные правила на самом деле достаточно просты, но почему-то на каждом новом курсе я встречаю людей, упорно их игнорирующих.
Давайте начнём с того, что Python – язык международный. Это вам не 1С, на котором пишут в буквальном смысле по-русски. В питоне всё по-английски. Всё и всегда. Мне попадались 2 уникума, которые упорно именовали переменные по-русски. Объясняли они это тем, что «так понятнее». Нет, если вы не знаете английского, то вам, возможно, будет понятнее. Но нужно учитывать, что IT – это англоязычная среда, все программы, и 90% документации написаны именно на английском, и, в конце концов, ваш код могут читать (а большинство времени будет потрачено именно на его чтение, причём не только вами) люди, не знающие русского. Ну или давайте наоборот: вам прислали на проверку кусок кода, где половина на хинди или суахили. Как вам такое? Возьмите себя в руки и если вы не знаете или плохо знаете английский – начните его учить с нуля. Не брезгуйте онлайн-переводчиками и словарями для выбора имён переменных и функций – это не стыдно.
Вообще, раз уж зашла об этом речь, то надо сказать, что есть отдельная книга, которая посвящена рефакторингу кода. Название переменной должно отражать то, что в ней лежит (или в нашем случае – на что она ссылается). Название функции должно чётко отражать то, что она делает. Многие и то и дело пишут примерно так:
>>> a = 5
>>> b = "mama"
>>> for i in b:
... print(i * a)
...
mmmmm
aaaaa
mmmmm
aaaaa
Казалось бы, всё работает – что ещё нужно? Да, работает. Даже если бы переменные были названы по-русски или по-китайски, то же бы работало, так как питону глубоко всё равно, какой алфавит вы используете для обозначения ссылок - он всё это перерабатывает в байтовые представления символов юникода. Кошмар же заключается в том, что не соблюдено главное правило питониста: код должен быть читаемым. Читаемым человеком, его глазами. Возможно, сейчас это будет сложно, но попробуйте представить, что между объявлением переменных и циклом for не ноль строк, как сейчас, а 100-150-200. Да, это утрированный пример, но всё-таки. Никто через 100, да даже через 50 строк не вспомнит, что такое переменные «a» и «b». Ну какие-то там переменные, но что в них лежит, на что они ссылаются? Может это число и строка? А может это строка и число? А может это и не строка и не число? Надо лезть выше, искать их, разбираться. Опять всё сводится к бесполезной трате времени.
Не проще ли было сразу назвать эти переменные по-человечески (в прямом смысле)? Давайте попробуем:
>>> number = 5
>>> string = "mama"
>>> for i in string:
... print(i * number)
...
mmmmm
aaaaa
mmmmm
aaaaa
Выхлоп не изменился, но мозгу становится понятнее, глазу приятнее. Осталось разобраться с «i».
В строке «for i in string» «i» является ничем иным как временной переменной. Её тоже нужно уметь красиво и понятно назвать. Как показывает моя собственная практика, иногда хорошее, грамотное название переменной снимает у студентов возникшие вопросы по дальнейшим действиям. Они внезапно начинают понимать, что делали не так и что нужно на самом деле делать и куда двигаться. Так давайте уже её переделаем и пойдём дальше:
>>> number = 5
>>> string = "mama"
>>> for letter in string:
... print(letter * number)
...
mmmmm
aaaaa
mmmmm
aaaaa
Именно это и описано в PEP8 – как писать качественный код, отвечающий требованиям и стилю Python, или как принято говорить – pythonic style или pythonic way. Многие из нас, кто ещё в школе пытался учиться программированию, помнят такой язык, как Basic или QBasic. Потом он перерос в VBA и до сих пор (к сожалению…) используется для написания макросов в офисных программах Microsoft. И в связи с этим у многих из нас при изучении питона всплывает Basic style – стиль кодирования на Basic. Pythonic style ушёл намного дальше. Читаемость кода за счёт простоты синтаксиса повысилась в разы. Вот для наглядности пример.
Задача: получите из входного потока число и напечатайте сумму его цифр.
Как бы эту задачу решило большинство в «Basic style»:
>>> number = input()
>>> summa = 0
>>> for dig in number:
… if dig in '0123456789':
… summa = summa + int(dig)
>>> print(summa)
Как это решается в «Pythonic style»:
>>> print(sum(int(x) for x in input() if x.isdigit()))
Возможно, вам сейчас ничего не понятно из того, что тут написано, но вам стоит оценить хотя бы то, сколько сил и времени сэкономлено за счёт сокращения количества строк. «Достаточно одной таблетки» (с). В данном случае – одной строки, а результат одинаковый.
Давайте тезисно определим основные правила синтаксиса:
- используя питон, мы пишем на английском языке;
- все названия переменных, функций, классов должны как можно полнее отражать их суть и содержание;
- названия переменных и функций принято писать в нижнем регистре, разделяя слова подчёркиванием (snake_case):
# ПРАВИЛЬНО
my_number = 42
def my_function():
#НЕ правильно
MyNumber = 42
Mynumber = 42
MYNUMBER = 42
mynumber = 42
МоЙнОмЕр = 42
def MyFunction():
def Myfunction():
def MYFUNCTION():
def myfunction():
- названия классов принято писать в CamelCase – слова пишутся с заглавной буквы без разделителей между словами:
# ПРАВИЛЬНО
class MySuperPuperClass:
#НЕ правильно
все остальные варианты
- для названия переменных, функций и классов нельзя использовать зарезервированные слова (слова, которые зашиты в питоне в качестве операторов, имен встроенных функций, классов и их атрибутов и методов);
- конец строки является концом инструкции (никакие специальные знаки не требуются);
- вложенные инструкции объединяются в блоки по величине отступов. 1 отступ = 4 пробела;
- вложенные инструкции в Python записываются в соответствии с одним и тем же шаблоном: основная инструкция завершается двоеточием, вслед за которым располагается вложенный блок кода с отступом в 4 пробела под строкой основной инструкции.
if a == 1:
print('mama' * 3) # 4 пробела после двоеточия
- в случае, если длина инструкции превышает 80 знаков, допустимо записывать одну инструкцию в нескольких строках. Достаточно ее заключить в пару круглых, квадратных или фигурных скобок:
if (a == 1 and b == 2 and
c == 3 and d == 4): # продолжение инструкции выравнивается по открывающей скобке
print('mama' * 3) # 4 пробела после двоеточия
- тело составной инструкции может располагаться в той же строке, что и тело основной, если тело составной инструкции не содержит составных инструкций.
>>> if x > y: print(x)
- технически возможно записать несколько инструкций в одной строке, разделяя их точкой с запятой (но такой способ не приветствуется, т.к. не соответствует стилю кодирования PEP8):
>>> a = 1; b = 2; print(a, b)
Это основные вещи, которые нужно выучить. Благо, что мы живём в 21 веке и рабочие среды уже давно умеют анализировать наш код на соответствие стилю, а также полно разнообразных сторонних линтеров. Поэтому, если вы переживаете, что вы не осилите этот момент – ваша IDE вам подскажет, где что поправить, и в большинстве случаев – сама же и поправит. Если она этого самостоятельно делать не умеет, то можно воспользоваться сторонними линтерами.
Что будет, если на всё это забить и писать как бог на душу положит? Да ничего. Просто вас проклянут до десятого колена те, кто будет это читать, а в остальном ничего.
Дзен питона
Также у питона есть свой собственный сборник мантр, который также призван формировать хороший стиль у разработчиков. Выполним в консоли команду:
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Перевод на русский конечно же существует:
Дзен Пайтон (составлен Тимом Питерсом)
Красивое лучше, чем уродливое.
Явное лучше, чем неявное.
Простое лучше, чем сложное.
Сложное лучше, чем запутанное.
Плоское лучше, чем вложенное.
Разреженное лучше, чем плотное.
Читаемость имеет значение.
Особые случаи не настолько особые, чтобы нарушать правила.
При этом практичность важнее безупречности.
Ошибки никогда не должны замалчиваться.
Если не замалчиваются явно.
Встретив двусмысленность, отбрось искушение угадать.
Должен существовать один — и, желательно, только один — очевидный способ сделать это.
Хотя он поначалу может быть и не очевиден, если вы не голландец (это такая шутливая отсылка к Гвидо).
Сейчас лучше, чем никогда.
Хотя никогда зачастую лучше, чем прямо сейчас.
Если реализацию сложно объяснить — идея плоха.
Если реализацию легко объяснить — идея, возможно, хороша.
Пространства имён — отличная вещь! Давайте будем делать их больше!
Все эти правила кажутся на первый взгляд элементарными. Но как я уже сказал выше: сформулировать что-то кратко и понятно – это искусство. Просто придерживайтесь этих мантр, и всё у вас будет хорошо.
«Hello, world!»
Пришло время написать первую программу. Хотя на самом деле это не так. Свою первую программу все те, кто скачал и поставил себе пайтон, уже написали. С точки зрения программирования рабочей программой называется та программа, которая запускается и не выдаёт ошибок. Так что с этой точки зрения пустой код – это вполне рабочая программа. Вы можете в этом сами убедиться, создав пустой файлик с расширением .py и запустив его через вашу IDE. Физически ваша «программа» ничего не выдаст, но интерпретатор вам скажет «Process finished with exit code 0», что означает, что код был запущен и завершен без ошибок, а значит он вполне рабочий.
Под первой программой стандартно понимается программа, выводящая на экран сообщение «Hello, world!» - то есть нам нужно просто вывести текст на экран. Для вывода на экран существует встроенная функция «print». Для вызова функции нам необходимо добавить к её названию круглые скобки: print().
Можно написать в консоли print(), нажать Enter – программа отработает без ошибок, но ничего напечатано не будет. И это нормально, ведь мы не сказали функции, что именно ей нужно напечатать. Пока что мы просто заставили её сработать вхолостую, что она и сделала.
Функция print весьма лояльна к пожеланиям разработчика и готова принимать в себя и выводить на экран почти всё, что ей скажут. Иными словами, она почти всеядная. Вывести её из себя, чтобы она завершилась ошибкой можно, но сложно.
Текст в питоне – это тип данных «строка» (подробнее о них мы поговорим в следующий раз, и поверьте, там есть о чём). Строки должны быть заключены в кавычки.
Hello, world! – это не строка.
"Hello, world!" – а вот это уже строка.
Значит именно так мы и должны передать её в функцию print.
Итак,
>>> print("Hello, world!")
Hello, world!
Отлично, мы сделали это! Но, можно ещё интереснее. Например, можно записать строку в переменную, передать в функцию эту переменную и распечатается та же строка:
>>> hello = "Hello, world!"
>>> print(hello)
Hello, world!
Мне надоело здороваться со всем миром, я хочу разнообразия. Для этого я хочу сам говорить программе, с кем поздороваться. Для этого я вызову функцию ввода с клавиатуры «input».
>>> hello = "Hello, "
>>> name = input() # с клавиатуры вводим любое слово, например, Петя
>>> print(hello, name, "!")
Hello, Петя !
>>> print("Hello, " + name + "!")
Hello, Петя!
На сегодня это всё, спасибо за внимание!
Всё это, конечно же, дублируется в отдельном канале в телеге. По всем вопросам обращайтесь через Telegram.
Да, и тег моё - потому что всё написано моими руками, а не тупо понакопировано с других сайтов.
P.S. Большое спасибо всем моим подписчикам за поддержку и активность! Без вас я, возможно, не решился бы продолжать.
Ссылки на предыдущие посты:
1. Предлагаю помощь в освоении Питона