Làm cách nào để ghi lại các thay đổi nhỏ đối với các hàm API phức tạp?


8

Giả sử chúng ta có một hàm API phức tạp, được nhập từ một số thư viện.

def complex_api_function(
        number, <lots of positional arguments>,
        <lots of keyword arguments>):
    '''really long docstring'''
    # lots of code

Tôi muốn viết một trình bao bọc đơn giản xung quanh chức năng đó để tạo ra một thay đổi nhỏ. Ví dụ , có thể truyền đối số đầu tiên dưới dạng chuỗi. Làm thế nào để tài liệu này? Tôi đã xem xét các lựa chọn sau:

Lựa chọn 1:

def my_complex_api_function(number_or_str, *args, **kwargs):
    '''
    Do something complex.

    Like `complex_api_function`, but first argument can be a string.

    Parameters
    ----------
    number_or_str : int or float or str
        Can be a number or a string that can be interpreted as a float.
        <copy paste description from complex_api_function docstring>
    *args
        Positional arguments passed to `complex_api_function`.
    **kwargs
        Keyword arguments passed to `complex_api_function`.

    Returns
    -------
    <copy paste from complex_api_function docstring>

    Examples
    --------
    <example where first argument is a string, e.g. '-5.0'>

    '''
    return complex_api_function(float(number_or_str), *args, **kwargs)

Nhược điểm: Người dùng phải xem các tài liệu complex_api_functionđể nhận thông tin về *args**kwargs. Cần điều chỉnh khi bản sao dán các phần từ complex_api_functionthay đổi.

Lựa chọn 2:

Sao chép và dán complex_api_functionchữ ký của (thay vì sử dụng *args**kwargs) và chuỗi của nó. Thực hiện một thay đổi nhỏ đối với chuỗi doc đề cập rằng đối số đầu tiên cũng có thể là một chuỗi. Thêm một ví dụ.

Nhược điểm: dài dòng, phải thay đổi khi complex_api_functionthay đổi.

Tùy chọn 3:

Trang trí my_complex_api_functionvới functools.wraps(complex_api_function).

Nhược điểm: Không có thông tin numbercũng có thể là một chuỗi.


Tôi đang tìm kiếm một câu trả lời không xoay quanh chi tiết về những thay đổi trong đó my_complex_api_function. Các thủ tục nên làm việc cho bất kỳ điều chỉnh nhỏ so với ban đầu complex_api_function.

Câu trả lời:


3

Tôi muốn giới thiệu một cái gì đó như sau:

def my_complex_api_function(number_or_str, *args, **kwargs):
    """This function is a light wrapper to `complex_api_function`.
    It allows you to pass a string or a number, whereas `complex_api_function` requires a 
    number. See :ref:`complex_api_function` for more details.

    :param number_or_str: number or str to convert to a number and pass to `complex_api_function`.
    :param args: Arguments to pass to `complex_api_function`
    :param kwargs: Keyword arguments to pass to `complex_api_function`
    :return: Output of `complex_api_function`, called with passed parameters
    """

Điều này là rõ ràng và súc tích. Nhưng cũng xin nhớ rằng, nếu sử dụng một hệ thống tài liệu như nhân sư, để liên kết các chức năng với :ref:`bob`hoặc tương tự.


1
Tôi thậm chí sẽ không đề cập đến loại nào complex_api_functionmong đợi cho tham số của nó vì điều đó chỉ trùng lặp thông tin (có thể chúng cũng có nhiều tùy chọn). Có lẽ người dùng của trình bao bọc đã quen thuộc với chức năng ban đầu và nếu không, bạn luôn có thể trỏ chúng đến các tài liệu gốc. Dù sao, tôi nghĩ rằng đây là cách để đi, chỉ ghi lại những gì được thêm vào chức năng ban đầu + cung cấp chi tiết về cách loại mới được chuyển đổi thành loại ban đầu (những chi tiết đó có thể quan trọng). Tức là cách đối số đó được xử lý để tương thích với hàm ban đầu.
a_guest

1
Đó là một điểm tốt về liên kết - Tôi đã thêm một chỉnh sửa cho một :ref:chuỗi. Tuy nhiên, đối với những thay đổi api nhỏ - như OP đang hỏi về - nó cho phép người dùng so sánh các chức năng đơn giản hơn. Trong trường hợp này, nỗ lực tối thiểu có thể mang lại thêm chút ít cho người dùng cuối - và khi tôi đọc tài liệu, tôi sẽ lấy tài liệu 12 trang trên tài liệu 6 trang trong hầu hết các trường hợp, vì điều đó dễ hiểu hơn.
Legorooj

5

Bạn có thể tự động hóa "chuyên môn hóa" của chuỗi tài liệu gốc bằng một phụ lục . Ví dụ, pydoc đang sử dụng thuộc tính đặc biệt__doc__ . Bạn có thể viết một trình trang trí tự động ghi đè chức năng ban đầu __doc__bằng phần phụ lục của bạn.

Ví dụ:

def extend_docstring(original, addendum):
    def callable(func):
        func.__doc__ = original + addendum
        return func

    return callable


def complex_api_function(a, b, c):
    '''
    This is a very complex function.

    Parameters
    ----------
    a: int or float
        This is the argument A.
    b: ....
    '''
    print('do something')

@extend_docstring(
    complex_api_function.__doc__,
    '''
    Addendum
    --------
    Parameter a can also be a string
    '''
)
def my_complex_api_function(a, b, c):
    return complex_api_function(float(a), b, c)

hoặc là...

def extend_docstring(original):
    def callable(func):
        func.__doc__ = original + func.__doc__
        return func

    return callable


def complex_api_function(a, b, c):
    '''
    This is a very complex function.

    Parameters
    ----------
    a: int or float
        This is the argument A.
    b: ....
    '''
    print('do something')

@extend_docstring(complex_api_function.__doc__)
def my_complex_api_function(a, b, c):
    '''
    Addendum
    --------
    Parameter a can also be a string
    '''
    return complex_api_function(float(a), b, c)

Nếu bạn chạy pydoc ( pydoc3 -w my_module.py), nó tạo ra: xem trước html được tạo bởi pydoc

Lưu ý bổ sung: Nếu bạn đang sử dụng Python 3, bạn có thể sử dụng các chú thích để ghi lại (các) loại tham số chức năng của mình. Nó cung cấp rất nhiều lợi ích, không chỉ tài liệu. Ví dụ:

from typing import Union

def my_complex_api_function(number_or_str: Union[int, float, str], *args, **kwargs):

1
Điều này có nhược điểm là thông tin quan trọng (mới) bị "ẩn" ở cuối chuỗi doc (có lẽ rất dài). Do đó khả năng khám phá của tính năng mới là rất thấp trong khi đó là thông tin có giá trị duy nhất bổ sung vào chuỗi doc hiện có. Ngoài ra, nó xung đột khai báo kiểu trên chuỗi doc gốc. Đó là nếu người dùng nhìn vào chuỗi doc mở rộng, họ sẽ thấy a : floatở trên cùng và không bao giờ đưa ra kết luận rằng họ cũng có thể sử dụng một chuỗi strở đây. Chỉ khi tình cờ họ cuộn đến cuối tài liệu họ sẽ khám phá ra nó.
a_guest

1
Bạn có thể thêm phần phụ lục vào đầu thay vì cuối ... Giống như một ghi chú "sửa đổi" ở đầu tài liệu.
Raphael Medaer

1
Một vấn đề khác là sự trùng lặp (+ đóng băng) thông tin. Giả sử bạn xây dựng một gói vận chuyển trình bao bọc này và bạn chỉ định các phụ thuộc của mình là complex_package >= 1.1.0. Bây giờ khi bạn xây dựng gói của mình, bạn phải sử dụng một phiên bản cụ thể cho complex_package. Giả sử đã tồn tại complex_package==1.5.0trên pypi và họ đã thêm một đối số từ khóa mới complex_api_functionvào phiên bản 1.3.0. Dù bằng cách nào (sử dụng 1.1.0hoặc 1.5.0) bạn sẽ có thông tin lỗi thời / sai cho một nhóm người dùng của bạn trong tài liệu. Điều tương tự cũng áp dụng cho những thay đổi trong tương lai chưa được công khai.
a_guest

-1

Không chắc chắn nếu đây là những gì bạn đang tìm kiếm, nhưng nó giúp tránh hoàn toàn câu hỏi.

def first_as_num_or_str(func):
    '''Decorator allowing the first parameter of the given function to be a number or a string

    :param func: A function whose first argument is a number
    :return: `func`, but now the first argument is cast to a float
    ''' 
    def new_func(*args, **kwargs):
        func(float(args[0]), args[1:], kwargs)
    return new_func

wrapped_api_func = first_as_num_or_str(complex_api_function)

Cảm ơn bạn. wrapped_api_funckhông có chuỗi, vì vậy vấn đề tài liệu không được giải quyết.
fact_panda
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.