Мне трудно выбрать между вариантами того, как написать этот клиент ниже:
import requests
class Client:
def request_endpoint_1(self, **params):
return requests.get('url_1', params)
def request_endpoint_2(self, **params):
return requests.get('url_2', params)
def request_endpoint_3(self, **params):
return requests.get('url_3', params)
.
.
.
from enum import Enum
class Endpoint(Enum):
endpoint_1 = 'url_1'
endpoint_2 = 'url_2'
endpoint_3 = 'url_3'
.
.
.
class Client:
def request(self, endpoint: Endpoint, **params):
return requests.get(endpoint.value, params)
Учитывая, что количество конечных точек очень велико (100+), второй вариант кажется лучшим выбором, поскольку он повторяется значительно меньше, и я предполагаю, что выделение памяти для перечислений также меньше, чем такое же количество методов экземпляра в 1-й вариант.
Но если я выберу вариант 1, это сделает код более удобным для IDE и более простым для написания кода с использованием Client
методов. При варианте 2 пользователь также должен импортировать класс Endpoint(Enum)
, который будет находиться в другом модуле, чем Client
.
Вопрос в том, какой из них я должен использовать и почему? Я также хочу узнать, есть ли между ними значительная разница в эффективности памяти.
@KennyOstrom Каждый метод на самом деле имеет еще несколько параметров в дополнение к URL-адресу. В этом случае каждое перечисление будет кортежем, например: (url, method, auth_type, ...)
Гораздо проще поместить именованные параметры в метод со строкой документации и подсказками типа.
@KennyOstrom Итак, определение сотен методов с большим количеством повторений def...return requests.get(... **params)
считается хорошей практикой?
Я не опубликовал ответ, потому что это всего лишь мое мнение, но вы упускаете важную часть. Каждый из этих сотен методов четко документирует весь вызов API вместе с его конкретными параметрами. Так да. Пока они документируют API, который вам все равно нужно документировать.
Хороший API должен быть самоочевидным, просто прочитав сигнатуры методов, и ни один из подходов в вашем вопросе не дает хорошего представления о том, что ожидает и возвращает каждая конечная точка, а именно о параметрах и возвращаемых значениях.
Было бы намного понятнее написать один метод для каждой конечной точки с правильно названными и типизированными параметрами. Каждый метод также должен возвращать значение типа данных, соответствующего характеру вызова, вместо универсального объекта Response
. Также сохраняйте общие параметры в качестве атрибутов экземпляра при инициализации объекта клиента.
Итак, чтобы ответить на заголовок вопроса «Много похожих методов против одного метода, который принимает много параметров?», Я бы сказал, что лучший подход - написать «много непохожих методов».
Например (используя какой-то воображаемый API хранилища):
class Client:
def __init__(self, auth_key: str):
self.common_params = {'auth_key': auth_key}
def list_directory(self, path: str, recursive: bool = False) -> list[str]:
return [
file['name']
for file in requests.get('url_1', params = {
'path': path,
'recursive': 'yes' if recursive else 'no',
**self.common_params
}).json()['files']
]
def copy_file(self, from_path: str, to_path: str) -> None:
requests.get('url_2', params = {
'from_path': from_path,
'to_path': to_path,
**self.common_params
})
Спасибо за ответ. Я должен был лучше продемонстрировать, как выглядит каждый метод более подробно, поскольку они выглядели поверхностными, а я стремился к простоте вопроса.
Считаете ли вы, что Python выделял бы слишком много памяти, чтобы быть неэффективным, если бы было около 500 методов, таких как два в вашем ответе? Я, вероятно, все равно буду придерживаться методов, но это было моей главной заботой.
Объем памяти, занимаемый скомпилированными байт-кодами, обычно ничтожно мал по сравнению с реальными пользовательскими данными, а 500 методов — это не так уж и много, если они служат совершенно разным целям. Однако документация вашего клиентского API должна разбивать 500 методов по категориям для облегчения навигации.
Не стесняйтесь обновить свой вопрос, добавив более содержательный пример API, демонстрирующий природу 500 методов, с которыми вы имеете дело. На данный момент они выглядят слишком общими, чтобы сказать, требуют ли они другого шаблона программирования.
Ваш пример хорошо имитирует характер моих методов. Мои еще менее сложны, так как я возвращаю ответ, когда сервер его отправляет. Пользователь может делать с ним что угодно. Методы становятся сложными только тогда, когда у них есть уникальные параметры для обработки и отправки.
Сделайте оба. Первый вариант — общедоступный класс с явными и задокументированными параметрами. Второй вариант — это базовый класс, который обрабатывает фактическое выполнение и ведение журнала. Перечисление может быть ненужным, но может помочь в регистрации. Различия во время выполнения маловероятны, и в любом случае запрос будет ничтожным.