-1
votes

Arguments variables / facultatifs dans une fonction au sein d'une fonction

Je travaille sur un script qui me permet de me connecter à la base de données satellite Sentinel pour télécharger les fichiers cartographiques demandés.

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,
                         )

Mon problème est que le type d'entrée "satellite" que je vais utiliser changera quels autres arguments sont nécessaires, requis ou même autorisés. Certains n'auront pas besoin de "sensormode" et d'autres auront peut-être besoin de "cloudcoverage". Comment pourrais-je écrire un code propre avec des arguments variables / optionnels dans une fonction au sein d'une fonction? Dois-je énumérer tous les arguments possibles?


1 commentaires

Qu'est-ce que "dans une fonction dans une fonction"? Qu'est-ce qui détermine les arguments nécessaires?


3 Réponses :


0
votes

Je pense que cela serait bien résolu par ** kwargs. Cela vous permet de passer n'importe quel argument de clavier à une fonction sans spécifier ensuite dans la signature de la fonction, c'est-à-dire:

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


barfoo = foobar(foo='something', bar='other_thing')


1 commentaires

Merci pour votre réponse! Utiliser ** kwargs semble être l'option la plus simple. J'ai créé un dictionnaire en dehors de la fonction et je le donnerai simplement comme un ** kwarg à la fonction à utiliser dans l'API. semble fonctionner jusqu'à présent



1
votes

Cette API semble trop fastidieuse à utiliser. Mieux vaut grouper les arguments avec des classes.

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)

Je n'ai aucune idée de ce qu'est une empreinte. Si différentes requêtes peuvent utiliser une empreinte différente et que les requêtes réutilisent souvent les mêmes empreintes, créez une classe Location pour l'empreinte.


1 commentaires

Merci pour votre réponse rapide! Je vais examiner votre code. L'empreinte est juste un fichier geojson d'un polygone avec les coordonnées dont je veux obtenir les images satellite.



1
votes

Je trouve que la réponse de Crawl Cycle est très pythonique et belle, alors je recommande d'y aller. Quoi qu'il en soit, je me suis amusé à travailler dessus, alors voici mon interprétation de ce que vous recherchiez :)

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))

Démo: http://tpcg.io/AONnvHn9 .

Il y a quelques contraintes qui gardent cette implémentation aussi concise qu'elle est:

  • Toutes les signatures API doivent être uniques.
  • Toutes les signatures d'API n'acceptent que les arguments de mot-clé.


0 commentaires