Python Tha thứ so với sự cho phép và gõ vịt


44

Trong Python, tôi thường nghe rằng tốt hơn là "cầu xin sự tha thứ" (bắt ngoại lệ) thay vì "xin phép" (kiểm tra loại / điều kiện). Liên quan đến việc thực thi gõ vịt trong Python, đây có phải là

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

tốt hơn hoặc xấu hơn

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

về hiệu suất, khả năng đọc, "pythonic", hoặc một số yếu tố quan trọng khác?


17
có một lựa chọn thứ ba, đừng làm bất cứ điều gì và coi bất kỳ foo nào mà không có thanh là một lỗi
jk.

Tôi nhớ rằng nghe hasattrđược thực hiện với thử / bắt chính xác trong nội bộ. Không chắc chắn nếu đó là sự thật ... (nó sẽ hoạt động khác với các thuộc tính, phải không? Có lẽ tôi đang nghĩ đến getattr..)
Izkata

@Izkata: Việc triển khaihasattr không sử dụng tương đương API C getattr(trả về Truenếu thành công, Falsenếu không), nhưng xử lý các ngoại lệ trong C nhanh hơn rất nhiều.
Martijn Pieters

2
Tôi đã chấp nhận câu trả lời của martijn, nhưng tôi muốn nói thêm rằng nếu bạn đang cố gắng đặt thuộc tính, bạn chắc chắn nên xem xét sử dụng thử / bắt vì đó có thể là một thuộc tính không có setter, trong trường hợp hasattr sẽ là đúng , nhưng nó vẫn sẽ tăng AttributionError.
darkfeline

Câu trả lời:


60

Nó thực sự phụ thuộc vào mức độ thường xuyên bạn nghĩ rằng ngoại lệ sẽ được ném.

Cả hai cách tiếp cận, theo tôi, đều có giá trị như nhau, ít nhất là về khả năng đọc và tính chất pythonic. Nhưng nếu 90% đối tượng của bạn không có thuộc tính, barbạn sẽ nhận thấy sự khác biệt hiệu suất rõ rệt giữa hai cách tiếp cận:

>>> import timeit
>>> def askforgiveness(foo=object()):
...     try:
...         x = foo.bar
...     except AttributeError:
...         pass
... 
>>> def askpermission(foo=object()):
...     if hasattr(foo, 'bar'):
...         x = foo.bar
... 
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789

Nhưng nếu 90% của các đối tượng của bạn làm có thuộc tính, các bảng đã được quay:

>>> class Foo(object):
...     bar = None
... 
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541

Vì vậy, từ quan điểm hiệu suất, bạn cần chọn cách tiếp cận phù hợp nhất với hoàn cảnh của mình.

Cuối cùng, một số sử dụng chiến lược của timeitmô-đun có thể là điều Pythonic nhất bạn có thể làm.


1
Vài tuần trước tôi đã hỏi câu hỏi này: lập trình viên.stackexchange.com / questions / 161798 / Từ đó Tôi đã hỏi liệu trong các ngôn ngữ được gõ một cách lỏng lẻo bạn có phải làm thêm việc kiểm tra loại không, và tôi đã bị mọi người nói rằng bạn đã bắn phá. Biết tôi thấy bạn có.
Tulains Córdova

@ user1598390: Khi bạn xác định API mong đợi kết hợp các loại đồng nhất, bạn phải thực hiện một số thử nghiệm. Hầu hết thời gian, bạn không. Đây là khu vực cụ thể mà từ đó bạn không thể rút ra các quy tắc về mô hình nói chung, tôi sợ.
Martijn Pieters

Chà, bất kỳ sự phát triển hệ thống nghiêm túc nào cũng liên quan đến việc xác định API. Vì vậy, tôi đoán loại ngôn ngữ nghiêm ngặt là tốt nhất cho điều đó bởi vì bạn phải viết mã ít hơn vì trình biên dịch kiểm tra các loại cho bạn trong thời gian biên dịch.
Tulains Córdova

1
@GarethRees: nó thiết lập một mẫu cho nửa sau của câu trả lời mà tôi chuyển trong một đối số cho phép thử dưới hàm.
Martijn Pieters

1
Lưu ý rằng hasattr, dù sao thì nó cũng thực sự tương đương với C-api, ngoại trừ dưới mui xe, vì nó chỉ ra cách chung duy nhất để xác định xem một đối tượng có thuộc tính trong Python hay không là cố gắng truy cập nó.
user2357112

11

Trong python bạn thường có hiệu suất tốt hơn khi thực hiện mọi thứ theo cách của Python. Với các ngôn ngữ khác, sử dụng ngoại lệ cho kiểm soát luồng thường được coi là một ý tưởng tồi tệ vì các ngoại lệ thường áp đặt một chi phí bất thường. Nhưng vì kỹ thuật này được xác nhận rõ ràng bằng Python, trình thông dịch được tối ưu hóa cho loại mã này.

Như với tất cả các câu hỏi về hiệu suất, cách duy nhất để chắc chắn là lập hồ sơ mã của bạn. Viết cả hai phiên bản và xem cái nào chạy nhanh hơn. Mặc dù theo kinh nghiệm của tôi, "cách Python" thường là cách nhanh nhất.


3

Hiệu suất, tôi cảm thấy, là một mối quan tâm thứ cấp. Nếu nó phát sinh, một trình hồ sơ sẽ giúp bạn tập trung vào các nút thắt thực sự, có thể hoặc không thể là cách bạn đối xử với các lập luận bất hợp pháp có thể.

Tính dễ đọc và đơn giản, mặt khác, luôn là mối quan tâm chính. Không có quy tắc cứng ở đây, chỉ cần sử dụng phán đoán của bạn.

Đây là một vấn đề phổ quát, nhưng các quy ước cụ thể về môi trường hoặc ngôn ngữ có liên quan. Ví dụ, trong Python, thường chỉ cần sử dụng thuộc tính mà bạn mong đợi và để AttributionError có thể tiếp cận người gọi.


-1

Về mặt chính xác , tôi nghĩ rằng xử lý ngoại lệ là cách để đi (đôi khi tôi sử dụng cách tiếp cận hasattr () bản thân mình). Vấn đề cơ bản với việc dựa vào hasattr () là nó biến vi phạm hợp đồng mã thành thất bại thầm lặng (đây là một vấn đề lớn trong JavaScript, vốn không ném vào các thuộc tính không tồn tại).


3
Câu trả lời của bạn dường như không bổ sung nhiều hơn những gì đã được người khác nêu. Không giống như các trang web khác, lập trình viên mong đợi câu trả lời đi vào giải thích lý do tại sao đằng sau câu trả lời. Bạn chạm vào một điểm tốt với vấn đề thất bại thầm lặng, nhưng không cung cấp công lý cho nó. Đọc những điều sau đây có thể giúp ích: lập trình

Câu trả lời cuối cùng tôi đưa ra bị chỉ trích là quá rộng. Tôi nghĩ tôi sẽ thử ngắn gọn và súc tích.
Joe
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.