Переменные / необязательные аргументы в функции внутри функции

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

from sentinelsat import SentinelAPI, read_geojson, geojson_to_wkt

def get_available(input_geojson, user, password, date_start, date_end, satellite, sensormode, product_type):

    # LogIn
    api = SentinelAPI(user, password , 'https://scihub.copernicus.eu/dhus')

    # Input parameter of the search
    footprint = geojson_to_wkt(read_geojson(input_geojson))   # irrelevant to the question
    products = api.query(footprint,
                         date = (date_start, date_end),
                         platformname = satellite,
                         sensoroperationalmode = sensormode,
                         producttype = product_type,
                         )

Моя проблема в том, что в зависимости от того, какой "вспомогательный" ввод я собираюсь использовать, будут изменены другие аргументы, необходимые, обязательные или даже разрешенные. Некоторым не понадобится "сенсорный режим", а другим, возможно, понадобится "облачный охват". Как мне написать чистый код с переменными / необязательными аргументами в функции внутри функции? Должен ли я перечислять все возможные аргументы?

3 ответа

Я считаю, что ответ Crawl Cycle очень питонический и красивый, поэтому я рекомендую пойти с этим. Тем не менее, мне было весело работать над этим, поэтому вот моя интерпретация того, что вы искали:)

import inspect


def foo_api(*, foo=None):
    print(f'foo_api: foo={foo}')

def bar_api(*, bar=None):
    print(f'bar_api: bar={bar}')

_API_BY_PARAMETERS = {
    frozenset(inspect.signature(api).parameters): api
    for api in (foo_api, bar_api)
}


def api(**kwargs):
    """Selects the correct API to call based on the given kwargs."""
    actual_params = frozenset(kwargs)
    if actual_params in _API_BY_PARAMETERS:
        actual_api = _API_BY_PARAMETERS[actual_params]
        return actual_api(**kwargs)
    else:
        param_errors = (
            (api.__name__,
             ', '.join(sorted(expected_params - actual_params)),
             ', '.join(sorted(actual_params - expected_params)))
            for expected_params, api in _API_BY_PARAMETERS.items())
        raise TypeError(
            'Arguments must match exactly with one of the APIs, but found '
            'the following differences: ' +
            '; '.join(
                f'{api_name} -> missing=[{missing}], unexpected=[{unexpected}]'
                for api_name, missing, unexpected in param_errors))

Демо: http://tpcg.io/AONnvHn9.

Есть несколько ограничений, которые делают эту реализацию такой лаконичной:

  • Все подписи API должны быть уникальными.
  • Все сигнатуры API принимают только аргументы ключевого слова.

Этот api кажется слишком утомительным для использования. Лучше сгруппируйте аргументы по классам.

from sentinelsat import SentinelAPI, read_geojson, geojson_to_wkt


class Satellite:
    def __init__(self, name, producttype, sensormode=None, cloudcoverage=None):
        self.name = name
        self.producttype = producttype
        self.sensormode = sensormode
        self.cloudcoverage = cloudcoverage


class SentinelConnection:
    def __init__(self, input_geojson, user, password):
        self.input_geojson = input_geojson
        self.user = user
        self.password = password
        self.api = None
        self.footprint = None

    def __enter__(self):
        self.api = SentinelAPI(self.user, self.password,
                               'https://scihub.copernicus.eu/dhus')
        self.footprint = geojson_to_wkt(read_geojson(self.input_geojson))

    def __exit__(self, exc_type, exc_val, exc_tb):
        # logout here
        pass


def get_available(conn, satellite, date_start, date_end):
    s = satellite
    products = conn.api.query(conn.footprint,
                              date=(date_start, date_end),
                              platformname=satellite,
                              sensoroperationalmode=s.sensormode,
                              producttype=s.product_type,
                              )


def main():
    with SentinelConnection("abc.json", "name", "password") as conn:
        satellite = Satellite('Sputnik X', 'military satellite')
        get_available(conn, satellite, date_start, date_end)

У меня нет никакого представления о том, что такое след. Если разные запросы могут использовать разный отпечаток и запросы часто повторно используют одни и те же посадочные места, создайте класс Location для отпечатка.

Я считаю, что **kwargs решит эту проблему. Это позволяет вам передавать любой аргумент ключевой работы функции, не указывая его в сигнатуре функции, то есть:

def foobar(foo, **kwargs):
   bar = kwargs["bar"]
   return foo + bar


barfoo = foobar(foo='something', bar='other_thing')
Другие вопросы по тегам