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

#python #class #keyword-argument #optional-arguments

Вопрос:

Пожалуйста, рассмотрите следующий код:

 def build_matrix(x_coordinates, y_coordinates, calc_element, **kwargs):

    matrix = np.zeros((len(x_coordinates), len(y_coordinates)))
    for i in range(len(x_coordinates)):
        for j in range(len(y_coordinates)):
            matrix  = calc_element(x_coordinates[i], y_coordinates[j], i, j, **kwargs)

    return matrix
 

Функция build_matrix используется для построения матрицы путем заполнения ее элементов с помощью вызываемой функции calc_element . Поскольку я точно не знаю, какие все возможные функции могут быть переданы этой build_matrix функции, я использую необязательные аргументы ключевых слов, чтобы мне не нужно было менять build_matrix их каждый раз, когда я хочу использовать их с другой функцией (единственное, что объединяет все эти функции, — это то, что им нужны аргументы x_coords_i, y_coords_j, i, j ).

Тем не менее, мне было интересно, будет ли питоническим использовать класс в этом случае вместо функции. Например, если бы я определил конкретную версию calc_element следующим образом

 class calc_element_velocity_mass():
    def __init__(self, velocity, mass):
        self.velocity = velocity
        self.mass = mass

    def __call__(self, x_coords_i, y_coords_j, i, j):
        return self.velocity * i / x_coords_i - y_coords_j * j * self.mass
 

затем, после инициализации, выполните следующие действия

 calc_element = calc_element_velocity_mass(20, 10)
 

Я мог бы передать его build_matrix без использования необязательных аргументов ключевого слова, т. Е., например, без передачи kwargs = dict(mass = 10, velocity = 20) build_matrix .

Вопрос: Будет ли это правильным способом занятий? И правильна ли моя первоначальная реализация, или мне следует подойти к этому по-другому? Спасибо!

Комментарии:

1. Любой из вариантов кажется мне разумным. Главное здесь то, что build_matrix() определяет, как calc_element используется. Если вы хотите build_matrix заключить контракт, у вас есть большая гибкость в том, каким должен быть ваш контракт. Обратите внимание , что текущая реализация позволяет build_matrix() принимать пустое kwargs значение, поэтому класс calc_element_velocity_mass будет работать с ним как есть.

2. Ну, для начала, def __init__(self, **kwargs): наверное, так и должно быть def __init__(self, velocity, mass) . В любом случае, работает либо то, либо другое.

3. Обратите внимание, что клиент для вашей функции может просто передать lambda x_cords_i, y_cords_j, i, j: calc_element(x_cords_i, y_cords_j, i, j, velocity=20, mass=10) , например (т. Е. с использованием частичного приложения). Важной частью является то, что ваш API должным образом задокументирован

Ответ №1:

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

 def calc_element_velocity_mass(velocity, mass):
    def f(x_coords_i, y_coords_j, i, j):
        return velocity * i / x_coords_i - y_coords_j * j * mass
    return f
 

Вы также можете вернуть лямбду вместо f , но я использовал здесь именованную функцию для ясности.

Комментарии:

1. Ура закрытиям! Это такой отличный способ элегантно инкапсулировать небольшое состояние.