Chúng ta có thể trả về một ngoại lệ từ một hàm trong python không?

Tăng một ngoại lệ

Là nhà phát triển Python, bạn có thể chọn ném ngoại lệ nếu có điều kiện xảy ra

Để ném [hoặc tăng] một ngoại lệ, hãy sử dụng từ khóa

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
9

Ví dụ

Báo lỗi và dừng chương trình nếu x nhỏ hơn 0

x = -1

nếu x < 0.
  raise Exception["Xin lỗi, không có số nào dưới 0"]

Tự mình thử »

Từ khóa

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
9 được sử dụng để đưa ra một ngoại lệ

Bạn có thể xác định loại lỗi nào sẽ gây ra và văn bản sẽ in cho người dùng

Ví dụ

Tăng TypeError nếu x không phải là số nguyên

x = "xin chào"

nếu không gõ[x] là int.
  tăng TypeError["Chỉ cho phép số nguyên"]

Tự mình thử »


Một ngày nọ, tôi nhận được một câu hỏi về một số mã cũ mà tôi đã viết, thay vì đưa ra một ngoại lệ cho một điều kiện lỗi như người đọc mong đợi, lại trả về một đối tượng lỗi

Với lớp EmailVerifyTokenGenerator của bạn, tại sao bạn lại trả về các lớp lỗi thay vì tăng các lỗi tùy chỉnh?

Tôi nghĩ rằng tôi quá háo hức để nêu ra lỗi nhưng có lẽ tôi đang thiếu điều gì đó với các lớp học 😁

Mã được đề cập bên dưới [được sửa đổi một chút và loại bỏ một số phương thức không thú vị]. Nó là một phần của hệ thống thực hiện xác minh địa chỉ email thông qua các liên kết ma thuật trong email

from dataclasses import dataclass

class VerifyFailed:
    pass


VerifyFailed = VerifyFailed[]  # singleton sentinel value


@dataclass
class VerifyExpired:
    email: str


class EmailVerifyTokenGenerator:
    def token_for_email[self, email]:
        ...

    def email_from_token[self, token]:
        """
        Extracts the verified email address from the token, or a VerifyFailed
        constant if verification failed, or VerifyExpired if the link expired.
        """
        max_age = settings.EMAIL_VERIFY_TIMEOUT
        try:
            unencoded_token = self.url_safe_decode[token]
        except [UnicodeDecodeError, binascii.Error]:
            return VerifyFailed
        try:
            return self.signer.unsign[unencoded_token, max_age=max_age]
        except [SignatureExpired,]:
            return VerifyExpired[self.signer.unsign[unencoded_token]]
        except [BadSignature,]:
            return VerifyFailed

Tóm lại, chúng tôi có một chức năng trích xuất địa chỉ email từ mã thông báo, kiểm tra chữ ký HMAC đi kèm với nó. Có 3 khả năng chúng tôi muốn giải quyết

  1. Trường hợp may mắn — chúng tôi có mã HMAC hợp lệ, chúng tôi chỉ cần trả lại địa chỉ email

  2. Chúng tôi có một chữ ký không hợp lệ

  3. Chúng tôi có một chữ ký hợp lệ nhưng đã hết hạn. Chúng tôi muốn xử lý vấn đề này một cách riêng biệt vì chúng tôi muốn hợp lý hóa trải nghiệm người dùng để nhận mã thông báo mới được tạo và gửi cho họ, điều đó có nghĩa là chúng tôi cần trả lại địa chỉ email

Nó đang sử dụng các chức năng của người ký của Django để thực hiện công việc nặng nhọc, nhưng điều đó không quan trọng đối với mục đích của chúng tôi, bởi vì chúng tôi đang hoàn thiện nó

Để tiếp tục thiết kế API của chúng tôi cho đoạn mã này, đây là một số tùy chọn không hợp lệ

  1. Chúng ta có thể có một cặp phương thức hoặc chức năng.

    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    1 và
    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    2, có thể được sử dụng độc lập. Điều này thật tệ vì bạn có thể dễ dàng sử dụng
    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    1 và hoàn toàn quên sử dụng
    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    2

    Nguyên tắc ở đây là chúng tôi muốn nhà phát triển sử dụng API này rơi vào hố thành công. Hoặc là nhà phát triển phải lấy mã của họ hoàn toàn chính xác, hoặc nếu không, thì rõ ràng mã đó sẽ bị hỏng và hoàn toàn không hoạt động, hoặc ít nhất là không có sai sót tinh vi với một số lỗi khó chịu, chẳng hạn như vấn đề bảo mật

  2. Chúng ta có thể có phương thức hoặc hàm

    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    1 với giá trị trả về là một bộ chứa
    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    2

    Điều này có một vấn đề tương tự như trên — mã cuộc gọi có thể sử dụng

    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    3 và quên kiểm tra tính hợp lệ của boolean

Sau khi loại trừ những điều đó, chúng ta có hai ứng cử viên chính về cách thiết kế

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
1

  1. Chúng tôi có thể làm cho nó tăng ngoại lệ cho các trường hợp "không hợp lệ" hoặc "hết hạn". Chúng tôi cần chuyển thêm dữ liệu cho cái sau, nhưng chúng tôi có thể đặt nó bên trong đối tượng ngoại lệ - như người hỏi ban đầu đã lưu ý

  2. Chúng tôi có thể làm cho nó trả về các đối tượng lỗi cho các trường hợp lỗi, như được mã hóa ở trên

Cả hai điều này đều thỏa mãn tiêu chí “hố thành công”. Nếu nhà phát triển vô tình không xử lý các trường hợp lỗi, họ sẽ không gặp lỗi khi chúng tôi xác minh địa chỉ email không được xác minh. Thay vào đó, chúng tôi có thể sẽ có một loại trình gỡ lỗi nào đó, trong trường hợp ứng dụng web, như ứng dụng này, có nghĩa là trang lỗi 500 đang được nhìn thấy và nội dung nào đó trong nhật ký của chúng tôi cho thấy khá rõ ràng điều gì đã xảy ra

Nếu chúng ta chọn tăng ngoại lệ, mã ngây thơ không kiểm tra ngoại lệ sẽ đơn giản là không tiến xa hơn - ngoại lệ sẽ lan rộng và chấm dứt trình xử lý. Với tùy chọn thứ hai nơi chúng tôi trả về các đối tượng lỗi, những đối tượng đó không thể vô tình được chuyển đổi thành giá trị thành công — đối tượng

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
5 chứa địa chỉ email, nhưng nó có dạng giá trị hoàn toàn khác với trường hợp hạnh phúc

Ở một mức độ nào đó, cả hai cách tiếp cận này đều tôn trọng nguyên tắc có thể được tóm tắt là Parse Don't Validate. Thay vì chỉ xác thực mã thông báo và trích xuất địa chỉ email dưới dạng hai thứ độc lập, chúng tôi đang phân tích cú pháp mã thông báo và mã hóa kết quả xác thực theo loại đối tượng mà sau đó sẽ chuyển qua chương trình

Nhưng cái nào tốt hơn?

Một trong những ảnh hưởng đến suy nghĩ của tôi là cách thức hoạt động của các kiểu trong Haskell và ngôn ngữ tương tự khác giúp dễ dàng tạo các kiểu và hàm tạo. Trong Haskell, sau đây là tất cả mã bạn cần để xác định kiểu trả về cho loại hàm này và 3 hàm tạo dữ liệu khác nhau mà bạn cần, sau đó thực hiện nhiệm vụ kép để khớp mẫu

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string

Bây giờ, Python gần như không ngắn gọn như vậy, nhưng các lớp dữ liệu là một cải tiến lớn để xác định những thứ như

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
5

Trong Haskell, do kiểm tra kiểu tĩnh, mẫu này khiến mã gọi không thể vô tình xử lý giá trị trả về một cách chính xác. Nhưng ngay cả trong Python, thứ không được tích hợp sẵn, tôi nghĩ vẫn có một số lợi thế hấp dẫn

  1. Chúng tôi hy vọng mã cuộc gọi sẽ xử lý tất cả các giá trị trả về khác nhau tại một số điểm và tại cùng một thời điểm. [Điều này không giống với một số mã mà chúng ta có thể đưa ra một ngoại lệ mà chúng ta không bao giờ mong đợi mã gọi sẽ xử lý cụ thể — nó sẽ được xử lý bằng các phương thức chung hơn ở một lớp khác]. Do đó, điều hợp lý là chúng ta coi cả 3 giá trị là cùng một loại — chúng chỉ là các giá trị trả về khác nhau

  2. Thay vào đó, nếu bạn đưa ra các ngoại lệ, bạn sẽ ngay lập tức buộc mã gọi vào một cấu trúc luồng điều khiển đặc biệt, cụ thể là điệu nhảy

    data EmailVerificationResult = EmailVerified string
                                 | VerifyFailed
                                 | VerifyExpired string
    
    7, điều này có thể gây bất tiện

  3. Đặc biệt, nếu bạn muốn giao việc xử lý giá trị cho một số chức năng hoặc mã khác để xử lý, bạn không thể thực hiện dễ dàng. Ví dụ: mã như thế này sẽ ổn với phương thức "đối tượng lỗi trả về", nhưng phức tạp đáng kể với phương thức "tăng ngoại lệ"

    verify_result = verifier.email_from_token[token]
    log_verify_result[request.ip_address, verify_result]
    # etc.
    

Tuy nhiên, trong những năm kể từ khi tôi viết mã, một số lập luận có lẽ thuyết phục hơn đã xuất hiện cho phương thức đối tượng lỗi

Đầu tiên, với một số thay đổi nhỏ [cụ thể là loại bỏ giá trị sentinel singleton], giờ đây chúng ta có thể thêm chữ ký loại cho

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
8

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
1

[Bạn có thể cần cho các phiên bản Python cũ hơn]

Bản thân đây là một lợi ích từ quan điểm tài liệu và để được trợ giúp IDE/trình soạn thảo tốt hơn

Chúng ta có thể tiến xa hơn với mypy. Chúng tôi có thể cấu trúc mã cuộc gọi của mình như sau để sử dụng kiểm tra tính toàn diện của mypy

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
2

Bây giờ, nếu chúng tôi xóa một trong các khối này, giả sử khối

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
5 [hoặc nếu chúng tôi đã thêm một tùy chọn khác vào
data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
8], mypy sẽ bắt khối đó cho chúng tôi

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
5

Với phương thức đối tượng lỗi, chúng ta cũng có thể viết mã xử lý của mình bằng cách khớp mẫu cấu trúc. Mã tương đương, bao gồm kiểm tra toàn diện mypy của chúng tôi, bây giờ trông như thế này

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
6

Điều này đã phá hủy địa chỉ email trong

data EmailVerificationResult = EmailVerified string
                             | VerifyFailed
                             | VerifyExpired string
5 được tích hợp sẵn — nó bị ràng buộc với tên
verify_result = verifier.email_from_token[token]
log_verify_result[request.ip_address, verify_result]
# etc.
2 trong nhánh đó

Hy vọng rằng điều này mang lại sự biện minh tốt cho cách tiếp cận mà tôi đã thực hiện với mã này. Đôi khi các ngoại lệ tốt hơn - thường là khi những điều được đề cập ở trên không áp dụng hoặc áp dụng ngược lại - nhưng tôi nghĩ các đối tượng lỗi cũng có vị trí của chúng và đôi khi là một giải pháp tốt hơn nhiều

Bạn có thể trả lại ngoại lệ trong Python không?

Lưu ý rằng khi bạn trả về một đối tượng thuộc lớp Ngoại lệ, bạn sẽ nhận được một đại diện cho giá trị được liên kết của nó, thường là mục đầu tiên trong danh sách các đối số của nó. In the example above, this is the string explanation of the exception. In some cases, it may be a tuple with other information about the exception.

Bạn có thể in một Python ngoại lệ không?

Nếu bạn định in ngoại lệ, tốt hơn nên sử dụng print[repr[e]] ; ngoại lệ cơ sở. Việc triển khai __str__ chỉ trả về thông báo ngoại lệ, không phải loại. Hoặc, sử dụng mô-đun theo dõi, có các phương pháp để in ngoại lệ hiện tại, được định dạng hoặc truy nguyên đầy đủ.

Hàm có thể trả về bằng Python không?

Hàm trả về một hàm khác trong Python . Đối tượng hạng nhất là một đối tượng có thể được gán cho một biến, được truyền dưới dạng đối số cho hàm hoặc được sử dụng làm giá trị trả về trong hàm. we can return a function from another function. A first-class object is an object that can be assigned to a variable, passed as an argument to a function, or used as a return value in a function.

3 loại ngoại lệ chính trong Python là gì?

Có ba loại lỗi chính có thể phân biệt được trong Python. lỗi cú pháp, ngoại lệ và lỗi logic .

Chủ Đề