Вызов функций на основе динамического списка от пользователя
У меня есть 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
)