Вызов функций на основе динамического списка от пользователя

У меня есть API генерации отчетов в моем django приложении, которое принимает название отчетов как ввод от пользователя, пользователь может выбирать различные отчеты из выпадающего списка

    reports = request.GET['reports'].split(',')

пример:

    print("selected reports", reports)

    >>seleceted reports ['AS', 'DD', 'IS', 'LM']

теперь это reports может меняться в зависимости от пользователя, и у меня есть словарь всех функций для создания этих отчетов

master = {
            'AS': audit_summary(),
            'AH': audit_health(),
            'MS': missing_scan(),
            'IS': individual(),
            'LM': loc_mis(),
            'MAS': missing_assets(),
            'DD' : dump(),
              }

Как я могу вызывать функции из словаря на основе динамического списка, который я получаю от пользователя?

for i in selected_reports:
    masters[i]   # this will call the function

предполагая, что переменная selected_reports содержит список

Кажется, я понял, что вы пытаетесь сделать. Вы можете изменить основной список, чтобы он был словарем функций вместо результатов функций, как это сделано здесь. Я не уверен на 100% в синтаксисе в конце, но я знаю, что это возможно.

master = {
            'AS': audit_summary,
            'AH': audit_health,
            'MS': missing_scan,
            'IS': individual,
            'LM': loc_mis,
            'MAS': missing_assets,
            'DD' : dump,
              }

def callAllTheFuncts(data):
    for item in data:
        master[item]()

Я предлагаю создать словарь так, чтобы он включал вызываемые и аргументы отдельно. Это предотвратит нежелательные вызовы. Вот упрощенный пример, который вы можете модифицировать под свои нужды:

data = {
    'a': {'function': print, 'parameters': ['a']},
    'b': {'function': print, 'parameters': ['b']},
    'c': {'function': print, 'parameters': ['c']}
}


def call_funcs():
    for _, v in data.items():
        v['function'](*v['parameters'])


call_funcs()


# Output:
# a
# b
# c

Конечно, вы можете использовать словарь в качестве аргумента вместо списка, но только не забудьте заменить * на ** для аргументов с ключевыми словами.

С вашими параметрами исходный словарь будет выглядеть следующим образом:

master = {
    'AS': {'function': Summary, 'parameters': [df, description_dict, emp]},
    'AH': {'function': lastSeenMissingAsset, 'parameters': [df, emp, description_dict]},
    'MS': {'function': missingScans, 'parameters': [df, emp]},
    'IS': {'function': individualScans, 'parameters': [df]},
    'LM': {'function': tagMismatch, 'parameters': [df, emp]},
    'MAS': {'function': missingAssets, 'parameters': [df, emp, description_dict, deployed_df]},
    'MAU': {'function': missingAudit, 'parameters': [df, pd.to_datetime(tt).date()]},
    'DD': {'function': dumpData, 'parameters': [df]}
}
<

По требованию более эффективно - предполагая, что содержимое меняется от пользователя/запроса, поэтому я принял этот подход.

Если вы планируете, что отчеты будут создаваться только один раз, а не по каждому запросу, то решение изменится.

Вы можете использовать functools.partial для создания вызываемого объекта, который вызывает ваши функции, генерирующие отчеты. Эта функция позволяет создавать "частичные" вызовы функций/методов, которые могут быть вызваны позже с добавлением дополнительных args или kwargs (иногда это бывает полезно). Но ее также можно использовать для создания вызываемой функции, которая не требует дополнительных args при вызове.

Из официальной документации:

Возвращает новый частичный объект, который при вызове будет вести себя как func, вызванный с позиционными аргументами args и ключевыми аргументами keywords. Если к вызову прилагаются дополнительные аргументы, они добавляются к args. Если предоставлены дополнительные аргументы ключевых слов, они расширяют и переопределяют ключевые слова.

from functools import partial

report_generators = dict(
    AS = partial(Summary, df, description_dict, emp),
    AH = partial(lastSeenMissingAsset, df, emp, description_dict),
    MS = partial(missingScans, df, emp),
    IS = partial(individualScans, df),
    LM = partial(tagMismatch, df, emp),
    MAS = partial(missingAssets, df, emp, description_dict, deployed_df),
    MAU = partial(missingAudit, df, partial(pd.to_datetime(tt).date, )),
    DD = partial(dumpData, df),
)

def my_route(self):
    report_ids = request.GET['reports'].split(',')
    # Generate as a flat list.
    report_contents = [report_generators[report_id]() for report_id in report_ids]
    # ..or generate as a dict, keyed by report id.
    report_contents = {report_id:report_generators[report_id]() for report_id in report_ids}
    # Now do stuff with the contents :)

Как указано во всех комментариях, проблема заключается в том, что все ваши функции (Summary и т.д.) вызываются один раз при создании главного словаря. Затем вы перебираете выбранные отчеты, тем самым вызывая некоторые функции второй раз.

Решением действительно является модификация master и использование partial:

master = {
    'AS': partial(Summary, df, description_dict, emp),
    'AH': partial(lastSeenMissingAsset, df, emp, description_dict),
    'MS': partial(missingScans, df, emp),
    'IS': partial(individualScans, df),
    'LM': partial(tagMismatch, df, emp),
    'MAS': partial(missingAssets, df, emp, description_dict, deployed_df),
    'MAU': partial(missingAudit, df, pd.to_datetime(tt).date()),
    'DD': partial(dumpData, df)
}

Затем, когда вы выполняете итерацию над master:

for i in reports:
    final[i] = master[i]()  # notice the () --> master[i] is a Callable

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

Например:

master = {
  'AS': (Summary, (df, description_dict, emp)),
  'AH': (lastSeenMissingAsset, (df, emp, description_dict)),
  'MS': (missingScans, (df, emp)),
  'IS': (individualScans, (df,)),
  'LM': (tagMismatch, (df, emp)),
  'MAS': (missingAssets, (df, emp, description_dict, deployed_df)),
  'MAU': (missingAudit, (df, pd.to_datetime(tt).date())),
  'DD': (dumpData, (df,))
}

final={}
print("selected reports", reports)
for i in reports:
  fn, args = master[i]
  final[i] = fn(*args)
print("final", final)

Вместе с lambda, вызывается только при использовании:

master = {
    'AS': lambda: Summary(df, description_dict, emp),
    'AH': lambda: lastSeenMissingAsset(df, emp, description_dict),
    ...
}

...
    final[i] = master[i]()

Проблема в том, как ваши функции ссылаются в вашем master словаре.

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

master = {
        'AS': Summary,
        'AH': lastSeenMissingAsset,
        'MS': missingScans,
        'IS': individualScans,
        'LM': tagMismatch,
        'MAS': missingAssets,
        'MAU': missingAudit,
        'DD': dumpData
}                 

и передавайте аргументы при прохождении итерации. Например, так:

final={}
    print("selected reports", reports)
    for i in reports:
        final[i] = master[i](**kwargs)
    print("final", final)

где **kwargs определяет аргументы, которые нужно передать функции при ее выполнении.

Примечание: более питоничным именованием является использование имен функций в нижнем регистре и змеином регистре (this_is_my_function)

Вернуться на верх