Лекция 1. Синтаксис, переменные и типы данных¶
В этой лекции — основы языка Python: как пишется код, какие у него ключевые слова и правила оформления, какие есть простые типы данных и какие операторы. Параллельно — те же темы в Go.
Основные свойства Python¶
- Динамическая типизация: тип переменной определяется значением, тип может меняться от присваивания к присваиванию.
- Регистрозависимый:
var,Var,VAR— три разные переменные. - Объектно-ориентированный: всё — объект, включая числа и функции.
- Интерпретируемый: код выполняется построчно интерпретатором (CPython), без отдельной фазы компиляции в машинный код.
Базовые свойства Go отличаются:
- Статическая типизация: тип переменной известен в момент компиляции, поменять его нельзя.
- Регистрозависимый (как Python).
- Компилируемый:
go buildсоздаёт исполняемый бинарник. - Структурный + интерфейсы (об ООП — в теме 6).
Лексика¶
Программа состоит из последовательности лексем:
- комментарии — поясняют код, интерпретатором пропускаются;
- литералы — записанные значения (
5,3.14,"строка"); - знаки пунктуации —
+,*,,, скобки; - идентификаторы — имена переменных, функций, классов;
- ключевые слова — зарезервированные имена языка.
Комментарии¶
# Однострочный — начинается с #
x = 5 # inline-комментарий, два пробела перед #
"""
Многострочный комментарий
через тройные кавычки — на самом деле это
литерал строки, который никуда не присваивается.
"""
В Go:
Комментарии должны объяснять почему что-то реализовано так, а не что делает код — код уже это показывает.
Идентификаторы¶
Правила (Python):
- состоит из букв (ASCII или Unicode), цифр,
_; - не начинается с цифры;
- регистрозависимый (
UserName ≠ username); - не совпадает с ключевым словом.
Соглашения PEP 8:
snake_caseдля переменных и функций:user_name,read_file;PascalCaseдля классов:UserAccount;UPPER_SNAKEдля констант:MAX_RETRIES = 5;- одно подчёркивание в начале (
_private) — внутреннее имя; - два подчёркивания в начале (
__name) — name mangling в классах; __double__— служебные имена Python (не пишем сами).
В Go:
-
camelCaseдля приватных,PascalCaseдля экспортируемых: -
регистр имени = модификатор видимости (заглавная = экспорт).
Ключевые слова Python¶
False None True and as assert
async await break class continue def
del elif else except finally for
from global if import in is
lambda nonlocal not or pass raise
return try while with yield match case
Имена в этом списке использовать как идентификаторы нельзя — IDE подсвечивает их цветом.
Для проверки в рантайме:
В Go список короче (25 ключевых слов): break, case, chan, const, continue, default, defer, else, fallthrough, for, func, go, goto, if, import, interface, map, package, range, return, select, struct, switch, type, var.
Синтаксис: отступы¶
Главная особенность Python — блоки кода обозначаются отступами, а не скобками.
Соглашение PEP 8: 4 пробела на уровень. Можно табы — но смешивать в одном файле нельзя.
В Go блоки — { }, отступы рекомендуются, но не обязательны:
Конец строки = конец инструкции¶
Точка с запятой не нужна. Можно разделять несколько инструкций в одной строке — но это плохой стиль:
В Go точки с запятой ставит компилятор автоматически между строками — писать их руками не нужно (и не принято).
Перенос длинной строки¶
В Python используют скобки — внутри (), [], {} строки можно продолжать без специальных символов:
Или явный перенос через \ (используется реже):
with open("/path/to/input") as input_file, \
open("/path/to/output", "w") as output_file:
output_file.write(input_file.read())
Простые типы данных¶
Целые числа (int)¶
В Python 3 целые числа неограниченной разрядности:
Операции:
| Оператор | Описание |
|---|---|
+, -, * |
сложение, вычитание, умножение |
/ |
деление (всегда float: 5 / 2 == 2.5) |
// |
целочисленное деление (5 // 2 == 2) |
% |
остаток (5 % 2 == 1) |
** |
возведение в степень (2 ** 10 == 1024) |
abs(x) |
модуль |
divmod(x, y) |
возвращает (x // y, x % y) |
pow(x, y, [z]) |
x ** y (опционально по модулю z) |
В Go целые — фиксированной разрядности:
| Тип | Размер | Диапазон |
|---|---|---|
int8 / uint8 |
1 байт | -128..127 / 0..255 |
int16 / uint16 |
2 байта | |
int32 / uint32 |
4 байта | |
int64 / uint64 |
8 байт | |
int / uint |
размер слова платформы (32 или 64 бит) | |
byte |
алиас uint8 |
|
rune |
алиас int32 (Unicode code point) |
Из-за фиксированной разрядности возможно переполнение (overflow): int8(127) + 1 == -128. Для длинной арифметики используют math/big.
Системы счисления¶
# Литералы в разных системах
0b10011 # двоичная — 19
0o23 # восьмеричная — 19
0x13 # шестнадцатеричная — 19
0x1F # 31
1_000_000 # подчёркивания как разделители (читаемее)
# Преобразование
int("19") # 19
int("10011", 2) # 19 (из двоичной)
int("FF", 16) # 255 (из hex)
bin(19) # '0b10011'
hex(255) # '0xff'
oct(8) # '0o10'
В Go то же самое:
const a = 0b10011 // 19
const b = 0o23 // 19
const c = 0x13 // 19
const d = 1_000_000
n, _ := strconv.ParseInt("FF", 16, 64) // 255
s := strconv.FormatInt(255, 16) // "ff"
Вещественные числа (float)¶
В Python float — это IEEE 754 double (64 бита).
Главная ловушка — неточность:
Для финансовых расчётов используют decimal.Decimal:
from decimal import Decimal
Decimal("0.1") + Decimal("0.1") + Decimal("0.1")
# Decimal('0.3') — точно
Или fractions.Fraction для точной арифметики дробей:
В Go тоже есть float32 / float64 с теми же проблемами; для точности — math/big.Float или decimal-пакеты из экосистемы (shopspring/decimal).
Комплексные числа¶
В Python они встроены:
В Go: complex64 / complex128:
Строки (str)¶
В Python 3 строки — Unicode. Литералы:
s1 = 'apostrophe'
s2 = "quotes"
s3 = '''многострочная
строка'''
s4 = """тоже многострочная"""
# Сырая строка — экранирование отключено
path = r"C:\Users\new\folder" # обратные слэши не интерпретируются
# Байтовая строка
b = b"bytes" # тип bytes, не str
# F-строка с интерполяцией (см. лекцию 4 темы 2)
name = "Аня"
greeting = f"Привет, {name}!"
Экранированные последовательности внутри обычных строк:
| Последовательность | Значение |
|---|---|
\n |
перевод строки |
\t |
табуляция |
\r |
возврат каретки |
\\ |
обратный слэш |
\' |
апостроф |
\" |
кавычка |
\xhh |
байт с кодом hh |
\uhhhh |
Unicode символ (16-bit) |
\Uhhhhhhhh |
Unicode символ (32-bit) |
Базовые операции:
"spam" + "eggs" # "spameggs" — конкатенация
"ha" * 3 # "hahaha" — повторение
len("spam") # 4
"spam"[0] # 's' — индексация
"spam"[-1] # 'm' — отрицательные индексы от конца
"spam"[1:3] # 'pa' — срез
"spam"[::-1] # 'maps' — реверс
В Go строки — это immutable []byte (UTF-8):
s := "Привет"
fmt.Println(len(s)) // 12 — БАЙТ! Не символов.
fmt.Println(s[0]) // 208 — байт, не код символа.
// Для итерации по «символам» (рунам) — for range:
for i, r := range s {
fmt.Printf("%d: %c\n", i, r)
}
Это важное отличие — подробнее в лекции 4 о кодировках.
Булевы значения¶
flag = True
empty = False
# Truthy / falsy — что считается ложью при if:
# False, None, 0, 0.0, "", [], (), {}, set() — falsy
# Всё остальное — truthy
if []: # не выполнится — пустой список falsy
print("never")
if [0]: # выполнится — непустой список truthy
print("yes")
В Go bool — отдельный тип, 0/nil/пустые строки не считаются ложью:
var x int
if x { // ОШИБКА компиляции: non-bool x used as if condition
...
}
if x != 0 { // правильно
...
}
None¶
Специальное значение — «ничего», аналог null в других языках:
В Go nil похож, но допустим только для указателей, слайсов, мап, каналов, функций, интерфейсов:
Объявление переменных¶
В Python переменные объявляются простым присваиванием:
x = 5
name = "Аня"
items = [1, 2, 3]
# Множественное присваивание
a, b, c = 1, 2, 3
# Swap без временной переменной
a, b = b, a
С Python 3.6 можно (и желательно) добавлять аннотации типов:
В Go объявление более строгое:
// Полная форма
var x int = 5
var name string = "Аня"
// С выводом типа
var x = 5
var name = "Аня"
// Краткая форма (только внутри функций)
x := 5
name := "Аня"
// Множественное
a, b, c := 1, 2, 3
a, b = b, a
Не использованная переменная в Go — ошибка компиляции. В Python — ничего не происходит (только linter может предупредить).
Операторы¶
Арифметические¶
| Оператор | Python | Go | Пример |
|---|---|---|---|
| Сложение | + |
+ |
2 + 3 |
| Вычитание | - |
- |
5 - 2 |
| Умножение | * |
* |
4 * 3 |
| Деление | / (всегда float) |
/ (тип сохраняется) |
5 / 2 → 2.5 (Py), 2 (Go, если int) |
| Целочисленное деление | // |
/ для int |
5 // 2 → 2 |
| Остаток | % |
% |
5 % 2 → 1 |
| Степень | ** |
нет (есть math.Pow) |
2 ** 10 |
Сравнения¶
| Оператор | Значение |
|---|---|
== |
равно |
!= |
не равно |
< / > |
меньше / больше |
<= / >= |
меньше или равно / больше или равно |
is / is not |
тождество (один и тот же объект) |
in / not in |
принадлежность (для строк, списков, словарей и т.д.) |
== сравнивает значения, is — идентичность объектов (id(a) == id(b)):
a = [1, 2, 3]
b = [1, 2, 3]
c = a
a == b # True (значения равны)
a is b # False (разные объекты)
a is c # True (один и тот же объект)
is None — каноническая проверка на None. Никогда не используйте == None.
Цепочки сравнений¶
В Python (но не в Go) можно писать:
Логические¶
| Python | Go | Значение |
|---|---|---|
and |
&& |
логическое И |
or |
\|\| |
логическое ИЛИ |
not |
! |
логическое НЕ |
and и or в Python — короткозамкнутые и возвращают значение, а не bool:
x = None or "default" # "default"
y = 0 or "x" # "x" — 0 falsy
z = "a" and "b" # "b" — оба truthy, вернёт последний
В Go &&/|| тоже короткозамкнутые, но всегда возвращают bool.
Побитовые¶
| Оператор | Значение |
|---|---|
& |
побитовое И |
\| |
побитовое ИЛИ |
^ |
побитовое исключающее ИЛИ (XOR) |
~ |
побитовая инверсия |
<< |
сдвиг влево |
>> |
сдвиг вправо |
a = 0b1100 # 12
b = 0b1010 # 10
a & b # 0b1000 — 8
a | b # 0b1110 — 14
a ^ b # 0b0110 — 6
~a # -13 (инверсия + знаковый бит)
a << 2 # 48
a >> 1 # 6
Аналогично в Go (но без оператора ~ — используют ^x):
Присваивание с операцией¶
| Оператор |
|---|
+=, -=, *=, /=, //=, %=, **= |
&=, \|=, ^=, <<=, >>= |
В Python нет ++ и --. В Go есть, но только как statement, не как выражение:
Walrus := (Python 3.8+)¶
«Моржовый оператор» — присваивание внутри выражения:
В Go := — это вообще объявление переменной с выводом типа (не путать).
Приоритет операторов¶
От наивысшего к наинизшему (Python):
**+x,-x,~x(унарные)*,/,//,%+,-(бинарные)<<,>>&^|==,!=,<,>,<=,>=,is,is not,in,not innotandor
При сомнениях — ставьте скобки, это короче, чем потом разбираться с багом.
Кодировка исходника¶
По стандарту PEP 3120 файлы Python — UTF-8. Это значит, что в коде можно писать русские идентификаторы (хотя это плохой стиль), комментарии на любом языке, строки с любыми Unicode-символами:
В Go исходники тоже UTF-8.
Структура файла¶
Типичный Python-файл:
#!/usr/bin/env python3
"""Краткое описание модуля.
Подробное описание, если нужно.
"""
from __future__ import annotations # для современного типизирования
import os
import sys
from pathlib import Path
import requests # сторонние пакеты
from .utils import helper # локальные импорты
MAX_RETRIES = 5
DEFAULT_TIMEOUT = 30
def main() -> int:
"""Точка входа."""
return 0
if __name__ == "__main__":
sys.exit(main())
PEP 8 рекомендует:
- две пустые строки между функциями верхнего уровня и классами;
- одна пустая строка между методами класса;
- импорты — в начале файла, в трёх группах (stdlib, сторонние, локальные).
Сравнение Python ↔ Go¶
| Аспект | Python | Go |
|---|---|---|
| Типизация | динамическая | статическая |
| Аннотации типов | опциональны (x: int) |
обязательны |
Размер int |
неограничен | фиксированный (32/64) |
Деление / |
всегда float | целое если оба int |
| Логические операторы | and/or/not |
&&/||/! |
| Возвращают bool? | or/and — нет, значение |
да |
++ / -- |
нет | есть (только statement) |
| Цепочки сравнений | 0 < x < 100 |
нет |
Walrus := |
да | := — объявление переменной |
| Блоки | отступы | { } |
| Точки с запятой | не нужны | вставляет компилятор |
| Неиспользуемая переменная | warning | ошибка компиляции |
Контрольные вопросы¶
- Чем динамическая типизация Python отличается от статической в Go? В чём плюсы и минусы каждой?
- Почему в Python
0.1 + 0.1 + 0.1 != 0.3и как этого избежать в финансовых расчётах? - В чём разница между
==иis? Когда применять каждый? - Что делает
orиandв Python — возвращают bool или значение? Приведите пример. - Почему
n++в Python не работает, и зачем это сделано? - Какие есть способы записать число 19 как целочисленный литерал?
- Чем строка в Python отличается от строки в Go по внутреннему представлению?
- Какое значение имеет walrus-оператор
:=в Python и почему его не было в более ранних версиях? - Что такое PEP 8 и зачем разработчику его читать?
- В чём смысл
if __name__ == "__main__":— и есть ли аналог в Go?