Loại bất biến vs Mutable


183

Tôi bối rối về loại bất biến là gì. Tôi biết floatđối tượng được coi là bất biến, với loại ví dụ này từ cuốn sách của tôi:

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

Điều này có được coi là bất biến vì cấu trúc / phân cấp lớp không?, Nghĩa floatlà ở đầu lớp và là lời gọi phương thức riêng của nó. Tương tự như loại ví dụ này (mặc dù cuốn sách của tôi nói dictlà có thể thay đổi):

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

Trong khi một cái gì đó có thể thay đổi có các phương thức bên trong lớp, với loại ví dụ này:

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

Ngoài ra, cuối cùng class(SortedKeyDict_a), nếu tôi chuyển loại thiết lập này cho nó:

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

mà không gọi examplephương thức, nó trả về một từ điển. Các SortedKeyDictvới __new__cờ nó như là một lỗi. Tôi đã thử chuyển số nguyên cho RoundFloatlớp __new__và nó không gắn cờ.


Bạn cũng có thể kiểm tra Danh sách gán với [:]python khi nào nên sử dụng copy.copy mà tôi cũng đã trả lời để biết thêm thông tin về khả năng biến đổi.
agf

Câu trả lời:


229

Gì? Phao là bất biến? Nhưng tôi không thể làm

x = 5.0
x += 7.0
print x # 12.0

Không phải là "mut" x sao?

Vâng, bạn đồng ý chuỗi là bất biến phải không? Nhưng bạn có thể làm điều tương tự.

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

Giá trị của biến thay đổi, nhưng nó thay đổi bằng cách thay đổi những gì biến đề cập đến. Một loại có thể thay đổi có thể thay đổi theo cách đó và nó cũng có thể thay đổi "tại chỗ".

Đây là sự khác biệt.

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

Ví dụ cụ thể

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]

4
Những gì bạn giải thích có nghĩa với tôi: các biến có thể thay đổi được truyền bằng tham chiếu, các biến không thay đổi được truyền theo giá trị. Điều này có đúng không?
Lorenz Meyer

16
Hầu như, nhưng không chính xác. Về mặt kỹ thuật, tất cả các biến được truyền bằng tham chiếu trong Python, nhưng có một ngữ nghĩa giống như vượt qua giá trị trong C. Một ví dụ về sự tương tự của bạn là nếu bạn làm như vậy def f(my_list): my_list = [1, 2, 3]. Với tham chiếu qua trong C, giá trị của đối số có thể thay đổi bằng cách gọi hàm đó. Trong Python, hàm đó không làm gì cả. def f(my_list): my_list[:] = [1, 2, 3]sẽ làm một cái gì đó.
sáng sao

6
Các loại đột biến có thể được thay đổi tại chỗ. Các loại bất biến không thể thay đổi tại chỗ. Đó là cách con trăn nhìn thế giới. Nó là bất kể làm thế nào các biến được truyền cho các chức năng.
ychaouche

13
Sự khác biệt chính giữa ngữ nghĩa của Python và ngữ nghĩa truyền qua tham chiếu C ++ là phép gán không phải là đột biến trong Python và nó nằm trong C ++. (Nhưng tất nhiên điều đó phức tạp bởi thực tế là sự phân công tăng cường, giống như a += bđôi khi đột biến. Và thực tế việc gán cho một phần của một vật thể lớn hơn đôi khi có nghĩa là sự đột biến của vật thể lớn hơn đó, không bao giờ là đột biến của phần đó, ví dụ, a[0] = bkhông biến đổi a[0], nhưng nó có thể làm thay đổi cách khắc phục a. Đó là lý do tại sao không nên cố gắng đưa mọi thứ theo C ++ và thay vào đó chỉ mô tả những gì Python làm theo thuật ngữ riêng của nó)
abarnert

2
Tôi thấy câu trả lời này gây hiểu lầm bởi vì nó không sử dụng id (), điều này rất cần thiết để hiểu ý nghĩa bất biến.
pawel_winzig

183

Bạn phải hiểu rằng Python đại diện cho tất cả dữ liệu của nó dưới dạng các đối tượng. Một số đối tượng như danh sách và từ điển có thể thay đổi, có nghĩa là bạn có thể thay đổi nội dung của chúng mà không thay đổi danh tính của chúng. Các đối tượng khác như số nguyên, số float, chuỗi và bộ dữ liệu là các đối tượng không thể thay đổi. Một cách dễ hiểu là nếu bạn nhìn vào ID đối tượng.

Dưới đây bạn thấy một chuỗi là bất biến. Bạn không thể thay đổi nội dung của nó. Nó sẽ tăng TypeErrornếu bạn cố gắng thay đổi nó. Ngoài ra, nếu chúng ta gán nội dung mới, một đối tượng mới sẽ được tạo thay vì nội dung được sửa đổi.

>>> 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

Bạn có thể làm điều đó với một danh sách và nó sẽ không thay đổi danh tính đối tượng

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

Để đọc thêm về mô hình dữ liệu của Python, bạn có thể xem tham chiếu ngôn ngữ Python:


4
+1 Đối với liên kết đến tài liệu Python. Tuy nhiên, tôi phải mất một thời gian cho đến khi tôi nhận ra rằng hôm nay bạn cần phân biệt bewteen Python 2 & 3 - Tôi đã cập nhật câu trả lời để nhấn mạnh điều đó.
benjamin

106

Loại bất biến phổ biến:

  1. số: int(), float(),complex()
  2. chuỗi bất biến: str(), tuple(), frozenset(),bytes()

Loại đột biến phổ biến (hầu hết mọi thứ khác):

  1. trình tự đột biến : list(),bytearray()
  2. loại thiết lập: set()
  3. loại ánh xạ: dict()
  4. lớp học, lớp học
  5. Vân vân.

Một mẹo để nhanh chóng kiểm tra xem một loại có thể thay đổi hay không, là sử dụng id()chức năng tích hợp.

Ví dụ, sử dụng trên số nguyên,

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

sử dụng trong danh sách,

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

11
Giải thích tốt. Thích khái niệm kiểm tra bởi id(). +1.
Parag Tyagi

4
Trên thực tế việc sử dụng id()là sai lệch ở đây. Một đối tượng nhất định sẽ luôn có cùng một id trong suốt vòng đời của nó, nhưng các đối tượng khác nhau tồn tại ở các thời điểm khác nhau có thể có cùng một id do bộ sưu tập rác.
tám

36

Trước hết, cho dù một lớp có các phương thức hay cấu trúc lớp của nó không liên quan gì đến tính đột biến.

ints và floats là bất biến . Nếu tôi làm

a = 1
a += 5

Nó chỉ tên aở một 1nơi nào đó trong bộ nhớ trên dòng đầu tiên. Trên dòng thứ hai, nó ngước lên rằng 1, cho biết thêm 5, được 6, sau đó điểm atại đó 6trong bộ nhớ - nó không thay đổi các 1đến một 6trong bất kỳ cách nào. Logic tương tự áp dụng cho các ví dụ sau, sử dụng các loại bất biến khác:

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

Đối với các loại có thể thay đổi , tôi có thể thực hiện thay đổi giá trị nơi nó được lưu trữ trong bộ nhớ . Với:

d = [1, 2, 3]

Tôi đã tạo một danh sách các địa điểm 1, 23trong bộ nhớ. Nếu tôi thì làm

e = d

Tôi chỉ chỉ evào cùng mộtlist d điểm tại. Sau đó tôi có thể làm:

e += [4, 5]

Và danh sách mà cả edđiểm tại sẽ được cập nhật để có vị trí 45trong bộ nhớ.

Nếu tôi quay trở lại một loại bất biến và làm điều đó với một tuple:

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

Sau đó, fvẫn chỉ trỏ đến bản gốctuple - bạn đã chỉ gvào một hoàn toàn mớituple .

Bây giờ, với ví dụ của bạn về

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

Nơi bạn đi qua

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

(mà là một tuplesố tuples) như val, bạn đang nhận được một lỗi vì tuples không có một .clear()phương pháp - bạn sẽ phải vượt qua dict(d)như valcho nó làm việc, trong trường hợp này bạn sẽ nhận được một sản phẩm nào SortedKeyDictnhư vậy.


2
Đây là lời giải thích rất tốt. Yêu câu hỏi này và rất nhiều quan điểm thú vị (mới) để giải thích nó.
Nhà khoa học thất bại

24

Nếu bạn đến với Python từ một ngôn ngữ khác (ngoại trừ một ngôn ngữ rất giống Python, như Ruby) và khăng khăng hiểu về ngôn ngữ đó, thì đây là nơi mọi người thường bị nhầm lẫn:

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

Trong Python, gán không phải là đột biến trong Python.

Trong C ++, nếu bạn viết a = 2, bạn đang gọi a.operator=(2), nó sẽ làm biến đổi đối tượng được lưu trữ trong đó a. (Và nếu có không có đối tượng được lưu trữ trong a, đó là một lỗi.)

Trong Python, a = 2không có gì cho bất cứ thứ gì được lưu trữ trong a; nó chỉ có nghĩa 2là bây giờ được lưu trữ trong athay thế. (Và nếu có không có đối tượng được lưu trữ trong a, đó là tốt.)


Cuối cùng, đây là một phần của sự khác biệt sâu sắc hơn.

Một biến trong một ngôn ngữ như C ++ là một vị trí được gõ trong bộ nhớ. Nếu alà một int, điều đó có nghĩa là 4 byte ở đâu đó mà trình biên dịch biết được cho là được hiểu là một int. Vì vậy, khi bạn thực hiện a = 2, nó sẽ thay đổi những gì được lưu trữ trong 4 byte bộ nhớ đó từ 0, 0, 0, 1thành 0, 0, 0, 2. Nếu có một biến int khác ở một nơi khác, nó có 4 byte riêng.

Một biến trong một ngôn ngữ như Python là tên của một đối tượng có tuổi thọ của chính nó. Có một đối tượng cho số 1và một đối tượng khác cho số 2. Và akhông phải là 4 byte bộ nhớ được biểu diễn dưới dạng int, nó chỉ là một tên trỏ vào 1đối tượng. Không có ý nghĩa gì a = 2khi biến số 1 thành số 2 (điều đó sẽ cung cấp cho bất kỳ lập trình viên Python nào quá nhiều sức mạnh để thay đổi hoạt động cơ bản của vũ trụ); những gì nó làm thay vào đó chỉ là làm cho aquên 1đối tượng và chỉ vào 2đối tượng thay vào đó.


Vì vậy, nếu sự phân công không phải là đột biến, đột biến gì?

  • Gọi một phương thức được ghi nhận là đột biến, như thế nào a.append(b). (Lưu ý rằng các phương thức này hầu như luôn luôn trả về None). Các loại bất biến không có bất kỳ phương pháp nào như vậy, các loại đột biến thường làm.
  • Gán cho một phần của đối tượng, như a.spam = bhoặc a[0] = b. Các loại không thay đổi không cho phép gán cho các thuộc tính hoặc thành phần, các loại có thể thay đổi thường cho phép cái này hoặc cái kia.
  • Đôi khi sử dụng phân công tăng cường, thích a += b, đôi khi không. Các loại đột biến thường làm thay đổi giá trị; loại bất biến không bao giờ làm, và thay vào đó cung cấp cho bạn một bản sao (họ tính toán a + b, sau đó gán kết quả cho a).

Nhưng nếu sự phân công không phải là đột biến, thì việc gán cho một phần của đột biến đối tượng như thế nào? Đó là nơi nó trở nên khó khăn. a[0] = blàm không đột biến a[0](một lần nữa, không giống như C ++), nhưng nó không đột biến a(không giống như C ++, ngoại trừ gián tiếp).

Tất cả điều này là lý do tại sao có lẽ tốt hơn là không cố gắng đặt ngữ nghĩa của Python theo ngôn ngữ mà bạn đã sử dụng và thay vào đó học các ngữ nghĩa của Python theo các thuật ngữ của riêng họ.


2
Nói a = 'hi'. a [0] = 'f' sẽ có 'in một' in ra 'fi' (Tôi có đúng không?), vì vậy khi bạn nói rằng nó không biến đổi [0], thay vào đó, điều đó có nghĩa là gì ? Bây giờ [n] cũng có vị trí riêng của nó chưa, và việc thay đổi giá trị của nó chỉ ra một giá trị khác?
Daniel Springer

19

Việc một đối tượng có thể thay đổi hay không phụ thuộc vào loại của nó. Điều này không phụ thuộc vào việc nó có các phương thức nhất định hay không, cũng như cấu trúc của hệ thống phân cấp lớp.

Các loại do người dùng định nghĩa (tức là các lớp) thường có thể thay đổi. Có một số trường hợp ngoại lệ, chẳng hạn như các lớp con đơn giản thuộc loại không thay đổi. Loại không thay đổi khác bao gồm một số được xây dựng trong các loại như int, float, tuplestr, cũng như một số các lớp học Python thực hiện trong C.

Một lời giải thích chung từ chương "Mô hình dữ liệu" trong Tài liệu tham khảo ngôn ngữ Python " :

Giá trị của một số đối tượng có thể thay đổi. Các đối tượng có giá trị có thể thay đổi được cho là có thể thay đổi; các đối tượng có giá trị không thể thay đổi một khi chúng được tạo ra được gọi là bất biến.

(Giá trị của một đối tượng container bất biến có chứa tham chiếu đến một đối tượng có thể thay đổi có thể thay đổi khi giá trị của đối tượng đó bị thay đổi; tuy nhiên, container vẫn được coi là bất biến, bởi vì bộ sưu tập các đối tượng mà nó chứa không thể thay đổi. giống như có một giá trị không thể thay đổi, nó tinh tế hơn.)

Khả năng biến đổi của một đối tượng được xác định bởi loại của nó; ví dụ, số, chuỗi và bộ dữ liệu là bất biến, trong khi từ điển và danh sách là có thể thay đổi.


+1 Lưu ý rằng chỉ một số loại tiện ích mở rộng (bạn có thể muốn xem lại định nghĩa của mình về điều đó, tất cả các loại dựng sẵn của Python đều được triển khai trong C) là không thể thay đổi. Những người khác (hầu hết, tôi dám nói) là hoàn toàn có thể thay đổi.

@delnan Bạn gọi "loại tiện ích mở rộng" là gì?
Eyquem

@eyquem: Tôi đã sử dụng thuật ngữ "các loại tiện ích mở rộng" không chính xác trong câu trả lời của mình và delnan đã đề cập đến điều đó. Sau khi nhận xét của anh ấy, tôi đã sửa lại câu trả lời của mình và tránh sử dụng thuật ngữ này.
taleinat

19

Sự khác biệt giữa đối tượng Mutable và bất biến

Định nghĩa

Đối tượng Mutable : Đối tượng có thể được thay đổi sau khi tạo nó.
Đối tượng bất biến : Đối tượng không thể thay đổi sau khi tạo.

Trong python sẽ cố gắng thay đổi giá trị của đối tượng bất biến nó sẽ cung cấp cho đối tượng mới.

Đối tượng đột biến

Dưới đây là danh sách các đối tượng trong python thuộc loại có thể thay đổi:

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

Đối tượng bất biến

Dưới đây là danh sách các đối tượng trong python thuộc loại không thay đổi:

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

Một số câu hỏi chưa được trả lời

Câu hỏi : Chuỗi có phải là loại bất biến không?
Trả lời : đúng vậy, nhưng bạn có thể giải thích điều này: Bằng chứng 1 :

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

Đầu ra

"Hello World"

Trong ví dụ trên, chuỗi đã được tạo một lần là "Hello" cuối cùng đã đổi thành "Hello World". Điều này ngụ ý rằng chuỗi là loại có thể thay đổi. Nhưng không phải chúng ta có thể kiểm tra danh tính của nó và kiểm tra xem nó có thuộc loại đột biến hay không.

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

Đầu ra

String is Immutable

Bằng chứng 2 :

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

Đầu ra

TypeError 'str' object does not support item assignment

Câu hỏi : Có phải Tuple là một loại bất biến?
Trả lời : có, đó là Bằng chứng 1 :

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

Đầu ra

'tuple' object does not support item assignment

Trong [46]: a = "Xin chào" Trong [47]: id (a) Out [47]: 140071263880128 Trong [48]: a = a.replace ("H", "g") Trong [49]: a Out [49]: 'gello' Trong [50]: id (a) Out [50]: 140071263881040
Phần mềm độc hại Argus

bạn có quan tâm đến việc chứng minh vấn đề chuyển nhượng vật phẩm của bạn với ví dụ nêu trên của tôi không
Argus Malware

phân công mục không phải là vấn đề trong các loại bất biến. Trong trường hợp của bạn, bạn đang thay đổi chuỗi a nhưng trong bộ nhớ, nó gán cho một biến mới. Việc gán vật phẩm trong trường hợp của tôi sẽ không thay đổi bộ nhớ của biến như trong trường hợp danh sách hoặc từ điển. nếu bạn đang thay thế, bạn đang tạo một biến mới không sửa đổi biến hiện có
anand tripathi

@ArgusMalware trong trường hợp của bạn, hai id bằng nhau vì cái đầu tiên được tái chế bởi GC, vì vậy cái thứ hai sử dụng lại bộ nhớ.
Cologler

11

Một đối tượng có thể thay đổi phải có ít nhất một phương thức có thể biến đổi đối tượng. Ví dụ, listđối tượng có appendphương thức, sẽ thực sự biến đổi đối tượng:

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

nhưng lớp floatkhông có phương thức để biến đổi một đối tượng float. Bạn có thể làm:

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

nhưng =toán hạng không phải là một phương thức. Nó chỉ tạo ra một ràng buộc giữa biến và bất cứ điều gì ở bên phải của nó, không có gì khác. Nó không bao giờ thay đổi hoặc tạo ra các đối tượng. Đó là một tuyên bố về những gì biến sẽ chỉ đến, kể từ bây giờ.

Khi bạn làm b = b + 0.1các =toán hạng liên kết với các biến để một phao mới, mà được tạo ra với te do 5 + 0.1.

Khi bạn gán một biến cho một đối tượng tồn tại, có thể thay đổi hay không, =toán hạng liên kết biến đó với đối tượng đó. Và không có gì xảy ra nữa

Trong cả hai trường hợp, =chỉ cần thực hiện các ràng buộc. Nó không thay đổi hoặc tạo đối tượng.

Khi bạn làm a = 1.0, =toán hạng không phải là tạo ra float, mà là 1.0một phần của dòng. Trên thực tế khi bạn viết 1.0nó là một tốc ký cho float(1.0)một lệnh gọi hàm tạo trả về một đối tượng float. (Đó là lý do tại sao nếu bạn nhập 1.0và nhấn enter, bạn sẽ nhận được "tiếng vang" 1.0được in bên dưới; đó là giá trị trả về của hàm xây dựng mà bạn đã gọi)

Bây giờ, nếu blà một phao và bạn gán a = b, cả hai biến được trỏ đến cùng một đối tượng, nhưng thực chất các biến giữa động không thể comunicate bản thân, bởi vì đối tượng là inmutable, và nếu bạn làm b += 1, bây giờ bđiểm đến một đối tượng mới, và alà vẫn chỉ vào cái cũ và không thể biết cái gì bđang chỉ vào.

nhưng nếu clà, giả sử, a list, và bạn chỉ định a = c, bây giờ accó thể "kết hợp", bởi vì listcó thể thay đổi, và nếu bạn làm vậy c.append('msg'), thì chỉ cần kiểm tra abạn nhận được tin nhắn.

(Nhân tiện, mọi đối tượng đều có một số id duy nhất được liên kết với, bạn có thể nhận được id(x). Vì vậy, bạn có thể kiểm tra xem một đối tượng có giống nhau hay không kiểm tra xem id duy nhất của nó có thay đổi hay không.)


6

Một lớp là bất biến nếu mỗi đối tượng của lớp đó có một giá trị cố định khi khởi tạo mà không thể thay đổi BÊN NGOÀI

Trong một từ khác thay đổi toàn bộ giá trị của biến đó (name)hoặc để nó một mình.

Thí dụ:

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

bạn mong đợi nó hoạt động và in hello world nhưng điều này sẽ gây ra lỗi sau:

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

Trình thông dịch đang nói: tôi không thể thay đổi ký tự đầu tiên của chuỗi này

bạn sẽ phải thay đổi toàn bộ stringđể làm cho nó hoạt động:

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

kiểm tra bảng này:

nhập mô tả hình ảnh ở đây

nguồn


Làm thế nào người ta có thể sửa đổi các thành phần của chuỗi python theo cách ngắn gọn hơn bạn đã trình bày ở trên?
Luke Davis

@LukeDavis Bạn có thể làm my_string = 'h' + my_string[1:]. Điều này sẽ tạo ra một chuỗi mới gọi là my_opes và my_opes ban đầu đã biến mất (in id(my_string)để xem điều này). Tất nhiên điều đó không linh hoạt lắm, đối với trường hợp tổng quát hơn, bạn có thể chuyển đổi sang danh sách và quay lại:l = list(my_string) l[0] = 'h' my_string = ''.join(l)
danio

5

Dường như với tôi rằng bạn đang chiến đấu với câu hỏi ý nghĩa thực sự có thể thay đổi / bất biến . Vì vậy, đây là một giải thích đơn giản:

Đầu tiên chúng ta cần một nền tảng để dựa trên sự khám phá.

Vì vậy, hãy nghĩ về bất cứ điều gì bạn lập trình như một đối tượng ảo, một cái gì đó được lưu trong bộ nhớ máy tính như một chuỗi các số nhị phân. (Tuy nhiên, đừng cố tưởng tượng điều này quá khó. ^^) Bây giờ trong hầu hết các ngôn ngữ máy tính, bạn sẽ không làm việc trực tiếp với các số nhị phân này mà thay vào đó, bạn sử dụng cách giải thích các số nhị phân.

Ví dụ: bạn không nghĩ về các số như 0x110, 0xaf0278297319 hoặc tương tự, nhưng thay vào đó bạn nghĩ về các số như 6 hoặc Chuỗi như "Xin chào, thế giới". Không bao giờ các số ít hơn hoặc Chuỗi là một cách giải thích số nhị phân trong bộ nhớ máy tính. Điều này cũng đúng với bất kỳ giá trị nào của một biến.

Tóm lại: Chúng tôi không lập trình với các giá trị thực tế mà bằng các diễn giải về giá trị nhị phân thực tế.

Bây giờ chúng ta có những diễn giải không được thay đổi vì logic và "những thứ gọn gàng" khác trong khi có những diễn giải có thể được thay đổi. Ví dụ, nghĩ về mô phỏng của một thành phố, nói cách khác là một chương trình có nhiều vật thể ảo và một số trong số đó là những ngôi nhà. Bây giờ những vật thể ảo này (những ngôi nhà) có thể được thay đổi và chúng vẫn có thể được coi là cùng một ngôi nhà không? Tất nhiên họ có thể. Do đó, chúng có thể thay đổi: Chúng có thể được thay đổi mà không trở thành một đối tượng "hoàn toàn" khác.

Bây giờ hãy nghĩ về số nguyên: Đây cũng là các đối tượng ảo (chuỗi các số nhị phân trong bộ nhớ máy tính). Vì vậy, nếu chúng ta thay đổi một trong số chúng, như tăng giá trị sáu lên một, nó vẫn là sáu? Vâng tất nhiên là không. Do đó, bất kỳ số nguyên là bất biến.

Vì vậy: Nếu bất kỳ thay đổi nào trong một đối tượng ảo có nghĩa là nó thực sự trở thành một đối tượng ảo khác, thì nó được gọi là bất biến.

Chú thích cuối:

(1) Không bao giờ trộn lẫn trải nghiệm trong thế giới thực của bạn về khả năng biến đổi và bất biến với lập trình bằng một ngôn ngữ nhất định:

Mỗi ngôn ngữ lập trình có một định nghĩa riêng về các đối tượng có thể bị tắt tiếng và đối tượng nào có thể không bị tắt tiếng.

Vì vậy, trong khi bây giờ bạn có thể hiểu sự khác biệt về ý nghĩa, bạn vẫn phải học cách triển khai thực tế cho từng ngôn ngữ lập trình. ... Quả thực có thể có một mục đích của một ngôn ngữ trong đó số 6 có thể bị tắt tiếng để trở thành số 7. Sau đó, đây sẽ là một thứ khá điên rồ hoặc thú vị, như mô phỏng các vũ trụ song song. ^^

(2) Giải thích này chắc chắn không khoa học, nó có nghĩa là giúp bạn nắm bắt sự khác biệt giữa đột biến và bất biến.


5

Mục tiêu của câu trả lời này là tạo ra một nơi duy nhất để tìm tất cả các ý tưởng hay về cách nhận biết nếu bạn đang đối phó với đột biến / không biến đổi (bất biến / đột biến), và nếu có thể, phải làm gì về nó? Đôi khi, sự đột biến là không mong muốn và hành vi của python về vấn đề này có thể cảm thấy phản trực giác đối với các lập trình viên đến từ các ngôn ngữ khác.

Theo một bài viết hữu ích của @ mina-gabriel:

Phân tích ở trên và kết hợp với một bài đăng của @ arrakëën:

Điều gì không thể thay đổi bất ngờ?

  • vô hướng (các loại biến lưu trữ một giá trị) không thay đổi bất ngờ
    • ví dụ số: int (), float (), phức ()
  • có một số "trình tự đột biến":
    • str (), tuple (), froundredet (), byte ()

Những gì có thể?

  • liệt kê như các đối tượng (danh sách, từ điển, bộ, bytearray ())
  • một bài đăng ở đây cũng cho biết các lớp và các thể hiện của lớp nhưng điều này có thể phụ thuộc vào những gì lớp kế thừa từ và / hoặc cách nó được xây dựng.

bởi "bất ngờ" Tôi có nghĩa là các lập trình viên từ các ngôn ngữ khác có thể không mong đợi hành vi này (với ngoại lệ hoặc Ruby, và có thể một vài ngôn ngữ "Python like" khác).

Thêm vào cuộc thảo luận này:

Hành vi này là một lợi thế khi nó ngăn bạn vô tình điền mã của bạn với các bản sao đa dạng của các cấu trúc dữ liệu lớn ăn bộ nhớ. Nhưng khi điều này là không mong muốn, làm thế nào để chúng ta có được xung quanh nó?

Với các danh sách, giải pháp đơn giản là xây dựng một danh sách mới như vậy:

list2 = danh sách (list1)

với các cấu trúc khác ... giải pháp có thể phức tạp hơn. Một cách là lặp qua các phần tử và thêm chúng vào cấu trúc dữ liệu trống mới (cùng loại).

các hàm có thể biến đổi bản gốc khi bạn chuyển qua các cấu trúc có thể thay đổi. Nói thế nào?

  • Có một số thử nghiệm được đưa ra trên các bình luận khác về chủ đề này nhưng sau đó có những bình luận cho thấy những thử nghiệm này không phải là bằng chứng đầy đủ
  • object.feft () là một phương thức của đối tượng ban đầu nhưng chỉ một số trong số các đột biến này. Nếu họ trả lại không có gì, họ có thể làm. Người ta sẽ mong đợi .append () sẽ biến đổi mà không cần kiểm tra tên của nó. .union () trả về kết hợp của set1.union (set2) và không biến đổi. Khi nghi ngờ, chức năng có thể được kiểm tra cho giá trị trả về. Nếu return = Không, nó không biến đổi.
  • sort () có thể là một cách giải quyết trong một số trường hợp. Vì nó trả về một phiên bản đã sắp xếp của bản gốc, nó có thể cho phép bạn lưu trữ một bản sao không bị đột biến trước khi bạn bắt đầu làm việc với bản gốc theo những cách khác. Tuy nhiên, tùy chọn này giả định rằng bạn không quan tâm đến thứ tự của các yếu tố ban đầu (nếu bạn làm thế, bạn cần tìm một cách khác). Ngược lại .sort () làm thay đổi bản gốc (như người ta có thể mong đợi).

Phương pháp tiếp cận không chuẩn (trong trường hợp hữu ích): Tìm thấy điều này trên github được xuất bản theo giấy phép MIT:

  • kho github dưới: tobgu có tên: pyrsistent
  • Nó là gì: Mã cấu trúc dữ liệu liên tục Python được viết để sử dụng thay cho cấu trúc dữ liệu cốt lõi khi đột biến là không mong muốn

Đối với các lớp tùy chỉnh, @semiaolon đề nghị kiểm tra xem có __hash__hàm nào không vì các đối tượng có thể thay đổi thường không có __hash__()hàm.

Đây là tất cả những gì tôi đã tích lũy về chủ đề này cho đến bây giờ. Ý tưởng khác, sửa chữa, vv được chào đón. Cảm ơn.


3

Một cách nghĩ về sự khác biệt:

Việc gán cho các đối tượng bất biến trong python có thể được coi là các bản sao sâu, trong khi các nhiệm vụ cho các đối tượng có thể thay đổi là nông


1
Điều này là không đúng. Tất cả các bài tập trong Python là bằng cách tham khảo. Không có sao chép liên quan.
tám

3

Câu trả lời đơn giản nhất:

Một biến có thể thay đổi là một biến có giá trị có thể thay đổi tại chỗ, trong khi đó ở biến không thay đổi của giá trị sẽ không xảy ra. Sửa đổi một biến không thay đổi sẽ xây dựng lại cùng một biến.

Thí dụ:

>>>x = 5

Sẽ tạo giá trị 5 được tham chiếu bởi x

x -> 5

>>>y = x

Tuyên bố này sẽ làm cho y đề cập đến 5 của x

x -------------> 5 <-----------

>>>x = x + y

Vì x là một số nguyên (loại bất biến) đã được xây dựng lại.

Trong câu lệnh, biểu thức trên RHS sẽ dẫn đến giá trị 10 và khi điều này được gán cho LHS (x), x sẽ xây dựng lại thành 10. Vì vậy, bây giờ

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

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


-1

Tôi chưa đọc tất cả các câu trả lời, nhưng câu trả lời được chọn là không chính xác và tôi nghĩ rằng tác giả có một ý tưởng rằng có thể gán lại một biến có nghĩa là bất kỳ kiểu dữ liệu nào cũng có thể thay đổi. Đó không phải là tình huống. Khả năng tương tác phải làm với việc chuyển bằng tham chiếu thay vì chuyển theo giá trị.

Hãy nói rằng bạn đã tạo Danh sách

a = [1,2]

Nếu bạn định nói:

b = a
b[1] = 3

Mặc dù bạn đã gán lại giá trị trên B, nhưng nó cũng sẽ gán lại giá trị trên a. Đó là bởi vì khi bạn gán "b = a". Bạn đang chuyển "Tham chiếu" cho đối tượng chứ không phải là bản sao của giá trị. Đây không phải là trường hợp với chuỗi, float, v.v ... Điều này làm cho danh sách, từ điển và các lượt thích có thể thay đổi, nhưng booleans, float vv bất biến.


-1

Ví dụ, đối với các đối tượng không thay đổi, gán sẽ tạo một bản sao mới của các giá trị.

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)

Đối với các đối tượng có thể thay đổi, phép gán không tạo ra một bản sao khác của các giá trị. Ví dụ,

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
Hoàn toàn không chính xác. Chuyển nhượng không bao giờ tạo ra một bản sao . Vui lòng đọc nedbatchelder.com/text/names.html Trong trường hợp đầu tiên, x=10chỉ đơn giản là một nhiệm vụ khác , trong khi x[2] = 5gọi một phương thức biến đổi. intcác đối tượng chỉ đơn giản là thiếu các phương thức trình biến đổi , nhưng ngữ nghĩa của phép gán python không phụ thuộc vào loại
juanpa.arrivillaga

-2

Trong Python, có một cách dễ dàng để biết:

Bất biến:

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

Có thể thay đổi:

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

Và:

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

Vì vậy, tôi nghĩ rằng hàm tích hợp cũng là bất biến trong Python.

Nhưng tôi thực sự không hiểu làm thế nào float hoạt động:

>>> 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

Nó rất kì lạ.


Nhưng điều đó rõ ràng là không hợp lệ. Bởi vì tuples là bất biến. Nhập x = (1, 2)và sau đó thử và biến đổi x, nó không thể. Một cách tôi đã tìm thấy để kiểm tra tính biến đổi là hash, nó hoạt động cho các đối tượng dựng sẵn ít nhất. hash(1) hash('a') hash((1, 2)) hash(True)tất cả công việc, và hash([]) hash({}) hash({1, 2})tất cả không hoạt động.
dấu chấm phẩy

@semicolon Đối với các lớp do người dùng định nghĩa thì hash()sẽ hoạt động nếu đối tượng định nghĩa một __hash__()phương thức, mặc dù các lớp do người dùng định nghĩa thường có thể thay đổi.
tám

1
@augurar Ý tôi là có, nhưng không có gì trong Python sẽ đảm bảo bất cứ điều gì, bởi vì Python không có kiểu gõ tĩnh thực sự hoặc đảm bảo chính thức. Nhưng hashphương pháp này vẫn còn khá tốt, vì các đối tượng có thể thay đổi thường không có __hash__()phương thức, vì làm cho chúng trở thành khóa trong từ điển chỉ là nguy hiểm.
dấu chấm phẩy

1
@augurar và dấu chấm phẩy (hoặc những người khác nếu họ biết): giải pháp __hash __ () ... người tạo ra một lớp tùy chỉnh có phải thêm nó vào đó không? Nếu vậy, quy tắc là nếu tồn tại đối tượng nên bất biến; nếu nó không tồn tại, chúng ta không thể biết vì người tạo có thể đơn giản rời đi nếu tắt.
TMWP
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.