Незмінні типи проти змінних типів


186

Мене бентежить те, що таке непорушний тип. Я знаю, що floatоб’єкт вважається непорушним, з таким типом прикладу з моєї книги:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

Чи вважається це незмінним через структуру / ієрархію класів? Значення floatзнаходиться у верхній частині класу і є його власним викликом методу. Схожий на цей тип прикладу (навіть якщо моя книга говорить про dictзмінне):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Тоді як щось змінне має методи всередині класу, з таким типом прикладу:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

Крім того, для останнього class(SortedKeyDict_a), якщо я передаю йому такий тип набору:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

не викликаючи exampleметод, він повертає словник. SortedKeyDictЗ __new__прапорами це помилкою. Я спробував передавати цілі числа до RoundFloatкласу, __new__і це позначило відсутність помилок.


Ви також можете ознайомитись із призначенням списку за допомогою [:] та python, коли використовувати copy.copy, на що я також відповів для отримання додаткової інформації про зміни.
agf

Відповіді:


232

Що? Поплавці незмінні? Але я не можу цього зробити

x = 5.0
x += 7.0
print x # 12.0

Хіба це не "мут" х?

Ви погоджуєтесь, що рядки незмінні, правда? Але ви можете зробити те ж саме.

s = 'foo'
s += 'bar'
print s # foobar

Значення змінної змінюється, але воно змінюється, змінюючи те, на що змінна відноситься. Тип, що змінюється, може змінюватися таким чином, і він також може змінюватися "на місці".

Ось різниця.

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

Конкретні приклади

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]

5
Те, що ви пояснюєте, означає для мене: змінні змінні передаються посиланням, незмінні змінні передаються за значенням. Це правильно ?
Лоренц Мейєр

17
Майже, але не зовсім. Технічно всі змінні передаються за допомогою посилання в Python, але мають семантику, більш схожу на пропуск за значенням у C. Контрприклад для вашої аналогії - якщо ви робите def f(my_list): my_list = [1, 2, 3]. При передачі посилання в C значення аргументу може змінюватися, викликаючи цю функцію. У Python ця функція нічого не робить. def f(my_list): my_list[:] = [1, 2, 3]зробив би щось.
ранкова

6
Типи змінних можна змінити на місці. Незмінні типи не можуть змінюватися на місці. Ось так пітон бачить світ. Це незалежно від того, як змінні передаються функції.
ychaouche

13
Ключова відмінність семантики Python від семантики проходження посилання C ++ полягає в тому, що призначення не є мутацією в Python, а в C ++. (Але, звичайно, це ускладнюється тим, що розширене призначення, як a += bіноді, є мутацією. І той факт, що присвоєння частині більшого об'єкта іноді означає мутацію цього більшого об'єкта, просто ніколи не мутацію частини - наприклад, a[0] = bне мутує a[0], але це, ймовірно, мутує a... Ось чому, можливо, краще не намагатися ставити речі в термін C ++, а натомість просто описати, що робить Python своїми словами ...)
abarnert

2
Я визнав цю відповідь оманливою, оскільки в ній не використовується id (), що важливо для розуміння того, що означає незмінне.
pawel_winzig

185

Ви повинні розуміти, що Python представляє всі свої дані як об’єкти. Деякі з таких об'єктів, як списки та словники, можуть змінюватися, тобто ви можете змінювати їхній вміст, не змінюючи їх ідентичності. Інші об'єкти, такі як цілі числа, плавці, рядки та кортежі - це об'єкти, які неможливо змінити. Простий спосіб зрозуміти це, якщо ви подивитеся на ідентифікатор об’єктів.

Нижче ви бачите рядок, непорушний. Ви не можете змінити його зміст. TypeErrorЯкщо ви спробуєте змінити це, це зробить a . Крім того, якщо ми призначимо новий контент, замість зміненого вмісту створюється новий об'єкт.

>>> s = "abc"
>>>id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>>id(s)
4800100
>>> s += "uvw"
>>>id(s)
4800500

Ви можете зробити це зі списком, і він не змінить ідентичність об'єктів

>>> i = [1,2,3]
>>>id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

Щоб дізнатися більше про модель даних Python, ви можете ознайомитись із посиланням на мову Python:


4
+1 Для посилання на документи Python. Однак у мене пройшло певний час, поки я не зрозумів, що сьогодні вам потрібно розмежувати вісімнадцять пітонів 2 і 3 - я оновив відповідь, щоб підкреслити це.
Бенджамін

107

Поширений незмінний тип:

  1. номера: int(), float(),complex()
  2. незмінні послідовності: str(), tuple(), frozenset(),bytes()

Поширений тип змінних (майже все інше):

  1. змінювані послідовності: list(),bytearray()
  2. тип набору: set()
  3. тип відображення: dict()
  4. класи, екземпляри класів
  5. тощо.

Один трюк, щоб швидко перевірити, чи є тип змінним чи ні, - це використовувати id()вбудовану функцію.

Приклади, використовуючи ціле число,

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

за допомогою списку,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)

11
Добре пояснено. Концепція перевірки сподобалась id(). +1.
Параг Тяги

4
Насправді використання id()тут вводить в оману. Даний об’єкт завжди матиме той самий ідентифікатор протягом свого життя, але різні об’єкти, які існують у різний час, можуть мати однаковий ідентифікатор через збирання сміття.
серпень

37

Перш за все, чи клас має методи, чи структура класу, це не має нічого спільного із змінністю.

ints і floats незмінні . Якщо я це роблю

a = 1
a += 5

Він вказує ім'я aна 1де - то в пам'яті на першій лінії. У другому рядку з'ясовується, що 1, додає 5, отримує 6, а потім вказує aна це 6в пам'яті - він жодним чином не змінив значення 1a 6на. Ця ж логіка застосовується до наступних прикладів, використовуючи інші незмінні типи:

b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')

Для типів, що змінюються , я можу зробити те, що повністю змінить значення, де воно зберігається в пам'яті . З:

d = [1, 2, 3]

Я створив список місць розташування 1, 2і 3в пам'яті. Якщо я тоді

e = d

Я просто вказую eна ті саміlist d моменти. Я можу тоді:

e += [4, 5]

І список, на який вказуються eі dпункти, і пункти на, також буде оновлено, щоб вони також містили місця 4та 5пам'ять.

Якщо я повернусь до непорушного типу і роблю це за допомогою tuple:

f = (1, 2, 3)
g = f
g += (4, 5)

Тоді fще лише вказує на оригіналtuple - ви вказали gна зовсім новеtuple .

Тепер, з вашим прикладом

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Куди ти проходиш

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

(Який є tupleз tuples) , як val, ви отримуєте повідомлення про помилку , тому що tupleїй немає .clear()методу - ви повинні пройти , dict(d)як valдля нього , щоб працювати, в цьому випадку ви отримаєте порожній SortedKeyDictв результаті.


2
Це дуже хороше пояснення. Полюбило це питання і багато цікавих (нових) перспектив для його пояснення.
Не вдалося вченому

25

Якщо ви приїжджаєте до Python з іншої мови (крім однієї, яка дуже схожа на Python, як Ruby), і наполягаєте на тому, щоб зрозуміти її з точки зору іншої мови, тут люди зазвичай плутаються:

>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!

У Python призначення не є мутацією в Python.

Якщо ви пишете на C ++, ви a = 2телефонуєте a.operator=(2), що буде мутувати об'єкт, що зберігається a. (І якщо в ньому не було жодного об'єкта a, це помилка.)

У Python a = 2нічого не робить із того, що було збережено a; це просто означає, що 2тепер aзамість цього зберігається . (І якщо в ньому не було жодного об'єкта a, це добре.)


Зрештою, це частина ще глибшого розрізнення.

Змінна в такій мові, як C ++ - це введене місце в пам'яті. Якщо aє int, це означає, що десь 4 байти, про які знає компілятор, слід інтерпретувати як int. Отже, коли ви робите a = 2це, він змінює те, що зберігається в цих 4 байтах пам'яті від 0, 0, 0, 1до 0, 0, 0, 2. Якщо є інша змінна int деінде, у неї є свої 4 байти.

Змінна такою мовою, як Python - це ім'я для об'єкта, який має власне життя. Існує об'єкт для номера 1та ще один об'єкт для числа 2. І aце не 4 байти пам'яті, які представлені у вигляді int, це лише ім'я, яке вказує на 1об’єкт. Немає сенсу a = 2перетворювати число 1 на число 2 (це дало б будь-якому програмісту Python занадто велику силу для зміни фундаментальних функцій Всесвіту); те, що він робить замість цього, просто змусити aзабути 1об’єкт і вказати на 2нього.


Отже, якщо призначення не є мутацією, що таке мутація?

  • Виклик методу, документально підтвердженого для мутації, як a.append(b). (Зауважте, що ці методи майже завжди повертаються None). У змінних типах немає таких методів, як правило, вони змінюються.
  • Присвоєння частини об’єкта, як-от a.spam = bабо a[0] = b. Незмінні типи не дозволяють присвоювати атрибутам чи елементам, типи змінних зазвичай дозволяють те чи інше.
  • Іноді використовують розширене завдання, як a += b, іноді ні. Типи змінних зазвичай мутують значення; незмінні типи ніколи не роблять, а надають вам копію (вони обчислюють a + b, а потім присвоюють результат a).

Але якщо призначення не є мутацією, то як призначати частину мутації об'єкта? Ось де це стає хитро. a[0] = bробить НЕ мутують a[0](знову - таки, в відміну від C ++), але він робить мутувати a( в відміну від C ++, за винятком побічно).

Все це, чому, мабуть, краще не намагатися ставити семантику Python в мові, до якої ви звикли, а замість цього вивчити семантику Python на власних термінах.


2
Скажіть a = 'привіт'. a [0] = 'f' матиме 'надрукувати' роздрукувати 'fi' (я правий поки що?), тож коли ви скажете, що він не мутує a [0], а a, що це означає ? Чи є у [n] і зараз своє власне місце, і зміна його значення вказує його на інше значення?
Даніель Спрінгер

19

Чи об'єкт є мутаційним чи ні, залежить від його типу. Це не залежить від того, має він чи ні певні методи, ні від структури ієрархії класів.

Зазначені користувачем типи (тобто класи) зазвичай змінюються. Є деякі винятки, такі як прості підкласи незмінного типу. Інші незмінні типи включають в себе деякі вбудовані типи , такі як int, float, tupleі str, а також деякі класи Python реалізовані в C.

Загальне пояснення з розділу "Модель даних" у довіднику мови Python " :

Значення деяких об'єктів може змінюватися. Об'єкти, значення яких може змінюватися, вважаються змінними; об'єкти, значення яких незмінно після їх створення, називаються незмінними.

(Значення об'єкта незмінного контейнера, який містить посилання на об'єкт, що змінюється, може змінюватись, коли значення останнього змінюється; проте контейнер все ще вважається незмінним, оскільки колекція об'єктів, які він містить, не може бути змінена. Отже, незмінність не є строго те саме, що мати незмінне значення, воно більш тонке.)

Змінюваність об'єкта визначається його типом; Наприклад, цифри, рядки та кортежі незмінні, а словники та списки - змінні.


+1 Зауважте, що лише деякі типи розширень (можливо, ви хочете переглянути своє визначення цього, всі вбудовані Python типи реалізовані на C) є незмінними. Інші (більшість, я б наважусь сказати) чудово змінюються.

@delnan Як ви називаєте "типи розширень" ?
eyquem

@eyquem: Я неправильно використав термін "типи розширень" у своїй відповіді, і делнан посилався на це. Після його коментаря я переглянув свою відповідь і уникав використання цього терміна.
taleinat

19

Різниця між змінними та незмінними об'єктами

Визначення

Змінний об'єкт : Об'єкт , який може бути змінений після його створення.
Незмінний об’єкт : Об'єкт, який неможливо змінити після його створення.

У python, якщо ви зміните значення незмінного об'єкта, він створить новий об'єкт.

Змінні об'єкти

Ось об’єкти в Python, які мають змінений тип:

  1. list
  2. Dictionary
  3. Set
  4. bytearray
  5. user defined classes

Незмінні об’єкти

Ось об’єкти в Python, які мають незмінний тип:

  1. int
  2. float
  3. decimal
  4. complex
  5. bool
  6. string
  7. tuple
  8. range
  9. frozenset
  10. bytes

Деякі запитання без відповіді

Запитання : Чи є рядок незмінним типом?
Відповідь : так , але можна пояснити це: Доказ 1 :

a = "Hello"
a +=" World"
print a

Вихідні дані

"Hello World"

У наведеному вище прикладі рядок, яку колись створили як "Hello", потім змінив на "Hello World". Це означає, що рядок має тип, що змінюється. Але це не тоді, коли ми перевіряємо його ідентичність, щоб побачити, чи є він змінним типом чи ні.

a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
    print "String is Immutable"

Вихідні дані

String is Immutable

Доказ 2 :

a = "Hello World"
a[0] = "M"

Вихідні дані

TypeError 'str' object does not support item assignment

Запитання : Чи кортеж незмінний тип?
Відповідь : так , так і є. Доказ 1 :

tuple_a = (1,)
tuple_a[0] = (2,)
print a

Вихідні дані

'tuple' object does not support item assignment

В [46]: a = "Привіт" В [47]: id (a) Out [47]: 140071263880128 В [48]: a = a.replace ("H", "g") В [49]: a Вийшов [49]: 'gello' В [50]: id (a) Вийшов [50]: 140071263881040
Argus Malware

Ви б хотіли довести свою проблему з присвоєнням товару моєму наведеному вище прикладу
Argus Malware

присвоєння елемента не видається в незмінних типах. У вашому випадку ви змінюєте рядок a, але в пам'яті присвоюєте йому нову змінну. Призначення елемента в моєму випадку не змінить пам'ять змінної, як у випадку зі списком чи словником. якщо ви робите заміну, ви створюєте нову змінну, не змінюючи існуючу змінну
anand tripathi

@ArgusMalware у вашому випадку два ідентичні дані дорівнюють тому, що перший перероблений GC, тому другий повторно використовує пам'ять.
Cologler

11

Об'єкт, що змінюється, повинен мати принаймні метод, здатний мутувати об'єкт. Наприклад, listоб'єкт має appendметод, який фактично мутує об'єкт:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

але клас float не має методу мутації плаваючого об'єкта. Ви можете зробити:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

але = операнд - це не метод. Це просто прив'язує змінну до того, що знаходиться праворуч від неї, нічого іншого. Він ніколи не змінює і не створює об'єктів. Це заява на те, на що вказуватиме змінна, з цього моменту.

коли виb = b + 0.1= операнд пов'язує змінний новий поплавок, яким створюються з ТЕ результатом 5 + 0.1.

Якщо ви присвоюєте змінну існуючому об'єкту, змінній або ні, то = операнд прив'язує змінну до цього об'єкта. І більше нічого не відбувається

В будь-якому випадку = справедливі зв'язати. Він не змінює і не створює об'єктів.

Коли ви це зробите a = 1.0, =операнд - це не поплавок, а 1.0частина рядка. Насправді, коли ви пишете, 1.0це скороченняfloat(1.0) виклику конструктора, що повертає об'єкт float. (Ось чому, якщо ви набираєте 1.0та натискаєте клавішу enter, ви отримуєте "ехо", 1.0надрукований нижче; це повернене значення конструкторської функції, яку ви викликали)

Тепер, якщо bє float і ви призначаєте a = b, обидві змінні вказують на один і той же об'єкт, але насправді змінні не можуть поєднати себе між собою, тому що об'єкт є незмінним, і якщо ви це зробите b += 1, тепер bвкажіть на новий об'єкт, іa це все ще вказує на старого і не може знати, на що bвказує.

але якщо cце, скажімо, a list, і ви призначаєте a = c, тепер aі ви cможете "з'єднуватися", тому що listце змінне, і якщо ви робитеc.append('msg') , то просто перевіряючи, aчи отримуєте ви повідомлення.

(До речі, кожен об’єкт має унікальний номер ідентифікаційного номера, до якого ви можете отримати id(x). Отже, ви можете перевірити, чи об’єкт однаковий чи не перевіряєте, чи змінився його унікальний ідентифікатор.)


6

Клас є незмінним , якщо кожен об'єкт цього класу має фіксоване значення при конкретизації , які не можуть ЗГОДОМ бути змінені

Іншим словом змінити все значення цієї змінної (name)або залишити її в спокої.

Приклад:

my_string = "Hello world" 
my_string[0] = "h"
print my_string 

Ви очікували, що це спрацює та надрукує привіт, але це призведе до наступної помилки:

Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment

Перекладач говорить: я не можу змінити перший символ цього рядка

вам доведеться змінити ціле string, щоб воно працювало:

my_string = "Hello World" 
my_string = "hello world"
print my_string #hello world

перевірте цю таблицю:

введіть тут опис зображення

джерело


Як можна змінювати компоненти рядка python більш стислим способом, ніж ви показали вище?
Люк Девіс

@LukeDavis Ви могли б зробити my_string = 'h' + my_string[1:]. Це створить нову рядок під назвою my_string, і вихідний my_string відсутній (роздрукувати, id(my_string)щоб побачити це). Звичайно, це не дуже гнучко, для більш загального випадку ви можете перейти до списку і назад:l = list(my_string) l[0] = 'h' my_string = ''.join(l)
danio

5

Мені здається, ти борешся з питанням, що насправді змінюється / незмінюється . Тож ось просте пояснення:

Спочатку нам потрібен фундамент, на якому можна базувати пояснення.

Тому думайте про все, що ви програмуєте як віртуальний об'єкт, те, що зберігається в пам'яті комп'ютерів як послідовність двійкових чисел. (Не намагайтесь уявити це занадто важко. ^^) Зараз у більшості комп'ютерних мов ви не працюєте безпосередньо з цими двійковими числами, а навпаки, більше використовуєте інтерпретацію двійкових чисел.

Наприклад, ви не думаєте про числа, такі як 0x110, 0xaf0278297319 або подібні, але натомість ви думаєте про числа, як 6 або Strings, як "Hello, world". Ніколи менші числа тез чи рядків є інтерпретацією двійкового числа в пам'яті комп'ютерів. Те саме стосується будь-якого значення змінної.

Якщо коротко: ми не програмуємо фактичні значення, а інтерпретації фактичних бінарних значень.

Зараз у нас є інтерпретації, які не повинні змінюватися заради логіки та інших "акуратних речей", хоча є інтерпретації, які цілком можуть бути змінені. Наприклад, подумайте про моделювання міста, іншими словами - програму, де багато віртуальних об’єктів, а деякі з них - будинки. Тепер ці віртуальні об'єкти (будинки) можуть бути змінені і чи все ще їх можна вважати тими самими будинками? Ну звичайно вони можуть. Таким чином вони мінливі: їх можна змінити, не ставши "абсолютно" іншим об'єктом.

Тепер подумайте про цілі числа: Це також віртуальні об'єкти (послідовності двійкових чисел у пам'яті комп'ютерів). Отже, якщо ми змінимо одну з них, наприклад, збільшуючи значення шість на одне, це все-таки шість? Ну звичайно ні. Таким чином, будь-яке ціле число незмінне.

Отже: Якщо будь-яка зміна віртуального об'єкта означає, що він фактично стає іншим віртуальним об’єктом, то він називається незмінним.

Заключні зауваження:

(1) Ніколи не змішуйте свій реальний досвід змінних та незмінних з програмуванням певною мовою:

Кожна мова програмування має власне визначення того, які об’єкти можуть бути відключені, а які - не.

Тож ви, мабуть, тепер зрозуміли різницю в значенні, вам все одно доведеться вивчити фактичну реалізацію для кожної мови програмування. ... Дійсно, може бути мета мови, коли 6 може бути приглушеним, щоб стати 7. Тоді знову це були б цілком божевільні чи цікаві речі, як моделювання паралельних всесвітів. ^^

(2) Це пояснення, безумовно, не є науковим, воно покликане допомогти вам зрозуміти різницю між змінним та незмінним.


5

Мета цієї відповіді - створити єдине місце, де можна знайти всі хороші ідеї щодо того, як сказати, чи маєте ви справу з мутуванням / немотацією (незмінним / змінним), і де можливо, що з цим робити? Бувають випадки, коли мутація небажана і поведінка пітона в цьому плані може відчувати контрінтуїтивність щодо кодерів, що надходять до неї з інших мов.

Відповідно до корисної публікації від @ mina-gabriel:

Проаналізувавши вищезазначене та поєднавши w / a post від @ arrakëën:

Що не може змінитися несподівано?

  • скаляри (змінні типи, що зберігають одне значення) не змінюються несподівано
    • числові приклади: int (), float (), complex ()
  • є кілька "змінних послідовностей":
    • str (), кортеж (), заморожений набір (), байти ()

Що може?

  • список, як об'єкти (списки, словники, набори, bytearray ())
  • публікація тут також говорить про класи та екземпляри класу, але це може залежати від того, що клас успадковує та / або як його побудовано.

під "несподівано" я маю на увазі, що програмісти з інших мов можуть не сподіватися на таку поведінку (за винятком або Ruby, а може, і декількох інших "Python like" мов).

Додавання до цього обговорення:

Така поведінка є перевагою, коли вона заважає випадково заповнити свій код неодноразовими копіями великих структур даних, що споживають пам'ять. Але коли це небажано, як нам його обійти?

За допомогою списків найпростішим рішенням є створення нового типу:

list2 = список (list1)

з іншими структурами ... рішення може бути складнішим. Один із способів - провести цикл через елементи та додати їх до нової порожньої структури даних (того ж типу).

Функції можуть мутувати оригінал під час передачі змінних структур. Як сказати?

  • Існують деякі тести, надані в інших коментарях до цієї теми, але потім є коментарі, що свідчать про те, що ці тести не є повним підтвердженням
  • object.function () - це метод вихідного об'єкта, але лише деякі з них мутують. Якщо вони нічого не повернуть, вони, мабуть, і роблять. Можна очікувати, що .append () буде вимкнено, не перевіряючи його назви. .union () повертає об'єднання set1.union (set2) і не мутує. Коли ви сумніваєтесь, функцію можна перевірити на повернене значення. Якщо return = Ні, він не мутує.
  • сортування () може бути вирішенням в деяких випадках. Оскільки він повертає відсортовану версію оригіналу, він може дозволити вам зберігати немутовану копію, перш ніж почати працювати над оригіналом іншими способами. Однак ця опція передбачає, що ви не дбаєте про порядок оригінальних елементів (якщо це зробити, вам потрібно знайти інший спосіб). На відміну від .sort () мутує оригінал (як можна було очікувати).

Нестандартні підходи (на випадок, якщо це корисно): Знайдено це на github, опублікованому під ліцензією MIT:

  • сховище github під: tobgu названо: pyrsistent
  • Що це таке: код стійкої структури даних Python, написаний для використання замість основних структур даних, коли мутація небажана

Для користувацьких класів @semicolon пропонує перевірити, чи є __hash__функція, оскільки об'єкти, що змінюються, як правило, не повинні мати a__hash__() функціонувати.

Це все, що я зараз накопичив на цю тему. Інші ідеї, виправлення тощо вітаються. Дякую.


3

Один із способів мислення різниці:

Призначення незмінним об'єктам в python можна вважати глибокими копіями, тоді як призначення об'єктам, що змінюються, дрібні.


1
Це неправильно. Всі завдання в Python - це посилання. Копіювання не бере участь.
augurar

3

Найпростіша відповідь:

Змінна змінна - це та, значення якої може змінитися на місці, тоді як у незмінній змінній зміна значення не відбудеться на місці. Змінення незмінної змінної відновить ту саму змінну.

Приклад:

>>>x = 5

Створить значення 5, на яке посилається х

х -> 5

>>>y = x

Це твердження змусить y посилатися на 5 з x

х -------------> 5 <----------- у

>>>x = x + y

Оскільки x як ціле число (незмінний тип) було відновлено.

У викладі вираз на RHS призведе до значення 10, і коли це буде призначено LHS (x), x відновиться до 10. Отже, зараз

x ---------> 10

y ---------> 5


-1

Я не прочитав усі відповіді, але обрана відповідь не є правильною, і я думаю, що у автора є думка, що можливість перепризначити змінну означає, що будь-який тип даних є змінним. Це не так. Змінюваність більше пов'язана з проходженням посилань, а не передачею за значенням.

Скажімо, ви створили Список

a = [1,2]

Якби ви сказали:

b = a
b[1] = 3

Незважаючи на те, що ви перепризначили значення B, воно також перепризначить значення a. Це тому, що коли ви призначаєте "b = a". Ви передаєте "Посилання" на об'єкт, а не копію значення. Це не стосується рядків, поплавків і т. Д. Це робить список, словники та подібні файли змінними, але булеві, плаваючі тощо не змінюються.


-1

Для незмінних об'єктів присвоєння створює нову копію значень, наприклад.

x=7
y=x
print(x,y)
x=10 # so for immutable objects this creates a new copy so that it doesnot 
#effect the value of y
print(x,y)

Для об'єктів, що змінюються, призначення не створює іншої копії значень. Наприклад,

x=[1,2,3,4]
print(x)
y=x #for immutable objects assignment doesn't create new copy 
x[2]=5
print(x,y) # both x&y holds the same list

1
Абсолютно неправильно. Призначення ніколи не створює копію . Будь ласка, прочитайте nedbatchelder.com/text/names.html У першому випадку x=10це просто інше завдання , коли x[2] = 5викликає мутаційний метод. intоб’єктам просто не вистачає мутаційних методів , але семантика призначення питона не залежить від типу
питона

-2

У Python є простий спосіб дізнатися:

Незмінне:

    >>> s='asd'
    >>> s is 'asd'
    True
    >>> s=None
    >>> s is None
    True
    >>> s=123
    >>> s is 123
    True

Змінні:

>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False

І:

>>> s=abs
>>> s is abs
True

Тому я думаю, що вбудована функція також незмінна в Python.

Але я дійсно не розумію, як працює поплавок:

>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256

Це так дивно.


Але це явно не вірно. Тому що кортежі незмінні. Введіть, x = (1, 2)а потім спробуйте і вимкнути звук x, це неможливо. Один із способів, яким я виявив, щоб перевірити наявність змін, це те hash, що він працює як мінімум для вбудованих об'єктів. hash(1) hash('a') hash((1, 2)) hash(True)всі працюють, і hash([]) hash({}) hash({1, 2})всі не працюють.
крапка з комою

@semicolon Для визначених користувачем класів hash()буде працювати, якщо об'єкт визначає __hash__()метод, навіть якщо визначені користувачем класи, як правило, змінюються.
серпень

1
@augurar Я маю на увазі «так», але нічого в Python нічого не гарантує, тому що Python не має реального статичного набору тексту чи формальних гарантій. Але hashметод все-таки досить хороший, тому що об'єкти, що змінюються, як правило, не повинні мати __hash__()метод, оскільки робити їх ключами у словнику просто небезпечно.
крапка з комою

1
@augurar і ​​крапка з комою (або інші, якщо вони знають це): __hash __ () рішення ... чи повинен творець користувацького класу додати його, щоб він був там? Якщо так, то правило, якщо існує, об'єкт повинен бути незмінним; якщо його не існує, ми не можемо сказати, оскільки його автор просто залишив, якщо він не вимкнений.
TMWP
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.