5.02. Переменные
Переменные
Важно понять, что в большинстве языков программирования переменная является «ящиком», в котором хранится значение. В Python — это ссылка на объект. То есть, переменная лишь имя, которое указывает (ссылается) на объект в памяти.
Когда мы пишем:
x = 42
…то мы не сохраняем 42 в x, а говорим интерпретатору создать объект типа int со значением 42 и связать с ним имя x. То есть, переменная не имеет типа, тип есть у объекта, на который она ссылается. Одна и та же переменная может ссылаться на объекты разных типов, и несколько переменных могут ссылаться на один и тот же объект.
a = [1, 2, 3]
b = a # b теперь ссылается на тот же список
b.append(4)
print(a) # [1, 2, 3, 4] — изменился и a!
a и b - две ссылки на один и тот же изменяемый объект.
В Python нет ключевых слов для объявления переменных. Нет ничего вроде var, let, const, int, str и т.п.
Объявление переменной происходит в момент первого присваивания:
name = "Алексей" # переменная создана
age = 30 # ещё одна
is_student = False # и ещё одна
Никаких предварительных объявлений — интерпретатор сам добавляет имя в текущее пространство имён.
Литерал — это способ записи значения прямо в коде. Они используются при присвоении переменных:
number = 42 # 42 — целочисленный литерал
pi = 3.14159 # литерал float
text = "Hello" # строковый литерал
flag = True # булев литерал
data = [1, 2, 3] # литерал списка
config = {"db": "prod"} # литерал словаря
point = (10, 20) # литерал кортежа
binary = b"hello" # байтовый литерал
Литералы — это «сырые» значения, которые создаются как объекты в памяти, и на них можно ссылаться через переменные. Как уже упоминалось, Python — динамически типизированный язык. Это означает, что тип переменной определяется во время выполнения, и она может менять тип в процессе работы программы.
value = 100 # int
value = "текст" # str
value = [1, 2] # list
Поскольку переменные — это ссылки, важно различать равенство значений (==) и идентичность объекта (is).
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True — значения одинаковые
print(a is b) # False — разные объекты в памяти
print(a is c) # True — ссылаются на один объект
Функция id() показывает уникальный идентификатор объекта:
print(id(a)) # например, 140234567890123
print(id(b)) # другой адрес
print(id(c)) # такой же, как у a
Области видимости переменных (Scopes). Python следует правилу LEGB, определяющему, где искать переменную при обращении к ней:
- L - Local, внутри функции;
- E - Enclosing - в обрамляющих функциях (замыканиях);
- G - Global - на уровне модуля;
- B - Built-in - встроенные имена (len, print, True).
Пример:
x = "глобальный"
def outer():
x = "обрамляющий"
def inner():
x = "локальный"
print(x) # -> "локальный"
inner()
print(x) # -> "обрамляющий"
outer()
print(x) # -> "глобальный"
Локальные переменные создаются внутри функции и доступны только внутри неё.
def greet():
message = "Привет!" # локальная переменная
print(message)
greet()
# print(message) # Ошибка! NameError
Глобальные переменные объявлены на уровне модуля (вне функций/классов):
counter = 0
def increment():
global counter # говорим, что хотим использовать глобальную переменную
counter += 1
increment()
print(counter) # 1
Без global попытка изменить counter создаст локальную переменную с тем же именем.
Аналогично — nonlocal для доступа к переменным из обрамляющей области:
def outer():
x = "внешний"
def inner():
nonlocal x
x = "изменили изнутри"
inner()
print(x) # "изменили изнутри"
Поэтому в Python, как и в JavaScript, есть лексическое окружение и замыкания (closures).
Замыкание — функция, которая запоминает значения из обрамляющей области, даже после завершения этой области.
def make_multiplier(n):
def multiplier(x):
return x * n # n — из обрамляющей области
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
Здесь multiplier — замыкание, которое «захватывает» значение n. Атрибут .__closure__ позволяет посмотреть захваченные переменные:
print(double.__closure__[0].cell_contents) # 2
Это мощный механизм, используемый в декораторах, каррировании и фабриках функций. Но о функциях позже.
Python поддерживает соглашения об именах, влияющих на поведение. Мы уже о них говорили - это _ для временной переменной, __name__ для специальных переменных модуля.
Кроме этого, в Python нет настоящих констант, но есть соглашение - имена в верхнем регистре считаются константами:
PI = 3.14159
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
Это не защищает от изменения, но сигнализирует, что значение не должно меняться.
PI = 3 # технически возможно, но плохо по стилю
Для настоящей защиты можно использовать модуль types.MappingProxyType (неизменяемый словарь), классы с @property или сторонние библиотеки вроде const.
from types import MappingProxyType
CONFIG = {
'API_URL': 'https://api.example.com',
'TIMEOUT': 30
}
READONLY_CONFIG = MappingProxyType(CONFIG)
# READONLY_CONFIG['API_URL'] = '...' # Ошибка!
И разумеется, нельзя использовать ключевые слова как имена:
# if = 5 # SyntaxError
# class = "A" # SyntaxError
Список ключевых слов можно получить:
import keyword
print(keyword.kwlist)
# ['False', 'None', 'True', 'and', 'as', 'assert', ...]
В именах разрешены буквы, цифры, подчёркивание. Начинать следует с буквы или нижнего подчёркивания (_), но не с цифры. А юникод-символы лучше избегать, хотя можно.
Стиль написания в Python именно snake_case, стандарт PEP 8, а имена описывать лучше так: user_name, total_price, is_active.
Имеется множественное присваивание:
a, b = 1, 2
x, y, z = "X", "Y", "Z"
С распаковкой:
first, *rest = [1, 2, 3, 4]
# first = 1, rest = [2, 3, 4]
Обмен значениями:
a, b = b, a # без временной переменной
Удаление переменной:
x = 100
del x # имя x удаляется из пространства имён
# print(x) # NameError
del удаляет ссылку, а не объект. Объект удалится сборщиком мусора, когда на него не останется ссылок.
Проверка существования переменной:
if 'my_var' in globals():
print("Есть такая глобальная переменная")
# Или через getattr, hasattr — особенно для объектов
Python использует пространства имён — словари, где ключи — имена, значения — объекты.
Виды:
- Локальное — locals()
- Глобальное — globals()
- Встроенное — dir(builtins)
x = 10
def func():
y = 20
print(locals()) # {'y': 20}
print(globals()['x']) # 10
func()
Эти функции полезны для метапрограммирования, но редко нужны в обычном коде.