Tại sao không có toán tử ++ và - trong Python?


Câu trả lời:


443

Không phải vì nó không có ý nghĩa; nó có ý nghĩa hoàn hảo để định nghĩa "x ++" là "x + = 1, đánh giá ràng buộc trước đó của x".

Nếu bạn muốn biết lý do ban đầu, bạn sẽ phải lội qua các danh sách gửi thư cũ của Python hoặc hỏi ai đó đã ở đó (ví dụ: Guido), nhưng thật dễ dàng để biện minh sau thực tế:

Tăng và giảm đơn giản không cần nhiều như trong các ngôn ngữ khác. Bạn không viết những thứ như for(int i = 0; i < 10; ++i)trong Python rất thường xuyên; thay vì bạn làm những việc như thế for i in range(0, 10).

Vì nó không cần thiết gần như thường xuyên, nên có ít lý do hơn để cung cấp cho nó cú pháp đặc biệt của riêng nó; khi bạn cần tăng, +=thường là tốt.

Đó không phải là một quyết định về việc liệu nó có ý nghĩa hay không, hay liệu nó có thể được thực hiện hay không - và nó có thể. Đó là một câu hỏi về việc liệu lợi ích có đáng để thêm vào cú pháp cốt lõi của ngôn ngữ. Hãy nhớ rằng, đây là bốn toán tử - postinc, postdec, preinc, preec, và mỗi trong số chúng sẽ cần phải có quá tải lớp riêng; tất cả chúng cần được chỉ định, và thử nghiệm; nó sẽ thêm opcodes vào ngôn ngữ (ngụ ý một công cụ VM lớn hơn và do đó chậm hơn); mỗi lớp hỗ trợ gia tăng logic sẽ cần phải thực hiện chúng (trên đầu trang +=-=).

Đây là tất cả dư thừa với +=-=, vì vậy nó sẽ trở thành một khoản lỗ ròng.


98
Nó thường hữu ích khi sử dụng một cái gì đó như mảng [i ++], không được thực hiện một cách gọn gàng với + = / - =.
Turner Hayes

102
@thayes: Đó không phải là một mẫu phổ biến trong Python.
Glenn Maynard

9
@thayes Vì đó sẽ nằm trong một vòng lặp, bạn cũng có thể lặp lại itrực tiếp - nếu bạn thực sự cần nó và không thể chỉ sử dụngarray.append()
Tobias Kienzler

31
Tôi thấy mối quan tâm lớn hơn nhiều là khả năng đọc và dự đoán. Quay trở lại những ngày C của tôi, tôi đã thấy quá nhiều lỗi xuất phát từ những hiểu lầm về sự khác biệt giữa i++++i...
Charles Duffy

5
Thêm vào để biện minh cho thực tế: trong một dự án tôi đang thực hiện, tôi đã gặp (nhiều hơn bất kỳ ai trong cuộc sống của họ) một số lượng mã C ++ hợp lý bị các vấn đề ++--được sử dụng theo cách dẫn đến không xác định hoặc không xác định hành vi. Họ làm cho nó có thể viết mã phân tích phức tạp, khó chính xác.
Brian Vandenberg

84

Câu trả lời ban đầu mà tôi đã viết là một huyền thoại từ văn hóa dân gian về điện toán : được Dennis Ritchie gỡ lỗi là "không thể về mặt lịch sử" như đã lưu ý trong các bức thư gửi cho các biên tập viên của Truyền thông ACM tháng 7 năm 2012 : 10.1145 / 2209249.2209251


Các toán tử tăng / giảm C được phát minh tại thời điểm trình biên dịch C không thông minh lắm và các tác giả muốn có thể chỉ định mục đích trực tiếp rằng một toán tử ngôn ngữ máy nên được sử dụng để lưu một số chu kỳ cho trình biên dịch có thể làm một

load memory
load 1
add
store memory

thay vì

inc memory 

và PDP-11 thậm chí còn hỗ trợ các hướng dẫn "tự động tăng tốc" và "tự động hoãn lại" tương ứng với *++p*p++, tương ứng. Xem phần 5.3 của hướng dẫn nếu tò mò khủng khiếp.

Vì trình biên dịch đủ thông minh để xử lý các thủ thuật tối ưu hóa cấp cao được tích hợp theo cú pháp của C, giờ đây chúng chỉ là một tiện ích cú pháp.

Python không có thủ thuật để truyền đạt ý định cho trình biên dịch vì nó không sử dụng.


4
Javascript có ++. Tôi không nghĩ đó là một "mẹo để truyền đạt ý định cho nhà lắp ráp." Thêm vào đó, Python không có mã byte. Vì vậy, tôi nghĩ rằng lý do là một cái gì đó khác.
Nathan Davis

13
Doanh nghiệp "cung cấp gợi ý cho trình biên dịch" này thực sự là một huyền thoại. Thành thật mà nói, đó là một bổ sung ngu ngốc cho bất kỳ ngôn ngữ nào và nó vi phạm hai giới luật sau: 1. Bạn không mã cho máy tính đọc, bạn mã cho một kỹ sư khác đọc. 2. Bạn không mã cho một kỹ sư có thẩm quyền đọc, bạn viết mã cho một kỹ sư có thẩm quyền để đọc trong khi kiệt sức lúc 3 giờ sáng và nhảy lên caffeine.

3
@ tgm1024 Để công bằng, khi mã hóa trên 10 ký tự3030 mỗi giây, teletype bán song công, bạn mã hóa để bạn có thể nhập mã trước vào tuần tới.
msw

4
@ tgm1024 Unix và C đã thấy phần lớn sự phát triển ban đầu của họ trên PDP-11 sử dụng các kiểu điện thoại cực kỳ chậm để liên lạc với người dùng. Mặc dù bạn đã chết ngay rằng ngày nay việc mã hóa cho máy chủ yếu là ngớ ngẩn, nhưng trước đó, đó là giao diện Người / Máy vốn là nút cổ chai. Thật khó để tưởng tượng làm việc chậm mà nếu bạn không bao giờ phải làm.
msw

65

Tôi luôn cho rằng nó phải làm với dòng zen của python:

Nên có một - và tốt nhất là chỉ có một - cách rõ ràng để làm điều đó.

x ++ và x + = 1 làm cùng một điều chính xác, vì vậy không có lý do gì để có cả hai.


10
one--bằng không?
Andre Holzner

25
one--là một trong câu, nhưng không ngay sau đó. Vì vậy, 'công án' này cũng gợi ý rằng các toán tử tăng / giảm là không rõ ràng.
Victor K

14
@EralpB Nếu bạn xóa + =, thì bạn không thể thực hiện những việc như x + = 10. + = là trường hợp tổng quát hơn của ++
Rory

8
Ngoài ra: "Rõ ràng là tốt hơn ngầm".
Ber

2
Chắc chắn là không giống nhau, bởi vì x + = 1 KHÔNG phải là biểu thức - đó là một tuyên bố - và nó không đánh giá bất cứ điều gì. Bạn không thể làm những thứ như: 'row [col ++] = a; hàng [col ++] = b '. Chưa kể các công cụ pre-inc và post-inc mà c ++ có.
Uri

38

Tất nhiên, chúng ta có thể nói "Guido chỉ quyết định theo cách đó", nhưng tôi nghĩ câu hỏi thực sự là về lý do cho quyết định đó. Tôi nghĩ rằng có một số lý do:

  • Nó trộn lẫn các tuyên bố và biểu thức, đó là thực hành không tốt. Xem http://norvig.com/python-iaq.html
  • Nó thường khuyến khích mọi người viết mã ít đọc hơn
  • Sự phức tạp thêm trong việc triển khai ngôn ngữ, điều không cần thiết trong Python, như đã đề cập

12
Vui mừng cuối cùng ai đó đã đề cập đến tuyên bố vs khía cạnh biểu hiện. Trong C gán là một biểu thức và do đó nó là toán tử ++. Trong Python gán là một câu lệnh, vì vậy nếu nó có ++, thì nó cũng có thể cần phải là một câu lệnh gán (và thậm chí ít hữu ích hơn hoặc cần thiết).
martineau

Đồng ý - nếu chúng là các tuyên bố, thì ở mức tối thiểu sẽ trở nên hoàn toàn vô nghĩa khi nói về sự khác biệt giữa các nhà khai thác sau và trước.
Crowman

17

Bởi vì, trong Python, số nguyên là bất biến (int's + = thực sự trả về một đối tượng khác).

Ngoài ra, với ++ / - bạn cần phải lo lắng về việc tăng / giảm sau so với sau tăng và chỉ cần thêm một lần nhấn phím để viết x+=1. Nói cách khác, nó tránh được sự nhầm lẫn tiềm ẩn với chi phí thu được rất ít.


ints là bất biến trong C là tốt. Nếu bạn không nghĩ vậy, hãy thử để có được trình biên dịch C của bạn để tạo mã cho 42++... Một cái gì đó như thế này (sửa đổi một hằng số theo nghĩa đen) đã thực sự có thể trong một số trình biên dịch Fortran cũ (hoặc vì vậy tôi đã đọc): Tất cả các mục đích sử dụng trong tương lai nghĩa đen trong chương trình đó sẽ thực sự có giá trị khác. Chúc mừng gỡ lỗi!
Lutz Prechelt

10
Đúng. 42 là một hằng số theo nghĩa đen . Các hằng số là (hoặc ít nhất nên là) bất biến. Điều đó không có nghĩa là C ints nói chung là bất biến. Một inttrong C chỉ đơn giản là chỉ định một vị trí trong bộ nhớ. Và các bit ở nơi đó là rất nhiều đột biến. Ví dụ, bạn có thể tạo một tham chiếu của một intvà thay đổi tham chiếu của tham chiếu đó. Thay đổi này có thể nhìn thấy trong tất cả các tham chiếu (bao gồm cả intbiến ban đầu ) đến địa điểm đó. Điều tương tự không giữ cho một đối tượng số nguyên Python.
Nathan Davis

x + = 1 không kết thúc trở thành inc mem. Python thực hiện một lệnh gọi hàm để thêm 1 vào một biến; thật thảm hại.
PalaDolphin

12

Trong trẻo!

Python rất nhiều về sự rõ ràng và không có lập trình viên nào có thể đoán chính xác ý nghĩa của --atrừ khi họ đã học một ngôn ngữ có cấu trúc đó.

Python cũng rất nhiều về việc tránh các cấu trúc mời các lỗi và các ++toán tử được biết đến là nguồn lỗi phong phú. Hai lý do này là không đủ để có các toán tử đó trong Python.

Quyết định mà Python sử dụng thụt lề để đánh dấu các khối thay vì các phương tiện cú pháp như một số hình thức bắt đầu / kết thúc hoặc đánh dấu kết thúc bắt buộc chủ yếu dựa trên cùng các cân nhắc.

Để minh họa, hãy xem cuộc thảo luận xung quanh việc giới thiệu một toán tử có điều kiện (trong C cond ? resultif : resultelse:) cho Python vào năm 2005. Đọc ít nhất tin nhắn đầu tiênthông điệp quyết định của cuộc thảo luận đó (có một số tiền thân về cùng một chủ đề trước đó).

Thông tin bên lề: PEP thường được đề cập trong đó là "Đề xuất mở rộng Python" PEP 308 . LC có nghĩa là hiểu danh sách , GE có nghĩa là biểu thức trình tạo (và đừng lo lắng nếu những điều đó làm bạn bối rối, chúng không phải là một trong số ít các điểm phức tạp của Python).


10

Hiểu biết của tôi về lý do tại sao python không có ++toán tử theo sau: Khi bạn viết điều này trong python, a=b=c=1bạn sẽ nhận được ba biến (nhãn) trỏ vào cùng một đối tượng (giá trị là 1). Bạn có thể xác minh điều này bằng cách sử dụng chức năng id sẽ trả về địa chỉ bộ nhớ đối tượng:

In [19]: id(a)
Out[19]: 34019256

In [20]: id(b)
Out[20]: 34019256

In [21]: id(c)
Out[21]: 34019256

Tất cả ba biến (nhãn) trỏ đến cùng một đối tượng. Bây giờ tăng một trong các biến và xem nó ảnh hưởng đến địa chỉ bộ nhớ như thế nào:

In [22] a = a + 1

In [23]: id(a)
Out[23]: 34019232

In [24]: id(b)
Out[24]: 34019256

In [25]: id(c)
Out[25]: 34019256

Bạn có thể thấy biến đó abây giờ trỏ đến một đối tượng khác là các biến bc. Bởi vì bạn đã sử dụng a = a + 1nó rõ ràng rõ ràng. Nói cách khác, bạn gán hoàn toàn một đối tượng khác cho nhãn a. Hãy tưởng tượng rằng bạn có thể viết a++nó sẽ gợi ý rằng bạn không gán cho ađối tượng mới thay đổi mà ratter tăng đối tượng cũ. Tất cả những thứ này là IMHO để giảm thiểu nhầm lẫn. Để hiểu rõ hơn, hãy xem cách các biến python hoạt động:

Trong Python, tại sao một hàm có thể sửa đổi một số đối số theo cảm nhận của người gọi, mà không phải là các đối số khác?

Là Python gọi theo giá trị hoặc gọi theo tham chiếu? Cũng không.

Python vượt qua giá trị hay tham chiếu?

Là Python pass-by-Reference hay pass-by-value?

Python: Làm thế nào để tôi vượt qua một biến bằng cách tham chiếu?

Hiểu các biến Python và Quản lý bộ nhớ

Mô phỏng hành vi vượt qua giá trị trong python

Các hàm Python gọi theo tham chiếu

Mã như một Pythonista: Python thành ngữ


9

Nó chỉ được thiết kế theo cách đó. Toán tử tăng và giảm chỉ là các phím tắt cho x = x + 1. Python thường áp dụng chiến lược thiết kế nhằm giảm số lượng phương tiện thay thế để thực hiện một thao tác. Nhiệm vụ tăng cường là thứ gần nhất với các toán tử tăng / giảm trong Python và chúng thậm chí không được thêm cho đến Python 2.0.


3
Yeah mate, như, bạn có thể thay thế return a[i++]bằng return a[i=i+1].
Luís de Sousa

7

Tôi rất mới với python nhưng tôi nghi ngờ lý do là vì sự nhấn mạnh giữa các đối tượng có thể thay đổi và bất biến trong ngôn ngữ. Bây giờ, tôi biết rằng x ++ có thể dễ dàng được hiểu là x = x + 1, nhưng nó LOOKS giống như bạn đang gia tăng tại chỗ một đối tượng có thể là bất biến.

Chỉ là phỏng đoán / cảm giác / linh cảm của tôi.


Trong khía cạnh này, x++gần với x += 1hơn x = x + 1, hai điều này tạo ra sự khác biệt cũng như trên các đối tượng có thể thay đổi.
glglgl

4

Đầu tiên, Python chỉ bị ảnh hưởng gián tiếp bởi C; nó bị ảnh hưởng nặng nề bởi ABC , dường như không có các toán tử này , vì vậy cũng không có gì đáng ngạc nhiên khi không tìm thấy chúng trong Python.

Thứ hai, như những người khác đã nói, tăng và giảm được hỗ trợ bởi +=-=đã.

Thứ ba, hỗ trợ đầy đủ cho bộ ++--toán tử thường bao gồm hỗ trợ cả phiên bản tiền tố và hậu tố của chúng. Trong C và C ++, điều này có thể dẫn đến tất cả các loại cấu trúc "đáng yêu" dường như (đối với tôi) chống lại tinh thần đơn giản và thẳng thắn mà Python nắm lấy.

Ví dụ, trong khi câu lệnh C while(*t++ = *s++);có vẻ đơn giản và thanh lịch đối với một lập trình viên có kinh nghiệm, với một người học nó, nó là bất cứ điều gì nhưng đơn giản. Ném vào một hỗn hợp của tăng và giảm tiền tố và hậu tố, và thậm chí nhiều ưu điểm sẽ phải dừng lại và suy nghĩ một chút.


3

Tôi tin rằng nó bắt nguồn từ tín ngưỡng Python rằng "rõ ràng là tốt hơn ngầm".


Chà, bạn không viết rõ ràng các câu lệnh "bắt đầu" và "kết thúc" bằng Python, phải không? Mặc dù tôi đồng ý với tuyên bố, tôi nghĩ rằng có những ranh giới cho điều đó. Trong khi chúng ta có thể tranh luận về ranh giới đó, tôi nghĩ tất cả chúng ta đều có thể đồng ý, rằng có một ranh giới, không thực tế để vượt qua. Và vì có rất nhiều ý kiến ​​và biện minh cho quyết định đó, tôi không nghĩ rằng đó là một lựa chọn rõ ràng. Ít nhất, tôi không thể tìm thấy một nguồn, nơi nó được nêu rõ ràng
Hasan Ammori

3

Điều này có thể là do @GlennMaynard đang xem xét vấn đề so với các ngôn ngữ khác, nhưng trong Python, bạn làm mọi thứ theo cách trăn. Đó không phải là một câu hỏi "tại sao". Nó ở đó và bạn có thể làm mọi thứ với cùng hiệu quả x+=. Trong The Zen of Python , nó được đưa ra: "chỉ nên có một cách để giải quyết vấn đề". Nhiều lựa chọn là tuyệt vời trong nghệ thuật (tự do ngôn luận) nhưng tệ hại trong kỹ thuật.


2

Các ++lớp của toán tử là biểu thức với tác dụng phụ. Đây là một cái gì đó thường không tìm thấy trong Python.

Vì lý do tương tự, một bài tập không phải là một biểu thức trong Python, do đó ngăn chặn if (a = f(...)) { /* using a here */ }thành ngữ chung .

Cuối cùng tôi nghi ngờ rằng có toán tử không phù hợp lắm với ngữ nghĩa tham chiếu của Pythons. Hãy nhớ rằng, Python không có biến (hoặc con trỏ) với ngữ nghĩa được biết đến từ C / C ++.


1
không có gì ngăn cản việc gọi một hàm với hiệu ứng phụ trong việc hiểu / kiểm tra biểu thức / danh sách: f(a)nơi alà một danh sách, một số đối tượng không thay đổi.
Jean-François Fabre

1

Có lẽ một câu hỏi tốt hơn sẽ là hỏi tại sao các toán tử này tồn tại trong C. K & R gọi các toán tử tăng và giảm 'bất thường' (Phần 2.8 trang 46). Giới thiệu gọi chúng là 'ngắn gọn hơn và thường hiệu quả hơn'. Tôi nghi ngờ rằng thực tế là các hoạt động này luôn xuất hiện trong thao tác con trỏ cũng đã đóng một phần trong phần giới thiệu của chúng. Trong Python, có lẽ đã quyết định rằng sẽ không có ý nghĩa gì khi cố gắng tối ưu hóa gia số (thực tế tôi mới thực hiện một thử nghiệm trong C và có vẻ như hội đồng tạo gcc sử dụng addl thay vì bao gồm cả hai trường hợp) và không có con trỏ số học; vì vậy nó sẽ chỉ là một cách khác để làm điều đó và chúng ta biết Python ghê tởm điều đó.


1

vì tôi hiểu nó nên bạn sẽ không nghĩ giá trị trong bộ nhớ bị thay đổi. trong c khi bạn làm x ++, giá trị của x trong bộ nhớ thay đổi. nhưng trong python tất cả các số là bất biến do đó địa chỉ mà x chỉ là vẫn không có x + 1. Khi bạn viết x ++, bạn sẽ nghĩ rằng x thay đổi những gì thực sự xảy ra là x điều chỉnh được thay đổi thành một vị trí trong bộ nhớ trong đó x + 1 được lưu trữ hoặc tạo lại vị trí này nếu không tồn tại.


1
Vì vậy, những gì làm cho điều này ++và khác với += 1?
Ber

1

Để hoàn thành câu trả lời tốt trên trang đó:

Giả sử chúng ta quyết định làm điều này, tiền tố ( ++i) sẽ phá vỡ các toán tử + và - unary.

Ngày nay, tiền tố bằng ++hoặc --không làm gì cả, vì nó cho phép toán tử unary plus hai lần (không làm gì) hoặc trừ unary hai lần (hai lần: tự hủy)

>>> i=12
>>> ++i
12
>>> --i
12

Vì vậy, nó có khả năng phá vỡ logic đó.


1

Các câu trả lời khác đã mô tả lý do tại sao nó không cần thiết cho các trình vòng lặp, nhưng đôi khi nó rất hữu ích khi gán để tăng một biến trong dòng, bạn có thể đạt được hiệu ứng tương tự bằng cách sử dụng bộ dữ liệu và nhiều phép gán:

b = ++a trở thành:

a,b = (a+1,)*2

b = a++trở thành:

a,b = a+1, a

Python 3.8 giới thiệu :=toán tử gán , cho phép chúng ta đạt được foo(++a)với

foo(a:=a+1)

foo(a++) vẫn còn khó nắm bắt


2
: = chuyển nhượng là một sự ô nhục
nehem

0

Tôi nghĩ rằng điều này liên quan đến các khái niệm về tính đột biến và tính bất biến của các đối tượng. 2,3,4,5 là bất biến trong trăn. Tham khảo hình ảnh dưới đây. 2 đã cố định id cho đến khi quá trình python này.

ID của hằng và biến

x ++ về cơ bản có nghĩa là gia tăng tại chỗ như C. Trong C, x ++ thực hiện gia số tại chỗ. Vì vậy, x = 3 và x ++ sẽ tăng 3 trong bộ nhớ lên 4, không giống như python trong đó 3 vẫn tồn tại trong bộ nhớ.

Do đó, trong python, bạn không cần tạo lại giá trị trong bộ nhớ. Điều này có thể dẫn đến tối ưu hóa hiệu suất.

Đây là một câu trả lời dựa trên linh cảm.


0

Tôi biết đây là một luồng cũ, nhưng trường hợp sử dụng phổ biến nhất cho ++ i không được đề cập, đó là các bộ chỉ mục thủ công khi không có chỉ mục được cung cấp. Tình huống này là lý do tại sao python cung cấp liệt kê ()

Ví dụ: Trong bất kỳ ngôn ngữ nào, khi bạn sử dụng cấu trúc như foreach để lặp lại một tập hợp - vì lợi ích của ví dụ, chúng tôi thậm chí sẽ nói đó là một tập hợp không có thứ tự và bạn cần một chỉ mục duy nhất cho mọi thứ để phân biệt chúng, nói

i = 0
stuff = {'a': 'b', 'c': 'd', 'e': 'f'}
uniquestuff = {}
for key, val in stuff.items() :
  uniquestuff[key] = '{0}{1}'.format(val, i)
  i += 1

Trong trường hợp như thế này, python cung cấp một phương thức liệt kê, ví dụ

for i, (key, val) in enumerate(stuff.items()) :
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.