2010-05-26 31 views
12

Tôi không thể hiểu loại ngoại lệ nào tôi nên xử lý 'ở đây và bây giờ' và loại ngoại lệ nào tôi nên nâng cao hoặc không xử lý ở đây và làm với chúng sau này (trên tầng cao hơn). Ví dụ: tôi đã viết ứng dụng khách/máy chủ bằng python3 với giao tiếp ssl. Khách hàng là nghĩa vụ phải xác minh các tập tin trên bất kỳ sự khác biệt về họ, và nếu khác tồn tại sau đó nó sẽ gửi tập tin 'cập nhật' này đến máy chủ.Làm cách nào để xử lý chính xác ngoại lệ trong Python3


class BasicConnection: 
    #blablabla 
    def sendMessage(self, sock, url, port, fileToSend, buffSize): 
     try: 
      sock.connect((url, port)) 
      while True: 
       data = fileToSend.read(buffSize) 
       if not data: break 
       sock.send(data) 
      return True 
     except socket.timeout as toErr: 
      raise ConnectionError("TimeOutError trying to send File to remote socket: %s:%d" 
            % (url,port)) from toErr 
     except socket.error as sErr: 
      raise ConnectionError("Error trying to send File to remote socket: %s:%d" 
            % (url,port)) from sErr 
     except ssl.SSLError as sslErr: 
      raise ConnectionError("SSLError trying to send File to remote socket: %s:%d" 
            % (url,port)) from sslErr 
     finally: 
      sock.close() 

Có đúng cách để sử dụng ngoại lệ trong python không? Vấn đề là: nếu file.read() ném IOError thì sao? Tôi có nên xử lý nó ở đây, hoặc chỉ không làm gì và bắt nó sau này? Và nhiều ngoại lệ có thể khác?

  1. Khách hàng sử dụng lớp này (BasicConnection) để gửi các file cập nhật vào máy chủ:

class PClient(): 
    def __init__(self, DATA): 
     '''DATA = { 'sendTo'  : {'host':'','port':''}, 
        'use_ssl'  : {'use_ssl':'', 'fileKey':'', 'fileCert':'', 'fileCaCert':''}, 
        'dirToCheck' : '', 
        'localStorage': '', 
        'timeToCheck' : '', 
        'buffSize' : '', 
        'logFile'  : ''} ''' 
     self._DATA = DATA 
     self._running = False 
     self.configureLogging() 


    def configureLogging(self): 
     #blablabla 

    def isRun(self): 
     return self._running 

    def initPClient(self): 
     try: 
      #blablabla 

      return True 
     except ConnectionError as conErr: 
      self._mainLogger.exception(conErr) 
      return False 
     except FileCheckingError as fcErr: 
      self._mainLogger.exception(fcErr) 
      return False 
     except IOError as ioErr: 
      self._mainLogger.exception(ioErr) 
      return False 
     except OSError as osErr: 
      self._mainLogger.exception(osErr) 
      return False 


    def startPClient(self): 
     try: 
      self._running = True 
      while self.isRun(): 
       try : 
        self._mainLogger.debug("Checking differences") 
        diffFiles = FileChecker().checkDictionary(self._dict) 

        if len(diffFiles) != 0: 
         for fileName in diffFiles: 
          try: 
           self._mainLogger.info("Sending updated file: %s to remote socket: %s:%d" 
            % (fileName,self._DATA['sendTo']['host'],self._DATA['sendTo']['port'])) 
           fileToSend = io.open(fileName, "rb") 
           result = False 
           result = BasicConnection().sendMessage(self._sock, self._DATA['sendTo']['host'], 
                     self._DATA['sendTo']['port'], fileToSend, self._DATA['buffSize']) 
           if result: 
            self._mainLogger.info("Updated file: %s was successfully delivered to remote socket: %s:%d" 
            % (fileName,self._DATA['sendTo']['host'],self._DATA['sendTo']['port'])) 
          except ConnectionError as conErr: 
           self._mainLogger.exception(conErr) 
          except IOError as ioErr: 
           self._mainLogger.exception(ioErr) 
          except OSError as osErr: 
           self._mainLogger.exception(osErr) 

         self._mainLogger.debug("Updating localStorage %s from %s " %(self._DATA['localStorage'], self._DATA['dirToCheck'])) 
         FileChecker().updateLocalStorage(self._DATA['dirToCheck'], 
                 self._DATA['localStorage']) 
        self._mainLogger.info("Directory %s were checked" %(self._DATA['dirToCheck'])) 
        time.sleep(self._DATA['timeToCheck']) 
       except FileCheckingError as fcErr: 
        self._mainLogger.exception(fcErr) 
       except IOError as ioErr: 
        self._mainLogger.exception(ioErr) 
       except OSError as osErr: 
        self._mainLogger.exception(osErr) 
     except KeyboardInterrupt: 
      self._mainLogger.info("Shutting down...") 
      self.stopPClient() 
     except Exception as exc: 
      self._mainLogger.exception(exc) 
      self.stopPClient() 
      raise RuntimeError("Something goes wrong...") from exc 

    def stopPClient(self): 
     self._running = False 

Is it correct? Có thể ai đó dành thời gian riêng của mình và chỉ giúp tôi hiểu phong cách xử lý ngoại lệ của pythonic? Tôi không thể hiểu phải làm gì với những ngoại lệ như NameError, TypeError, KeyError, ValueError ... và cứ thế ....... Họ có thể bị ném vào bất kỳ tuyên bố nào, bất cứ lúc nào ... và phải làm gì với họ, nếu tôi muốn đăng nhập mọi thứ.

  1. Và thông tin nào mọi người thường đăng nhập? Nếu xảy ra lỗi, tôi nên đăng nhập thông tin gì? Tất cả traceback, hoặc chỉ là thông điệp có liên quan về nó hay cái gì khác?

  2. Tôi hy vọng có ai đó giúp tôi. Cảm ơn rất nhiều.

Trả lời

24

Nói chung, bạn nên "nắm bắt" các ngoại lệ mà bạn mong đợi xảy ra (vì chúng có thể do lỗi người dùng hoặc các vấn đề môi trường khác ngoài tầm kiểm soát của chương trình), đặc biệt nếu bạn biết mã của mình có thể làm được về chúng. Chỉ cần cung cấp thêm chi tiết trong báo cáo lỗi là một vấn đề cận biên, mặc dù một số thông số của chương trình có thể yêu cầu (ví dụ: máy chủ chạy dài không bị hỏng do các sự cố như vậy, nhưng thay vì đăng nhập nhiều thông tin trạng thái, hãy cung cấp người dùng giải thích tóm tắt và chỉ tiếp tục làm việc cho các truy vấn trong tương lai).

NameError, TypeError, KeyError, ValueError, SyntaxError, AttributeError, và như vậy, có thể được coi là do sai sót trong chương trình - lỗi, không vấn đề ngoài tầm kiểm soát của lập trình viên. Nếu bạn đang phát hành một thư viện hoặc khung, để mã của bạn sẽ được gọi bằng mã khác ngoài tầm kiểm soát của bạn, thì các lỗi như vậy có thể rất có thể nằm trong mã khác; bạn nên thường xuyên để cho các ngoại lệ tuyên truyền để giúp các lập trình viên khác gỡ lỗi của chính họ. Nếu bạn đang phát hành một ứng dụng, bạn sở hữu các lỗi và bạn phải chọn chiến lược giúp bạn tìm thấy chúng.

Nếu lỗi của bạn hiển thị trong khi người dùng cuối đang chạy chương trình, bạn nên ghi lại nhiều thông tin trạng thái và cung cấp cho người dùng giải thích tóm tắt và lời xin lỗi (có thể với yêu cầu gửi cho bạn thông tin nhật ký, nếu bạn không thể tự động hóa điều đó - hoặc, ít nhất, hãy yêu cầu sự cho phép trước khi bạn gửi bất kỳ thứ gì từ máy của người dùng đến máy của bạn). Bạn có thể lưu một số tác phẩm của người dùng cho đến nay, nhưng thường (trong một chương trình được biết là lỗi) có thể không hoạt động.

Hầu hết các lỗi sẽ hiển thị trong quá trình kiểm tra của bạn tất nhiên; trong trường hợp đó, việc truyền bá ngoại lệ rất hữu ích vì bạn có thể kết nối nó với trình gỡ lỗi và khám phá chi tiết của lỗi.

Đôi khi một số ngoại lệ như thế này chỉ hiển thị vì "dễ dàng hơn để yêu cầu sự tha thứ hơn sự cho phép" (EAFP) - một kỹ thuật lập trình được chấp nhận hoàn toàn bằng Python. Trong trường hợp đó tất nhiên bạn nên xử lý chúng cùng một lúc. Ví dụ:

try: 
    return mylist[theindex] 
except IndexError: 
    return None 

ở đây bạn có thể mong đợi rằng theindex nói chung là một chỉ số hợp lệ vào mylist, nhưng đôi khi bên ngoài giới hạn mylist 's - và các trường hợp sau, bởi ngữ nghĩa của các ứng dụng theo giả thuyết, trong đó đoạn này thuộc về, không phải là một lỗi, chỉ cần một chút bất thường được cố định bằng cách xem xét danh sách được mở rộng khái niệm trên cả hai mặt với số vô hạn của None s. Nó dễ dàng hơn để chỉ cần thử/ngoại trừ hơn để kiểm tra đúng cho các giá trị tích cực và tiêu cực của chỉ số (và nhanh hơn, nếu được ra khỏi giới hạn là một sự xuất hiện thực sự hiếm).

trường hợp Tương tự như vậy thích hợp cho KeyErrorAttributeError xảy ra ít thường xuyên, nhờ vào sự getattr dựng sẵn và get phương pháp dicts (mà cho phép bạn cung cấp một giá trị mặc định), collections.defaultdict, vv; nhưng các danh sách không có tương đương trực tiếp với những danh sách này, do đó, việc thử/ngoại trừ được xem thường xuyên hơn cho IndexError.

Cố gắng nắm bắt lỗi cú pháp, nhập lỗi, lỗi giá trị, lỗi tên, v.v ... hơi hiếm và gây tranh cãi hơn - mặc dù nó chắc chắn sẽ phù hợp nếu lỗi được chẩn đoán trong "trình cắm", thứ ba mã bên ngoài kiểm soát của bạn mà khung/ứng dụng của bạn đang cố gắng tải và thực thi động (thực sự đó là trường hợp bạn đang cung cấp thư viện hoặc tương tự và cần phải cùng tồn tại một cách hòa bình với mã ngoài tầm kiểm soát của bạn cũng có thể bị lỗi) . Lỗi loại và giá trị đôi khi có thể xảy ra trong mô hình EAFP - ví dụ: khi bạn cố gắng quá tải một hàm để chấp nhận một chuỗi hoặc một số và hoạt động hơi khác nhau trong mỗi trường hợp, việc bắt các lỗi như vậy có thể tốt hơn là kiểm tra các loại - nhưng khái niệm hàm rất quá tải thường xuyên hơn mơ hồ.

Quay lại "lỗi người dùng và môi trường", người dùng chắc chắn sẽ phạm sai lầm khi họ cung cấp cho bạn đầu vào, cho biết tên tệp không thực sự ở xung quanh (hoặc bạn không có quyền đọc hoặc ghi nếu đó là những gì bạn được cho là đang làm), vân vân: tất cả các lỗi như vậy nên tất nhiên bị bắt và dẫn đến một lời giải thích rõ ràng cho người dùng về những gì đã sai, và một cơ hội khác để có được quyền đầu vào. Mạng đôi khi đi xuống, cơ sở dữ liệu hoặc các máy chủ bên ngoài khác có thể không phản hồi như mong đợi, v.v. - đôi khi đáng để bắt gặp các vấn đề đó và thử lại (có thể sau một chút chờ đợi - có thể có dấu hiệu cho người dùng biết có thể đã vô tình rút phích cắm cáp và bạn muốn cho họ cơ hội sửa chữa mọi thứ và cho bạn biết khi nào nên thử lại), đôi khi (đặc biệt là trong các chương trình dài không giám sát) không có gì nhiều bạn có thể làm ngoại trừ việc tắt lệnh (và ghi chi tiết của mọi khía cạnh có thể có liên quan của môi trường).

Vì vậy, tóm lại, câu trả lời cho tiêu đề Q của bạn là "nó phụ thuộc" ;-). Tôi hy vọng tôi đã được sử dụng trong danh sách nhiều tình huống và các khía cạnh mà nó có thể phụ thuộc, và đề xuất những gì nói chung là thái độ hữu ích nhất để thực hiện các vấn đề như vậy.

+1

mẫn hữu ích của một số "cảm xúc ruột" đi kèm với trải nghiệm với python. Tôi háo hức chờ đợi các thực hành tốt nhất của Python - nhưng không phải trước các mẫu Python :) –

3

Để bắt đầu, bạn không cần bất kỳ _mainLogger nào. Nếu bạn muốn bắt bất kỳ ngoại lệ nào, có thể đăng nhập hoặc gửi chúng qua email hoặc bất cứ điều gì, làm điều đó ở mức cao nhất có thể - chắc chắn không phải trong lớp học này.

Ngoài ra, bạn chắc chắn không muốn chuyển đổi mọi Ngoại lệ thành RuntimeError. Hãy để nó nổi lên. Phương thức stopClient() không có mục đích ngay bây giờ. Khi có, chúng ta sẽ xem xét nó ..

Bạn về cơ bản có thể quấn ConnectionError, IOError và OSError với nhau (như, tái huy động như một cái gì đó khác), nhưng không nhiều hơn thế nữa ...