Gõ vịt, xác thực dữ liệu và lập trình quyết đoán trong Python


10

Về gõ vịt :

Gõ vịt được hỗ trợ bởi thói quen không kiểm tra loại đối số trong các cơ quan phương thức và chức năng, dựa vào tài liệu, mã rõ ràng và kiểm tra để đảm bảo sử dụng đúng.

Về xác thực đối số (EAFP: Dễ dàng yêu cầu sự tha thứ hơn là sự cho phép). Một ví dụ thích nghi từ đây :

... nó được coi là nhiều pythonic hơn để làm:

def my_method(self, key):
    try:
        value = self.a_dict[member]
    except TypeError:
        # do something else

Điều này có nghĩa là bất kỳ ai khác sử dụng mã của bạn không phải sử dụng từ điển hoặc lớp con thực sự - họ có thể sử dụng bất kỳ đối tượng nào thực hiện giao diện ánh xạ.

Thật không may trong thực tế nó không đơn giản. Nếu thành viên trong ví dụ trên có thể là số nguyên thì sao? Số nguyên là bất biến - vì vậy việc sử dụng chúng làm khóa từ điển là hoàn toàn hợp lý. Tuy nhiên, chúng cũng được sử dụng để lập chỉ mục các đối tượng loại trình tự. Nếu thành viên tình cờ là một số nguyên thì ví dụ hai có thể cho qua danh sách và chuỗi cũng như từ điển.

Về lập trình quyết đoán :

Các xác nhận là một cách có hệ thống để kiểm tra xem trạng thái bên trong của chương trình có đúng như lập trình viên mong đợi hay không, với mục tiêu là bắt lỗi. Cụ thể, chúng tốt cho việc nắm bắt các giả định sai được đưa ra trong khi viết mã hoặc lạm dụng giao diện bởi một lập trình viên khác. Ngoài ra, họ có thể đóng vai trò là tài liệu nội tuyến ở một mức độ nào đó, bằng cách làm cho các giả định của lập trình viên trở nên rõ ràng. ("Rõ ràng là tốt hơn ngầm định.")

Các khái niệm được đề cập đôi khi mâu thuẫn, vì vậy tôi dựa vào các yếu tố sau khi chọn nếu tôi không thực hiện bất kỳ xác thực dữ liệu nào, xác thực mạnh hoặc sử dụng các xác nhận:

  1. Xác nhận mạnh mẽ. Bằng cách xác nhận mạnh mẽ, tôi có nghĩa là nâng một Ngoại lệ tùy chỉnh ( ApiErrorví dụ). Nếu chức năng / phương thức của tôi là một phần của API công khai, tốt hơn hết là xác thực đối số để hiển thị thông báo lỗi tốt về loại không mong muốn. Bằng cách kiểm tra loại tôi không có nghĩa là chỉ sử dụng isinstance, mà còn nếu đối tượng được thông qua hỗ trợ giao diện cần thiết (gõ vịt). Trong khi tôi ghi lại API và chỉ định loại dự kiến ​​và người dùng có thể muốn sử dụng chức năng của tôi theo cách không mong muốn, tôi cảm thấy an toàn hơn khi kiểm tra các giả định. Tôi thường sử dụng isinstancevà nếu sau này tôi muốn hỗ trợ các loại hoặc vịt khác, tôi thay đổi logic xác thực.

  2. Lập trình quyết đoán. Nếu mã của tôi là mới, tôi sử dụng khẳng định rất nhiều. Lời khuyên của bạn về điều này là gì? Bạn có sau đó loại bỏ khẳng định từ mã?

  3. Nếu chức năng / phương thức của tôi không phải là một phần của API, nhưng chuyển một số đối số của nó sang một mã khác không được tôi viết, nghiên cứu hoặc kiểm tra, tôi thực hiện rất nhiều khẳng định theo giao diện được gọi. Logic của tôi đằng sau điều này - tốt hơn là thất bại trong mã của tôi, sau đó ở đâu đó sâu hơn 10 cấp độ trong stacktrace với lỗi không thể hiểu được buộc phải gỡ lỗi rất nhiều và sau đó thêm khẳng định vào mã của tôi.

Nhận xét và lời khuyên về khi nào nên sử dụng hay không sử dụng xác nhận loại / giá trị, khẳng định? Xin lỗi vì không phải là công thức tốt nhất của câu hỏi.

Ví dụ, hãy xem xét hàm sau, Customermô hình khai báo SQLAlchemy ở đâu :

def add_customer(self, customer):
    """Save new customer into the database.
    @param customer: Customer instance, whose id is None
    @return: merged into global session customer
    """
    # no validation here at all
    # let's hope SQLAlchemy session will break if `customer` is not a model instance
    customer = self.session.add(customer)
    self.session.commit()
    return customer

Vì vậy, có một số cách để xử lý xác nhận:

def add_customer(self, customer):
    # this is an API method, so let's validate the input
    if not isinstance(customer, Customer):
        raise ApiError('Invalid type')
    if customer.id is not None:
        raise ApiError('id should be None')

    customer = self.session.add(customer)
    self.session.commit()
    return customer

hoặc là

def add_customer(self, customer):
    # this is an internal method, but i want to be sure
    # that it's a customer model instance
    assert isinstance(customer, Customer), 'Achtung!'
    assert customer.id is None

    customer = self.session.add(customer)
    self.session.commit()
    return customer

Khi nào và tại sao bạn sẽ sử dụng từng thứ này trong ngữ cảnh gõ vịt, kiểm tra kiểu, xác thực dữ liệu?


1
bạn không nên xóa các xác nhận giống như các bài kiểm tra đơn vị trừ khi vì lý do hiệu suất
Bryan Chen

Câu trả lời:


4

Hãy để tôi đưa ra một số nguyên tắc hướng dẫn.

Nguyên tắc số 1. Như được nêu trong http://docs.python.org/2/reference/simple_stmts.html chi phí hoạt động của các xác nhận có thể được loại bỏ bằng tùy chọn dòng lệnh, trong khi vẫn ở đó để gỡ lỗi. Nếu hiệu suất là một vấn đề, làm điều đó. Để lại các khẳng định. (Nhưng đừng làm gì quan trọng trong các khẳng định!)

Nguyên tắc số 2. Nếu bạn đang xác nhận điều gì đó và sẽ có một lỗi nghiêm trọng, thì hãy sử dụng một xác nhận. Hoàn toàn không có giá trị trong việc làm một cái gì đó khác. Nếu ai đó sau đó muốn thay đổi điều đó, họ có thể thay đổi mã của bạn hoặc tránh cuộc gọi phương thức đó.

Nguyên tắc số 3. Đừng không cho phép một cái gì đó chỉ vì bạn nghĩ rằng đó là một điều ngu ngốc để làm. Vì vậy, nếu phương thức của bạn cho phép chuỗi thông qua? Nếu nó hoạt động, nó hoạt động.

Nguyên tắc số 4. Không cho phép những thứ có dấu hiệu sai lầm. Ví dụ, xem xét việc thông qua một từ điển các tùy chọn. Nếu từ điển đó chứa những thứ không phải là tùy chọn hợp lệ, thì đó là dấu hiệu cho thấy ai đó không hiểu API của bạn hoặc người nào khác có lỗi đánh máy. Thổi vào đó có nhiều khả năng bắt lỗi chính tả hơn là ngăn người khác làm điều gì đó hợp lý.

Dựa trên 2 nguyên tắc đầu tiên, phiên bản thứ hai của bạn có thể bị vứt đi. Mà hai trong số bạn thích là một vấn đề của hương vị. Mà bạn nghĩ nhiều khả năng? Rằng ai đó sẽ chuyển một người không phải là khách hàng add_customervà mọi thứ sẽ bị phá vỡ (trong trường hợp đó phiên bản 3 được ưu tiên) hoặc một lúc nào đó ai đó sẽ muốn thay thế khách hàng của bạn bằng một đối tượng proxy nào đó đáp ứng tất cả các phương thức phù hợp (trong trường hợp phiên bản 1 được ưu tiên).

Cá nhân tôi đã thấy cả hai chế độ thất bại. Tôi có xu hướng đi với phiên bản 1 theo nguyên tắc chung là tôi lười biếng và nó ít gõ hơn. (Ngoài ra, loại thất bại đó thường có xu hướng xuất hiện sớm hay muộn theo cách khá rõ ràng. Và khi tôi muốn sử dụng một đối tượng proxy, tôi thực sự khó chịu với những người đã trói tay tôi.) Nhưng có những lập trình viên tôi tôn trọng sẽ đi theo con đường khác.


Tôi thích v.3, đặc biệt là khi thiết kế giao diện - viết các lớp và phương thức mới. Ngoài ra tôi coi v.3 hữu ích cho các phương thức API - bởi vì mã của tôi là mới đối với các phương thức khác. Tôi nghĩ cách tiếp cận quyết đoán là một sự thỏa hiệp tốt, bởi vì nó bị loại bỏ trong sản xuất khi chạy ở chế độ tối ưu hóa. > Thổi vào đó có nhiều khả năng mắc lỗi đánh máy hơn là ngăn người khác làm điều gì đó hợp lý. <Vì vậy, bạn không phiền khi có xác nhận như vậy?
warvariuc

Hãy đặt nó theo cách này. Tôi thấy rằng bản đồ kế thừa kém với cách tôi muốn phát triển các thiết kế. Tôi thích thành phần. Vì vậy, tôi ngại ngùng khi khẳng định rằng điều này phải thuộc về lớp học đó. Nhưng tôi không phản đối những khẳng định mà tôi nghĩ rằng họ cứu tôi một cái gì đó.
btilly
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.