Добавление методов к классу программно в Python 3+ - PullRequest
0 голосов
/ 20 марта 2019

У меня есть класс, который предоставляет простой интерфейс для API. Я хочу определить метод этого класса для каждого маршрута.

Поскольку большинство маршрутов одинаковы, многие функции могут быть преобразованы в более общую функцию, причем многие маршруты являются лишь частично примененной версией этой функции

class MyAPIWrapper:

    def _get_results(self, route, **params):
        # Do the important stuff here
        results = ...
        return results

    def get_customers(self):
        return self._get_results(self, 'customers')

    def get_transactions(self):
        return self._get_results(self, 'transactions')

    # etc, etc, etc

Однако очевидно, что это все еще приводит к значительному количеству шаблонов в определении класса.

Одной из альтернатив является добавление нового метода, который добавляет метод каждого маршрута программно:

import functools

class MyAPIWrapper:

    def __init__(self):
        self._init_route_methods()

    def _get_results(self, route, **params):
        # Do the important stuff here
        results = ...
        return results

    def _init_route_methods(self):
        for route in ['customers', 'transactions', ...]:
            route_fn = functools.partial(self. _get_results, route)
            setattr(self, f'get_{route}', route_fn)

Преимущество заключается в уменьшении количества шаблонов и позволяет легко добавлять / удалять маршруты. Однако добавление методов при инициализации кажется мне несколько не элегантным.

Есть ли лучший и / или более идиоматический способ сделать это?

1 Ответ

2 голосов
/ 20 марта 2019

Вы можете быть удивлены, что это добьется цели:

class MyAPIWrapper:
    def _get_results(self, route, **params):
        # Do the important stuff here
        return route

    for route in ['customers', 'transactions']:
        exec("""
    def get_{0}(self):
        return self._get_results('{0}')
    """.strip().format(route))
MyAPIWrapper().get_customers()    # return customers
MyAPIWrapper().get_transactions() # return transactions

Плюсы

  • Хорошая читаемость
  • Минимальное изменение кода

Против

Обратите внимание, что exec имеет немного издержек, чем setattr(MyAPIWrapper,'get_%s'%route, ...), что имеет значение, только если вы собираетесь создать миллионы методов в цикле.

Если вы хотите сделать то же самое для многих различных классов APIWrapper, рассмотрите возможность использования класса decorator .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...