Как сделать неизменяемую переменную? [закрыт]
Закрыт. Этот вопрос необходимо уточнить или дополнить подробностями. Ответы на него в данный момент не принимаются.
Хотите улучшить этот вопрос? Добавьте больше подробностей и уточните проблему, отредактировав это сообщение.
Закрыт 5 лет назад .
У меня есть переменная. В ходе программы она меняться не должна. Хочу сделать нечто вроде константы, которую ни намеренно, ни случайно изменить будет нельзя. (хотелось бы также, чтобы от этого она меньше занимала места). Для списков есть кортежи (неизменяемые списки), а вот для переменных что можно использовать? Можно ли сделать неизменяемый словарь?
Отслеживать
New Python Programmist
задан 6 июл 2018 в 7:46
New Python Programmist New Python Programmist
318 1 1 золотой знак 6 6 серебряных знаков 18 18 бронзовых знаков
В ходе программы она меняться не должна — ну так не меняйте ее в своей программе.
6 июл 2018 в 7:55
Переменную можно сделать такой только если она будет в объекте/классе. Просто так нельзя запретить ей присваивать значения. Добавьте пример вашего кода, который показывает когда не нужно менять значение
6 июл 2018 в 8:04
@gil9red Пример кода тут не уместен. Я ввел переменные-параметры, чтобы если вдруг надо будет их изменить, не лазить по всех программе и не менять эти параметры, а просто поменять их вначале кода. Но так как их будет много, хотелось бы уменьшить в размере и сделать неизменяемыми.
6 июл 2018 в 9:25
Это вы думаете что неуместен, то, что вы сказали это интересная инфа. Получается, вам нужно константы, которые нужно объявить в одном месте и не позволить их менять?
6 июл 2018 в 9:28
в питоне нет констант самих по себе, разве что указание что переменные в верхнем регистре ими типо считаются, но никто не помешает их менять, но IDE по идеи могут «ругаться» на такие вещи
6 июл 2018 в 9:34
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Можно сделать через NamedTuple:
from typing import NamedTuple class Const(NamedTuple): VALUE_1 = "ABC" VALUE_2 = 1000 CONST = Const() print(CONST.VALUE_1) # ABC print(CONST.VALUE_2) # 1000 print() CONST.VALUE_1 = 100 # AttributeError: 'Const' object attribute 'VALUE_1' is read-only print(CONST.VALUE_1)
Тоже самое, но вручную:
class Const: VALUE_1 = property(fget=lambda self: "ABC") VALUE_2 = property(fget=lambda self: 1000) CONST = Const() print(CONST.VALUE_1) # ABC print(CONST.VALUE_2) # 1000 print() CONST.VALUE_1 = 100 # AttributeError: can't set attribute print(CONST.VALUE_1)
Отслеживать
ответ дан 6 июл 2018 в 9:44
77.1k 6 6 золотых знаков 57 57 серебряных знаков 123 123 бронзовых знака
интересное решение! я раньше такого не встречал.
6 июл 2018 в 12:12
это по сути то же самое чего хочет избежать автор, только вид сбоку, поскольку в CONST все равно можно записать всё что угодно.
28 июн 2021 в 9:23
Если Вы хотите, чтобы в переменную не могли записать значения, то можно, например, сделать её закрытым членом класса, а в открытых методах реализовать доступ к этой переменной.
class Foo: def __init__(self, bar): self.__bar = bar def get(self): return self.__bar biz = Foo(42) >>> biz.get() 42 >>> biz.__bar AttributeError: 'Foo' object has no attribute '__bar'
Также думаю стоит сказать немного про типы данных в Python. В Python все типы данных делятся на изменяемые (mutable) и неизменяемые (unmutable). К изменяемым относятся:
- списки (list)
- множества (set)
- словари (dict)
- числа (int, float, double)
- строки (str)
- кортежи (tuple)
Рассмотрим случай с неизменяемыми типами. Когда Вы инициализируете переменную, например
a = 42
то в памяти создаётся объект, содержащий данное значение и ссылка на объект присваивается переменной a. Адрес объекта можно узнать с помощью функции id
>>> id(a) 1848305056
Если теперь мы изменим значение переменной
a = a + 1
то, она будет хранить ссылку на другой объект в области памяти:
>>> id(a) 1848305072
Но если мы попробуем проделать такие же операции с каким-либо изменяемым типом, например, списком, то увидим, что переменная продолжает хранить ссылку на тот же объект:
>>> spam = [1, 2, 3] >>> id(spam) 48769064 >>> spam.append(4) >>> id(spam) 48769064
Кружок по программированию, 2017-2018
В питоне всё, что можно положить в переменную, является объектом. А сама переменная является ссылкой на этот объект.
a = 1
a = 2
b = a
Объекты «живут» в своём мире независимо от переменных, а переменные — один из способов обратиться извне к миру объектов. Так в примере выше объект Целое-число-1 продолжает жить даже после того, как мы поменяли переменную a .
Объекты в питоне бывают двух значительно отличающихся сортов: изменяемые (mutable) и неизменяемые (immutable). Неизменяемыми являются целые и действительные числа ( int , float ), строки ( str ), последовательности байтов (бинарные данные, bytes ), а также кортежи, все элементы которых неизменяемы ( tuple ). Напротив, списки ( list ), словари ( dict ) и множества ( set ) являются изменяемыми. Что всё это значит? Неизменяемые объекты обладают замечательным свойством: они не могут измениться в результате работы программы. Если некоторая переменная «смотрит» на неизменяемый объект, то можно быть уверенным, что её значение не поменяется, если только ей не присвоить ссылку на другой объект. Например, в результате выполнения кода a = 1 в переменной a хранится ссылка на объект Целое-число-1, и что бы не происходило с другими переменными, a будет всегда равно 1.
Изменяемые объекты не обладают таким постоянством. Они скорее напоминают контейнер для хранения: контейнер остаётся на месте, а вот содержимое может сильно измениться. Что всё это для нас значит? Ну, например, если передать список в «чужую» функцию, то его могут испортить до неузнаваемости:
def list_destroyer(l): l[0] = 'ha' l[1] = 'hi' l[2] = 'ho' a = [1, 2, 3] print(a) # -> [1, 2, 3] list_destroyer(a) print(a) # -> ['ha', 'hi', 'ho']
Здесь можно поиграться с этим примером и посмотреть на то, что происходит с ссылками:
Обратите внимание, при передаче списка в функцию можно модифицировать элементы списка при помощи присваиваний A[i] = . , методов pop , append , insert и т.д. Это не является модификацией списка, как целого, т.е. имя A по-прежнему указывает на тот же список, при этом содержимое списка меняется.
Не всегда такое поведение является ожидаемым:
a = [1, 2, 3] b = a b.insert(0, 'ops') b.pop() print(b) # -> ['ops', 1, 2] print(a) # -> ['ops', 1, 2]
Присваивание b = a не создает новый список, а всего лишь делает b ссылкой на уже существующий список. Для того, чтобы «сохранить» старый список, нужно создавать его копию:
a = [1, 2, 3] b = a.copy() b.insert(0, 'ops') b.pop() print(b) # -> ['ops', 1, 2] print(a) # -> [1, 2, 3]
# Изменяемые и неизменяемые объекты в Python
Все значения в Python это объекты. Объекты делятся на изменяемые и неизменяемые.

Каждая переменная ссылается на экземпляр объекта. При создании, объекту присваивается уникальный идентификатор (object id) и тип объекта. Тип объекта не меняется после создания, но может изменится состояние объекта. Изменяемые объекты меняют своё состояние после создания, а неизменяемые сохраняются в том виде в котором были созданы.
Встроенные неизменяемые типы: int , float , bool , str , tuple , unicode . Встроенные изменяемые типы list , set , dict . Пользовательские классы обычно изменяемы. Для имитирования неизменяемости переопределите методы изменения и удаления значений чтобы они возвращали исключение.

Чтобы узнать ссылается ли переменная на изменяемый или неизменяемый рассмотрим подробнее функции id() и type() .
# Функции id и type
Встроенная функция id() возвращает числовой идентификатор объекта. Обычно это число соответствует месту нахождения объекта в памяти, однако это относится к особенностям реализации интерпретатора Python и зависит от платформы. Оператор is сравнивает идентификаторы двух объектов.
Встроенная функция type() возвращает тип объекта. Рассмотрим два примера.
# Пример 1 >>> x = "Holberton" >>> y = "Holberton" >>> id(x) 140135852055856 >>> id(y) 140135852055856 >>> print(x is y) '''comparing the types''' True
1
2
3
4
5
6
7
8
9
# Пример 2 >>> a = 50 >>> type(a) class: ‘int’> >>> b = "Holberton" >>> type(b) class: 'string'>
1
2
3
4
5
6
7
Проверим, с помощью этих функций, какие типы являются изменяемыми, а какие нет.
# Изменяемые и неизменяемые объекты
Изменяемые объекты могут изменять свое состояние или содержимое, неизменяемые не могут. Практический пример проверки на изменяемость:
x = 10 y = x
Мы создали объект типа int . Идентификаторы x и y ссылаются на один объект.
id(x) == id(y) id(y) == id(10)
Выполним простую операцию:
x = x + 1
Определяем типы данных в Python. Изменяемые и неизменяемые типы
В этой статье мы поговорим, как определить тип переменной в Python. Заодно, расскажем, почему одни переменные считают изменяемыми, а другие нет. И какие тут существуют тонкости, связанные с терминологией.
В некоторых случаях нужно определить тип данных переменной в Python. Проверить, к какому типу принадлежит та или иная переменная, можно посредством функции type:
>>> a = 10 >>> b = [1,2,3] >>> type(a) == int True >>> type(b) == list True >>> type(a) == float FalseТакже мы можем определить тип данных переменной в Python посредством функции isinstance() :
>>> isinstance(a,int) True >>> isinstance(b,list) True >>> isinstance(b,tuple) False >>> c = (4,5,6) >>> isinstance(c,tuple) TrueЗдесь стоит обратить внимание, что isinstance() в отличие от type даёт возможность проверять тип данных на принадлежность хотя бы одному типу из кортежа, который передан в качестве 2-го аргумента:
>>> isinstance(a,(float, int, str)) True >>> isinstance(a,(list, tuple, dict)) FalseТакже следует упомянуть и другое, не менее значимое достоинство isinstance() — поддержка наследования. Для isinstance() экземпляр производного класса является экземпляром его базового класса:
>>> class A (list): . pass . >>> a = A() >>> type(a) == list False >>> type(a) == A True >>> isinstance(a,A) True >>> isinstance(a,list) TrueПроверка типов
Есть и ещё кое-что: во время преобразования типов данных переменных в Python нередко возникают следующие ошибки:
In [1]: int('a') ------------------------------------------------------ ValueError Traceback (most recent call last) in () ----> 1 int('a') ValueError: invalid literal for int() with base 10: 'a'Впрочем, ошибка является вполне логичной, ведь мы пробуем преобразовать в десятичный формат строку „a“.
Понятно, что на практике вы с такой ошибкой не столкнётесь. Однако бывает, что надо, к примеру, пробежаться по списку строк, преобразовав в числа те, которые уже содержат числа. Чтобы ошибки избежать, нужно сначала определить, с каким типом переменных мы имеем дело.
В Python соответствующие средства, конечно, имеются. К примеру, используя метод isdigit() , мы определим, состоит ли наша строка из одних только цифр:
In [2]: "a".isdigit() Out[2]: False In [3]: "a10".isdigit() Out[3]: False In [4]: "10".isdigit() Out[4]: TrueЕсть и ещё один метод — isalpha() . Он проверит, состоит ли наша строка из одних лишь букв:
In [7]: "a".isalpha() Out[7]: True In [8]: "a100".isalpha() Out[8]: False In [9]: "a-- ".isalpha() Out[9]: False In [10]: "a ".isalpha() Out[10]: FalseА вот isalnum() определит, состоит ли наша строка из цифр или букв:
In [11]: "a".isalnum() Out[1]: True In [12]: "a10".isalnum() Out[12]: TrueНо давайте снова вернёмся к упомянутой в начале статьи функции type. Порой, в зависимости от результата, функция или библиотека может выводить различные типы объектов. К примеру, если объект только один, то возвращается строка, а если их несколько, то нам возвращается кортеж. Мы же хотим построить ход программы по иному, с учётом того, что было возвращено: строка либо кортеж. И здесь как раз и пригодится type:
In [13]: type("string") Out[13]: str In [14]: type("string") is str Out[14]: TrueТо же самое и с кортежем, и с иными типами данных:
In [15]: type((1,2,3)) Out[15]: tuple In [16]: type((1,2,3)) is tuple Out[16]: True In [17]: type((1,2,3)) is list Out[17]: FalseНеизменяемые и изменяемые данные в Python
Считается, что все типы данных в языке программирования Python можно отнести к любой из двух категорий: — изменяемые (mutable); — неизменяемые (unmutable).
И многие из предопределённых типов являются типами неизменяемых объектов: — символьные строки (class 'str'); — числовые данные (int, float, complex); — кортежи (tuple).
Что касается других типов, то они определены как изменяемые: — множества (set), — списки (list), — словари (dict).
Кроме того, вновь определяемые пользователем классы (типы) тоже можно определить как изменяемые или неизменяемые. И вообще, изменяемость объектов какого-нибудь типа считается принципиально значимой характеристикой, которая определяет, способен ли объект такого типа выступать в виде ключа для словарей (dict) либо нет.
И тут есть один интересный нюанс, связанный с самой терминологией «изменяемый-неизменяемый» (именно она используется в русскоязычном переводе). На самом деле, такой вариант названия не совсем удачный, он вносит неоднозначность. Здесь скорее бы подошёл термин «мутирующий-немутирующий», т. к. он лучше отображает суть происходящего. А суть заключается в том, способен ли объект данного типа менять свою структурность?
К примеру строка s = 'abcdef' относится к неизменяемому типу, ведь в Python нельзя (это вам не C/C++) поменять какой-нибудь одиночный символ в строке, допустим, через s[ 2 ] = 'z', и это не говоря о том, чтобы вставить символ внутрь строки. Однако мы можем сделать s = s[ :2 ] + 'z' = s[ 3: ] и получить в итоге нужную строку 'abzdef', но это будет абсолютно другая строка, размещённая по абсолютно другому адресу в памяти, то есть s — переустановленная ссылка на новую строку. Однако поменять строку либо её длину (структурность) по текущей ссылке нельзя. В этом, как раз, и заключается неизменяемость объекта — неконстантность, ведь его значение поменять можно, однако это уже будет ссылка на другой объект с новым значением.
На этом всё, если хотите прокачать навыки Python-программирования "по-врослому", записывайтесь на курсы в OTUS:
При подготовке статьи использовались следующие материалы:
- Типизация в Python. Типы данных, переменные;
- Синтаксис Python: type, isinstance — определяем тип переменной;
- Проверка типов;
- Тонкости использования языка Python: типы данных.