Hành vi phản trực giác của int () trong python


83

Trong tài liệu đã nói rõ rằng int (number) là một chuyển đổi loại sàn:

int(1.23)
1

và int (string) trả về một int nếu và chỉ khi chuỗi là một số nguyên theo nghĩa đen.

int('1.23')
ValueError

int('1')
1

Có lý do đặc biệt nào cho điều đó không? Tôi thấy nó phản trực giác rằng các tầng chức năng trong một trường hợp, nhưng không phải trường hợp khác.

Câu trả lời:


123

Không có lý do đặc biệt . Python chỉ đơn giản là áp dụng nguyên tắc chung của nó là không thực hiện các chuyển đổi ngầm, đây là nguyên nhân nổi tiếng của các vấn đề, đặc biệt là đối với người mới, trong các ngôn ngữ như Perl và Javascript.

int(some_string)là một yêu cầu rõ ràng để chuyển đổi một chuỗi sang định dạng số nguyên; các quy tắc cho chuyển đổi này chỉ định rằng chuỗi phải chứa một đại diện chữ số nguyên hợp lệ. int(float)là một yêu cầu rõ ràng để chuyển đổi một số thực thành một số nguyên; các quy tắc cho chuyển đổi này chỉ định rằng phần phân số của float sẽ bị cắt bớt.

Để int("3.1459")trả về 3trình thông dịch sẽ phải chuyển đổi hoàn toàn chuỗi thành một float. Vì Python không hỗ trợ chuyển đổi ngầm định, thay vào đó, nó chọn đưa ra một ngoại lệ.


type(3)lợi nhuận <type int>. Tuy nhiên, trăn không phàn nàn về điều đó float("3"). Không phải python đang ngầm chuyển đổi chuỗi thành int và sau đó thành float?
franksands

Số "3" là một giá trị dấu phẩy động hợp lệ mặc dù theo nghĩa đen của chương trình, nó sẽ được hiểu là một số nguyên. Không cần chuyển đổi số nguyên.
holdenweb

75

Đây gần như chắc chắn là một trường hợp áp dụng ba trong số các nguyên tắc từ Zen của Python :

Rõ ràng là tốt hơn ngầm.

[...] tính thực tế đánh bại sự thuần khiết

Lỗi không bao giờ nên trôi qua một cách âm thầm

Một số phần trăm thời gian, ai đó đang int('1.23')gọi chuyển đổi sai cho trường hợp sử dụng của họ và muốn một cái gì đó tương tự floathoặc decimal.Decimalthay thế. Trong những trường hợp này, rõ ràng tốt hơn là họ nhận được một lỗi ngay lập tức để họ có thể sửa chữa, thay vì âm thầm đưa ra giá trị sai.

Trong trường hợp đó bạn làm muốn truncate rằng đến một int, nó là tầm thường để làm một cách rõ ràng như vậy bằng cách đi qua nó thông qua floatđầu tiên, và sau đó gọi một trong những int, round, trunc, floorhoặc ceilcho phù hợp. Điều này cũng làm cho mã của bạn tự lập tài liệu hơn, đề phòng việc sửa đổi sau này "sửa" intlời gọi cắt ngắn âm thầm giả định floatbằng cách làm rõ rằng giá trị làm tròn giá trị bạn muốn.


Tôi nghĩ những nguyên tắc đó đã được áp dụng từ lâu trước khi Thiền được hình thành, nhưng theo cách nào đó thì cả hai sẽ có vẻ hòa hợp.
holdenweb

17

Đôi khi một thử nghiệm suy nghĩ có thể hữu ích.

  • Hành vi A: int('1.23')không thành công với một lỗi. Đây là hành vi hiện có.
  • Hành vi B: int('1.23')sản xuất 1không có lỗi. Đây là những gì bạn đang đề xuất.

Với hành vi A, thật đơn giản và nhỏ nhặt để có được hiệu quả của hành vi B: sử dụng int(float('1.23'))thay thế.

Mặt khác, với hành vi B, việc nhận được hậu quả của hành vi A phức tạp hơn đáng kể:

def parse_pure_int(s):
    if "." in s:
        raise ValueError("invalid literal for integer with base 10: " + s)
    return int(s)

(và ngay cả với đoạn mã ở trên, tôi không hoàn toàn tin tưởng rằng không có trường hợp góc nào mà nó xử lý sai.)

Do đó, hành vi A được thể hiện nhiều hơn hành vi B.

Một điều khác cần xem xét: '1.23'là một biểu diễn chuỗi của một giá trị dấu phẩy động. Chuyển đổi '1.23'thành số nguyên về mặt khái niệm bao gồm hai chuyển đổi (chuỗi chuyển thành số nguyên), nhưng int(1.23)int('1')mỗi chuyển đổi chỉ liên quan đến một chuyển đổi.


Biên tập:

Và thực sự, có những trường hợp góc mà đoạn mã trên không xử lý được: 1e-21E-2cả hai đều là giá trị dấu phẩy động.


Để làm rõ: Tôi sẽ không đề xuất hành vi B, bởi vì điều đó chỉ nguy hiểm, như bạn và những người khác đã nêu. Tôi không chắc có một giải pháp tốt hơn giải pháp hiện tại. Một tùy chọn sẽ là đặt các tên khác nhau cho các hàm, nhưng đó chỉ là nhiều thứ hơn để nhập. Giải pháp rõ ràng của việc có int (1.23) không thành công và chỉ int (float-with-no-decimal-place) trả về một số nguyên không có ý nghĩa gì trong một ngôn ngữ được nhập động.
StefanS

1
Trường hợp góc có thể là int('123E-2')hoặc int('1L').
Jared Goguen

11

Nói một cách đơn giản - chúng không có cùng chức năng.

  • int (decimal) hoạt động như 'sàn, tức là loại bỏ phần thập phân và trả về dưới dạng int'
  • int (string) hoạt động như 'văn bản này mô tả một số nguyên, chuyển đổi nó và trả về dưới dạng int'.

Chúng là 2 hàm khác nhau có cùng tên trả về một số nguyên nhưng chúng là các hàm khác nhau.

'int' ngắn gọn và dễ nhớ và ý nghĩa của nó được áp dụng cho từng kiểu rất trực quan đối với hầu hết các lập trình viên, đó là lý do tại sao họ chọn nó.

Không có ngụ ý rằng họ đang cung cấp cùng một chức năng hoặc kết hợp, họ chỉ đơn giản là có cùng tên và trả về cùng một kiểu. Chúng có thể dễ dàng được gọi là 'floorDecimalAsInt' và 'convertStringToInt', nhưng chúng dùng cho 'int' vì nó dễ nhớ, (99%) trực quan và hiếm khi xảy ra nhầm lẫn.

Phân tích cú pháp văn bản dưới dạng Số nguyên cho văn bản bao gồm dấu thập phân chẳng hạn như "4,5" sẽ gây ra lỗi trong phần lớn các ngôn ngữ máy tính và phần lớn các lập trình viên dự kiến ​​sẽ gây ra lỗi vì giá trị văn bản không đại diện cho số nguyên và ngụ ý họ đang cung cấp dữ liệu sai


2
Vậy tại sao hai 'chức năng khác nhau' lại có cùng tên? Nghe giống như vi phạm một số điều vô nghĩa của thiền.
hobbs

vì tên có ý nghĩa với 2 chức năng khác nhau và ngắn gọn. Int-IFY một số thập phân (sàn), chuyển đổi một chuỗi đến một int (chuyển đổi)

Về mặt kỹ thuật, nó có thể hữu ích để nhớ rằng đó intlà một loại (và một loại tích hợp tại đó). Người tạo của nó ( __new__) nhận một số kiểu đối số có thể có. Hành vi của nó cho mỗi loại được xác định rõ ràng.
holdenweb

Câu trả lời này chỉ đơn giản là sai như đã nêu. intthực tế không phải là một hàm mà là một kiểu, có __new____init__các phương thức nhận đối số chuỗi hoặc float, xử lý từng đối số một cách thích hợp. Sẽ chính xác hơn nếu nói rằng kiểu xử lý hai kiểu đối số khác nhau, nhưng chỉ có một int.
holdenweb
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.