2009-03-31 59 views
29

Có cách nào đẹp hơn của cách làm như sau:Các câu lệnh thử lồng nhau trong python?

try: 
    a.method1() 
except AttributeError: 
    try: 
     a.method2() 
    except AttributeError: 
     try: 
      a.method3() 
     except AttributeError: 
      raise 

Nó trông khá khó chịu và tôi không muốn làm:

if hasattr(a, 'method1'): 
    a.method1() 
else if hasattr(a, 'method2'): 
    a.method2() 
else if hasattr(a, 'method3'): 
    a.method3() 
else: 
    raise AttributeError 

để duy trì hiệu quả tối đa.

+0

Bạn đã thử nghiệm lý thuyết của mình rằng tùy chọn thứ hai không hiệu quả? Nó sẽ làm tôi ngạc nhiên nếu nó không hiệu quả hơn cái đầu tiên. – Oddthinking

+0

Oddthinking có thể là chính xác. hasattr loại bỏ sự cần thiết cho một ngoại lệ được nâng lên. –

+1

Thực ra, việc thực thi hasattr() về cơ bản chỉ gọi getattr() và trả về False nếu một ngoại lệ được nâng lên; xem http://svn.python.org/view/python/tags/r254/Python/bltinmodule.c?view = markup – Miles

Trả lời

21

Có lẽ bạn có thể thử một cái gì đó như thế này:

def call_attrs(obj, attrs_list, *args): 
    for attr in attrs_list: 
     if hasattr(obj, attr): 
      bound_method = getattr(obj, attr) 
      return bound_method(*args) 

    raise AttributeError 

Bạn sẽ gọi nó là như thế này:

call_attrs(a, ['method1', 'method2', 'method3']) 

này sẽ cố gắng để gọi các phương pháp theo thứ tự họ đang ở trong danh sách. Nếu bạn muốn vượt qua bất kỳ đối số, bạn chỉ có thể vượt qua chúng cùng sau khi danh sách như vậy:

call_attrs(a, ['method1', 'method2', 'method3'], arg1, arg2) 
4

Làm thế nào để gói gọn các cuộc gọi trong một chức năng?

def method_1_2_or_3(): 
    try: 
     a.method1() 
     return 
    except AttributeError: 
     pass 
    try: 
     a.method2() 
     return 
    except AttributeError: 
     pass 
    try: 
     a.method3() 
    except AttributeError: 
     raise 
+1

Tại sao phần "đóng gói"? chỉ là 'pass' là một ý tưởng khá hay, có vẻ như với tôi. – cregox

1

Nếu bạn đang sử dụng đối tượng kiểu mới:

methods = ('method1','method2','method3') 
for method in methods: 
    try: 
     b = a.__getattribute__(method) 
    except AttributeError: 
     continue 
    else: 
     b() 
     break 
else: 
    # re-raise the AttributeError if nothing has worked 
    raise AttributeError 

Tất nhiên, nếu bạn aren' Khi sử dụng đối tượng kiểu mới, bạn có thể thử __dict__ thay vì __getattribute__.

EDIT: Mã này có thể chứng minh là một mớ hỗn độn đang la hét. Nếu không tìm thấy __getattribute__ hoặc __dict__, hãy đoán ngẫu nhiên loại lỗi nào được nêu ra.

+0

Chắc chắn sử dụng hàm getattr() thay cho phương thức __getattribute__. – Miles

+0

Tôi không thể tìm ra các ưu điểm tương đối của getattr so với __getattribute__. Có tồn tại các đối tượng mà sẽ tăng AttributeError và cái kia sẽ hoạt động. –

23

Một thay đổi nhỏ cho lần hai trông khá đẹp và đơn giản. Tôi thực sự nghi ngờ bạn sẽ nhận thấy bất kỳ sự khác biệt về hiệu năng giữa hai, và đây là một chút đẹp hơn so với một thử lồng/excepts

def something(a): 
    for methodname in ['method1', 'method2', 'method3']: 
     try: 
      m = getattr(a, methodname) 
     except AttributeError: 
      pass 
     else: 
      return m() 
    raise AttributeError 

Cách rất dễ đọc khác là để làm ..

def something(a): 
    try: 
     return a.method1() 
    except: 
     pass 

    try: 
     return a.method2() 
    except: 
     pass 

    try: 
     return a.method3() 
    except: 
     pass 

    raise AttributeError 

Trong khi dài, nó rất rõ ràng những gì các chức năng đang làm .. Hiệu suất thực sự không phải là một vấn đề (nếu một vài thử/trừ báo cáo làm chậm kịch bản của bạn xuống đáng chú ý, có lẽ là một vấn đề lớn hơn với cấu trúc kịch bản)

+1

Tôi thích thứ hai vì nó rất dễ đọc và dễ hiểu. Nếu hiệu suất thực sự là một vấn đề, áp phích ban đầu có thể làm điều gì đó sai. –

3

Một giải pháp nhỏ gọn:

getattr(a, 'method1', 
    getattr(a, 'method2', 
     getattr(a, 'method3')))() 
+1

Nhỏ gọn nhưng có thể sai. Nếu 'a' có' method1' nhưng * không * có 'method3', thì điều này sẽ thất bại. Đối số thứ ba cho 'getattr' được đánh giá trước khi' getattr' được gọi, có nghĩa là mã này cố gắng tìm nạp 'method3' trước khi nó xem xét' method1' và 'method2'. Xem [Câu trả lời của Ethan Furman] (http://stackoverflow.com/a/7971414/33732) để có giải pháp thay thế an toàn hơn. –

5
method = (
     getattr(a, 'method1', None) or 
     getattr(a, 'method2', None) or 
     getattr(a, 'method3') 
     ) 
method() 

này đầu tiên sẽ tìm kiếm method1, sau đó method2, sau đó method3. Tìm kiếm sẽ dừng ngay khi tìm thấy một trong số đó. Nếu không tìm thấy phương pháp nào trong số getattr cuối cùng sẽ tăng ngoại lệ.

Các vấn đề liên quan