Tại sao chúng ta cần bộ dữ liệu trong Python (hoặc bất kỳ loại dữ liệu bất biến nào)?


140

Tôi đã đọc một số hướng dẫn về python (Lặn vào Python, cho một người) và tài liệu tham khảo ngôn ngữ trên Python.org - Tôi không hiểu tại sao ngôn ngữ cần bộ dữ liệu.

Các bộ dữ liệu không có phương thức nào so với một danh sách hoặc bộ và nếu tôi phải chuyển đổi một bộ dữ liệu thành một bộ hoặc danh sách để có thể sắp xếp chúng, thì điểm đầu tiên của việc sử dụng bộ dữ liệu là gì?

Bất biến?

Tại sao mọi người quan tâm nếu một biến sống ở một nơi khác trong bộ nhớ so với khi nó được phân bổ ban đầu? Toàn bộ hoạt động kinh doanh bất biến trong Python này dường như được nhấn mạnh quá mức.

Trong C / C ++ nếu tôi phân bổ một con trỏ và trỏ đến một số bộ nhớ hợp lệ, tôi không quan tâm địa chỉ đó nằm ở đâu miễn là nó không rỗng trước khi tôi sử dụng nó.

Bất cứ khi nào tôi tham chiếu biến đó, tôi không cần biết liệu con trỏ có còn trỏ đến địa chỉ ban đầu hay không. Tôi chỉ kiểm tra null và sử dụng nó (hoặc không).

Trong Python, khi tôi phân bổ một chuỗi (hoặc tuple) gán nó cho x, sau đó sửa đổi chuỗi, tại sao tôi lại quan tâm nếu đó là đối tượng ban đầu? Miễn là biến chỉ vào dữ liệu của tôi, đó mới là vấn đề.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

x vẫn tham chiếu dữ liệu tôi muốn, tại sao mọi người cần quan tâm nếu id của nó giống hay khác nhau?


12
bạn đang chú ý đến khía cạnh sai của tính đột biến: "dù id giống hay khác" chỉ là tác dụng phụ; "Liệu dữ liệu được trỏ đến bởi các tham chiếu khác mà trước đây chỉ vào cùng một đối tượng hiện phản ánh các cập nhật" là rất quan trọng.
Charles Duffy

Câu trả lời:


124
  1. đối tượng bất biến có thể cho phép tối ưu hóa đáng kể; đây có lẽ là lý do tại sao các chuỗi cũng bất biến trong Java, được phát triển khá riêng biệt nhưng cùng thời gian với Python và mọi thứ đều bất biến trong các ngôn ngữ thực sự có chức năng.

  2. trong Python nói riêng, chỉ các bất biến mới có thể được băm (và do đó, các thành viên của bộ hoặc khóa trong từ điển). Một lần nữa, điều này tối ưu hóa đủ khả năng, nhưng không chỉ là "đáng kể" (thiết kế các bảng băm tốt lưu trữ các đối tượng hoàn toàn có thể thay đổi là một cơn ác mộng - hoặc bạn lấy bản sao của mọi thứ ngay khi bạn băm nó, hoặc cơn ác mộng kiểm tra xem liệu băm của đối tượng đã thay đổi kể từ lần cuối cùng bạn tham khảo về cái đầu xấu xí của nó).

Ví dụ về vấn đề tối ưu hóa:

$ python -mtimeit '["fee", "fie", "fo", "fum"]'
1000000 loops, best of 3: 0.432 usec per loop
$ python -mtimeit '("fee", "fie", "fo", "fum")'
10000000 loops, best of 3: 0.0563 usec per loop

11
@musicfreak, hãy xem bản chỉnh sửa tôi vừa thực hiện khi xây dựng bộ dữ liệu nhanh hơn 7.6 lần so với xây dựng danh sách tương đương - bây giờ bạn không thể nói rằng bạn "chưa bao giờ thấy sự khác biệt đáng chú ý", trừ khi định nghĩa của bạn về "đáng chú ý" "Thật sự rất đặc biệt ...
Alex Martelli

11
@musicfreak Tôi nghĩ bạn đang lạm dụng "tối ưu hóa sớm là gốc rễ của mọi tội lỗi". Có một sự khác biệt lớn giữa việc thực hiện tối ưu hóa sớm trong một ứng dụng (ví dụ: nói "bộ dữ liệu nhanh hơn danh sách, vì vậy chúng tôi sẽ chỉ sử dụng bộ dữ liệu trong tất cả các ứng dụng!") Và thực hiện các điểm chuẩn. Điểm chuẩn của Alex là sâu sắc và biết rằng việc xây dựng một tuple nhanh hơn việc xây dựng một danh sách có thể giúp chúng ta trong các hoạt động tối ưu hóa trong tương lai (khi thực sự cần thiết).
Virgil Dupras

5
@Alex, là "xây dựng" một tuple thực sự nhanh hơn "xây dựng một danh sách", hay chúng ta đang thấy kết quả của thời gian chạy Python lưu trữ bộ dữ liệu? Có vẻ như sau này với tôi.
Triptych

6
@ACoolie, điều đó hoàn toàn bị chi phối bởi các randomcuộc gọi (hãy thử làm điều đó, bạn sẽ thấy!), Vì vậy không đáng kể lắm. Hãy thử python -mtimeit -s "x=23" "[x,x]"và bạn sẽ thấy một sự tăng tốc có ý nghĩa hơn 2-3 lần để xây dựng bộ dữ liệu so với việc xây dựng danh sách.
Alex Martelli

9
cho bất cứ ai thắc mắc - chúng tôi đã có thể loại bỏ hơn một giờ xử lý dữ liệu bằng cách chuyển từ danh sách sang bộ dữ liệu.
Đánh dấu Ribau

42

Không có câu trả lời nào ở trên chỉ ra vấn đề thực sự của bộ dữ liệu so với danh sách, điều mà nhiều người mới sử dụng Python dường như chưa hiểu hết.

Tuples và danh sách phục vụ các mục đích khác nhau. Danh sách lưu trữ dữ liệu đồng nhất. Bạn có thể và nên có một danh sách như thế này:

["Bob", "Joe", "John", "Sam"]

Lý do sử dụng đúng danh sách là vì đó là tất cả các loại dữ liệu đồng nhất, cụ thể là tên của mọi người. Nhưng hãy lấy một danh sách như thế này:

["Billy", "Bob", "Joe", 42]

Danh sách đó là tên đầy đủ của một người và tuổi của họ. Đó không phải là một loại dữ liệu. Cách chính xác để lưu trữ thông tin đó là trong một tuple hoặc trong một đối tượng. Hãy nói rằng chúng tôi có một vài:

[("Billy", "Bob", "Joe", 42), ("Robert", "", "Smith", 31)]

Tính bất biến và khả năng biến đổi của Tuples và Lists không phải là điểm khác biệt chính. Danh sách là danh sách các loại mặt hàng giống nhau: tệp, tên, đối tượng. Tuples là một nhóm các loại đối tượng khác nhau. Chúng có các cách sử dụng khác nhau và nhiều danh sách lạm dụng mã hóa Python cho các bộ dữ liệu có nghĩa là gì.

Xin đừng.


Biên tập:

Tôi nghĩ rằng bài đăng trên blog này giải thích lý do tại sao tôi nghĩ rằng điều này tốt hơn tôi đã làm: http://news.e-scribe.com/397


13
Tôi nghĩ rằng bạn có một tầm nhìn ít nhất là do tôi đồng ý, không biết những người khác.
Stefano Borini

13
Tôi cũng không đồng ý với câu trả lời này. Tính đồng nhất của dữ liệu hoàn toàn không liên quan gì đến việc bạn nên sử dụng danh sách hay bộ dữ liệu. Không có gì trong Python gợi ý sự khác biệt này.
Glenn Maynard

14
Guido đã đưa ra quan điểm này một vài năm trước đây. aspn.activestate.com/ASPN/Mail/Message/python-list/1566320
John La Rooy

11
Mặc dù Guido (người thiết kế Python) dự định các danh sách sẽ được sử dụng cho dữ liệu đồng nhất và bộ dữ liệu không đồng nhất, nhưng thực tế là ngôn ngữ không thực thi điều này. Do đó, tôi nghĩ cách giải thích này là vấn đề phong cách hơn bất cứ điều gì khác. Điều đó xảy ra rằng trong các trường hợp sử dụng thông thường của nhiều người, các danh sách có xu hướng giống như mảng và các bộ dữ liệu có xu hướng giống như bản ghi. Nhưng điều này không nên ngăn mọi người sử dụng danh sách cho dữ liệu không đồng nhất nếu nó phù hợp với vấn đề của họ hơn. Như Zen của Python nói: Tính thực tế đánh bại sự thuần khiết.
John Y

9
@Glenn, về cơ bản bạn đã sai. Một trong những cách sử dụng chính của bộ dữ liệu là một kiểu dữ liệu tổng hợp để lưu trữ nhiều mẩu dữ liệu có liên quan. Việc bạn có thể lặp lại một tuple và thực hiện nhiều thao tác tương tự không làm thay đổi điều này. (Như tài liệu tham khảo cho rằng các bộ dữ liệu trong nhiều ngôn ngữ khác không có các tính năng lặp giống như các đối tác trong danh sách của chúng)
HS.

22

nếu tôi phải chuyển đổi một tuple thành một tập hợp hoặc danh sách để có thể sắp xếp chúng, thì điểm đầu tiên của việc sử dụng một tuple là gì?

Trong trường hợp cụ thể này, có lẽ không phải là một điểm. Đây không phải là vấn đề, vì đây không phải là một trong những trường hợp bạn cân nhắc sử dụng bộ dữ liệu.

Như bạn chỉ ra, tuples là bất biến. Những lý do để có các loại bất biến áp dụng cho bộ dữ liệu:

  • sao chép hiệu quả: thay vì sao chép một đối tượng bất biến, bạn có thể đặt bí danh cho nó (liên kết một biến với một tham chiếu)
  • hiệu quả so sánh: khi bạn đang sử dụng bản sao theo tham chiếu, bạn có thể so sánh hai biến bằng cách so sánh vị trí, thay vì nội dung
  • thực tập: bạn cần lưu trữ tối đa một bản sao của bất kỳ giá trị bất biến nào
  • không cần phải đồng bộ hóa quyền truy cập vào các đối tượng bất biến trong mã đồng thời
  • const đúng: một số giá trị không được phép thay đổi. Điều này (với tôi) là lý do chính cho các loại bất biến.

Lưu ý rằng việc triển khai Python cụ thể có thể không sử dụng tất cả các tính năng trên.

Các khóa từ điển phải là bất biến, nếu không việc thay đổi các thuộc tính của đối tượng khóa có thể làm mất hiệu lực các bất biến của cấu trúc dữ liệu cơ bản. Tuples do đó có thể có khả năng được sử dụng như là chìa khóa. Đây là một hệ quả của sự đúng đắn const.

Xem thêm " Giới thiệu bộ dữ liệu ", từ Lặn vào Python .


2
id ((1,2,3)) == id ((1,2,3)) là sai. Bạn không thể so sánh các bộ dữ liệu chỉ bằng cách so sánh vị trí, bởi vì không có gì đảm bảo rằng chúng được sao chép bằng cách tham chiếu.
Glenn Maynard

@Glenn: Lưu ý nhận xét đủ điều kiện "khi bạn đang sử dụng bản sao theo tham chiếu". Trong khi bộ mã hóa có thể tạo ra triển khai của riêng họ, thì sao chép tham chiếu cho các bộ dữ liệu phần lớn là vấn đề đối với trình thông dịch / trình biên dịch. Tôi chủ yếu đề cập đến cách ==thực hiện ở cấp độ nền tảng.
outis

1
@Glenn: cũng lưu ý rằng sao chép theo tham chiếu không áp dụng cho các bộ dữ liệu trong (1,2,3) == (1,2,3). Đó là vấn đề thực tập nhiều hơn.
outis

Như tôi đã nói khá rõ ràng, không có gì đảm bảo rằng chúng được sao chép bằng cách tham chiếu . Tuples không được thực hiện trong Python; đó là một khái niệm chuỗi.
Glenn Maynard

Giống như tôi đã nói rất rõ ràng: Tôi không nói về lập trình viên so sánh các bộ dữ liệu bằng cách so sánh vị trí. Tôi đang nói về khả năng nền tảng có thể, có thể đảm bảo sao chép theo tham chiếu. Ngoài ra, thực tập có thể được áp dụng cho bất kỳ loại bất biến nào, không chỉ các chuỗi. Việc triển khai Python chính có thể không thực hiện các loại bất biến, nhưng thực tế Python có các loại không thay đổi làm cho việc thực hiện một tùy chọn.
outis

15

Đôi khi chúng ta thích sử dụng các đối tượng làm khóa từ điển

Để biết giá trị của nó, bộ dữ liệu gần đây (2.6+) đã phát triển index()count()phương pháp


5
+1: Danh sách có thể thay đổi (hoặc bộ có thể thay đổi hoặc từ điển có thể thay đổi) làm khóa từ điển không thể hoạt động. Vì vậy, chúng tôi cần các danh sách bất biến ("bộ dữ liệu"), các bộ đóng băng và ... à ... một từ điển đông lạnh, tôi cho rằng.
S.Lott

9

Tôi luôn thấy có hai loại hoàn toàn riêng biệt cho cùng một cấu trúc dữ liệu cơ bản (mảng) là một thiết kế khó xử, nhưng không phải là vấn đề thực sự trong thực tế. (Mọi ngôn ngữ đều có mụn cóc, bao gồm Python, nhưng đây không phải là ngôn ngữ quan trọng.)

Tại sao mọi người quan tâm nếu một biến sống ở một nơi khác trong bộ nhớ so với khi nó được phân bổ ban đầu? Toàn bộ hoạt động kinh doanh bất biến trong Python này dường như được nhấn mạnh quá mức.

Đây là những điều khác nhau. Khả năng tương tác không liên quan đến nơi lưu trữ trong bộ nhớ; nó có nghĩa là những thứ nó chỉ ra không thể thay đổi.

Các đối tượng Python không thể thay đổi vị trí sau khi chúng được tạo, có thể thay đổi hoặc không. (Chính xác hơn, giá trị của id () không thể thay đổi - trong thực tế, điều tương tự.) Bộ lưu trữ bên trong của các đối tượng có thể thay đổi có thể thay đổi, nhưng đó là một chi tiết triển khai ẩn.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

Đây không phải là sửa đổi ("biến đổi") biến; nó tạo ra một biến mới có cùng tên và loại bỏ biến cũ. So sánh với một hoạt động đột biến:

>>> a = [1,2,3]
>>> id(a)
3084599212L
>>> a[1] = 5
>>> a
[1, 5, 3]
>>> id(a)
3084599212L

Như những người khác đã chỉ ra, điều này cho phép sử dụng các mảng làm chìa khóa cho từ điển và các cấu trúc dữ liệu khác cần sự bất biến.

Lưu ý rằng các khóa cho từ điển không phải là hoàn toàn bất biến. Chỉ một phần của nó được sử dụng như là một chìa khóa cần phải bất biến; Đối với một số sử dụng, đây là một sự khác biệt quan trọng. Ví dụ: bạn có thể có một lớp đại diện cho một người dùng, so sánh sự bình đẳng và hàm băm theo tên người dùng duy nhất. Sau đó, bạn có thể treo dữ liệu có thể thay đổi khác trên lớp - "người dùng đã đăng nhập", v.v. Vì điều này không ảnh hưởng đến sự bình đẳng hoặc hàm băm, nên có thể sử dụng nó làm khóa trong từ điển. Điều này không quá phổ biến trong Python; Tôi chỉ nêu ra vì một số người đã tuyên bố rằng các khóa cần phải là "bất biến", điều này chỉ đúng một phần. Mặc dù vậy, tôi đã sử dụng nhiều lần với các bản đồ và bộ C ++.


>>> a = [1,2,3] >>> id (a) 3084599212L >>> a [1] = 5 >>> a [1, 5, 3] >>> id (a) 3084599212L Bạn ' vừa sửa đổi một loại dữ liệu có thể thay đổi, vì vậy nó không có ý nghĩa liên quan đến câu hỏi ban đầu. x = 'hello "id (x) 12345 x =" goodbye "id (x) 65432 Ai quan tâm xem đó có phải là một đối tượng mới hay không. Miễn là x chỉ vào dữ liệu tôi đã gán, đó mới là vấn đề.
pyNewGuy

4
Bạn đang bối rối vượt quá khả năng của tôi để giúp bạn.
Glenn Maynard

+1 để chỉ ra sự nhầm lẫn trong các câu hỏi phụ, dường như là nguồn khó khăn chính trong việc nhận thức giá trị của các bộ dữ liệu.
outis

1
Nếu tôi có thể, +1 khác để chỉ ra rằng phiếu tự đánh giá thực sự cho các khóa là liệu đối tượng có thể được băm hay không ( docs.python.org/glossary.html#term-hashable ).
outis

7

Như gnibbler đưa ra trong một bình luận, Guido có một ý kiến không được chấp nhận / đánh giá cao hoàn toàn: Các danh sách trên các dữ liệu đồng nhất, các bộ dữ liệu dành cho dữ liệu không đồng nhất. Tất nhiên, nhiều người trong số những người phản đối giải thích điều này có nghĩa là tất cả các yếu tố của một danh sách nên cùng loại.

Tôi thích nhìn thấy nó khác đi, không giống như những người khác cũng có trong quá khứ:

blue= 0, 0, 255
alist= ["red", "green", blue]

Lưu ý rằng tôi coi alist là đồng nhất, ngay cả khi loại (alist [1])! = Type (alist [2]).

Nếu tôi có thể thay đổi thứ tự của các phần tử và tôi sẽ không gặp vấn đề gì trong mã của mình (ngoài các giả định, ví dụ, thì nó nên được sắp xếp,), thì nên sử dụng một danh sách. Nếu không (như trong tuple blueở trên), thì tôi nên sử dụng một tuple.


Nếu tôi có thể tôi sẽ bỏ phiếu trả lời này lên 15 lần. Đây chính xác là cách tôi cảm nhận về bộ dữ liệu.
Cấp cho Paul

6

Chúng rất quan trọng vì chúng đảm bảo cho người gọi rằng đối tượng mà chúng vượt qua sẽ không bị biến đổi. Nếu bạn làm điều này:

a = [1,1,1]
doWork(a)

Người gọi không đảm bảo giá trị của a sau cuộc gọi. Tuy nhiên,

a = (1,1,1)
doWorK(a)

Bây giờ bạn với tư cách là người gọi hoặc là người đọc mã này đều biết rằng a giống nhau. Bạn luôn có thể cho kịch bản này tạo một bản sao của danh sách và vượt qua điều đó nhưng bây giờ bạn đang lãng phí chu kỳ thay vì sử dụng một cấu trúc ngôn ngữ có ý nghĩa ngữ nghĩa hơn.


1
Đây là một tài sản rất thứ cấp của tuples. Có quá nhiều trường hợp bạn có một đối tượng có thể thay đổi mà bạn muốn chuyển đến một hàm và không được sửa đổi, cho dù đó là danh sách có sẵn hay một số lớp khác. Không có khái niệm "tham số const theo tham chiếu" trong Python (ví dụ: const foo & trong C ++). Các tuple tình cờ cung cấp cho bạn điều này nếu nó thuận tiện để sử dụng một tuple, nhưng nếu bạn đã nhận được một danh sách từ người gọi của mình, bạn có thực sự sẽ chuyển đổi nó thành một tuple trước khi chuyển nó đi nơi khác không?
Glenn Maynard

Tôi đồng ý với bạn về điều đó. Một tuple không giống như tát vào một từ khóa const. Quan điểm của tôi là tính bất biến của một tuple mang thêm ý nghĩa cho người đọc mã. Đưa ra một tình huống mà cả hai sẽ hoạt động và mong đợi của bạn là nó không nên thay đổi bằng cách sử dụng bộ dữ liệu sẽ thêm ý nghĩa bổ sung đó cho người đọc (trong khi vẫn đảm bảo nó)
Matthew Manela

a = [1,1,1] doWork (a) nếu dowork () được định nghĩa là def dowork (arg): arg = [0,0,0] gọi dowork () trong danh sách hoặc tuple có cùng kết quả
pyNewGuy


1

Câu hỏi của bạn (và nhận xét tiếp theo) tập trung vào việc id () thay đổi trong khi chuyển nhượng. Tập trung vào hiệu ứng tiếp theo này của sự khác biệt giữa thay thế đối tượng bất biến và sửa đổi đối tượng có thể thay đổi thay vì chính sự khác biệt có lẽ không phải là cách tiếp cận tốt nhất.

Trước khi chúng tôi tiếp tục, hãy đảm bảo rằng hành vi được thể hiện dưới đây là những gì bạn mong đợi từ Python.

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1[0] = 2
>>> print a2[0]
2

Trong trường hợp này, nội dung của a2 đã được thay đổi, mặc dù chỉ a1 có giá trị mới được gán. Tương phản với những điều sau đây:

>>> a1 = (1,)
>>> a2 = a1
>>> print a2[0]
1
>>> a1 = (2,)
>>> print a2[0]
1

Trong trường hợp sau này, chúng tôi đã thay thế toàn bộ danh sách, thay vì cập nhật nội dung của nó. Với các loại bất biến như bộ dữ liệu, đây là hành vi duy nhất được phép.

Vì sao vấn đề này? Giả sử bạn có một lệnh:

>>> t1 = (1,2)
>>> d1 = { t1 : 'three' }
>>> print d1
{(1,2): 'three'}
>>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
>>> t1 = (2,3) ## creates a new tuple, does not modify the old one
>>> print d1   ## as seen here, the dict is still intact
{(1,2): 'three'}

Sử dụng một tuple, từ điển sẽ an toàn khi các khóa của nó được thay đổi "từ bên dưới" thành các mục băm thành một giá trị khác. Điều này là rất quan trọng để cho phép thực hiện hiệu quả.


Như những người khác đã chỉ ra, tính bất biến! = Khả năng băm. Không phải tất cả các bộ dữ liệu có thể được sử dụng làm khóa từ điển: {([1], [2]): 'value'} không thành công vì các danh sách có thể thay đổi trong bộ dữ liệu có thể bị thay đổi, nhưng {((1), (2)): ' giá trị '} là OK.
Ned Deily

Ned, điều đó đúng, nhưng tôi không chắc rằng sự khác biệt đó là nguyên nhân cho câu hỏi đang được hỏi.
Charles Duffy

@ K. bảng điểm đã thực sự có thể. Vấn đề được xác định chính xác, chắc chắn; giải pháp không hợp lệ.
Charles Duffy

@MichaelPuckettII, tương tự, xem ở trên.
Charles Duffy
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.