2016-06-20 3 views
0

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

def plot_data_processing(data_param_1=3, data_param_N=4, 
     processing_param_1='a', processing_param_2='b', plotting_param_1='c', 
     plotting_param_2=1324): 
    data = get_data(data_param_1=data_param_1, data_param_1=data_param_N), 
    processed_data = process_data(data, processing_param_1=processing_param_1, processing_param_2=processing_param_2) 
    plot_data(processed_data, plotting_param_1=plotting_param_1, plotting_param_2=plotting_param_2) 

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

def plot_data_processing(data_kwargs, processing_kwargs, plotting_kwargs): 
    data = get_data(**data_kwargs), 
    processed_data = process_data(data, **processing_kwargs) 
    plot_data(processed_data, **plotting_kwargs) 

plot_data_processing(dict(data_param_1=3, data_param_N=4), dict(processing_param_1='a', processing_param_2='b'), dict(plotting_param_1='c',plotting_param_2=1324)) 

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

def plot_data_processing(data_getter, data_processor, plotter): 
    data = data_getter(), 
    processed_data = data_processor(data) 
    plotter(processed_data) 

class DataGetter(object): 
    def __init__(self, data_param_1=3, data_param_N=4): 
     self.data_param_1 = data_param_1 
     self.data_param_N = data_param_N 
    def __call__(self): 
     # .... 
     return data 

# ... Also define classes DataProcessor and Plotter 

plot_data_processing(DataGetter(data_param_1=3, data_param_N=4), DataProcessor(processing_param_1='a', processing_param_2='b'), Plotter(plotting_param_1='c',plotting_param_2=1324)) 

Однако это также, кажется, включать ненужную структуру и код пушистой (self.x = х, и все, что). Я могу обойти, что с помощью парциальных (или лямбды):

def plot_data_processing(data_getter, data_processor, plotter): 
    data = data_getter(), 
    processed_data = data_processor(data) 
    plotter(processed_data) 

# Called like: 
plot_data_processing(
    data_getter = partial(get_data, data_param_1=3, data_param_N=4), 
    data_processor = partial(process_data, processing_param_1='a', processing_param_2=3), 
    plotter = partial(plot, plotting_param_1='c', plotting_param_2=1342), 
    ) 

Но это также, кажется, неудовлетворительного - потому что нет четкого «типа» аргументов для вызова функции с - только частичной функцией, которая должна работать при вызываемый - затрудняет работу другого программиста, который хочет использовать эту функцию.

Итак, ни один из этих методов не дает мне чувствовать себя выполненным или счастливым. Наверное, мне нравится частичный, но я хотел бы каким-то образом объявить, что частичная функция подчиняется некоторому интерфейсу.

Кто-нибудь знает лучший способ?

+1

Что значит «заявить, что частичная функция подчиняется некоторому интерфейсу» *? – jonrsharpe

+0

Например, моя функция «data_getter» не должна принимать никаких аргументов и возвращать какой-либо объект данных. Мой «data_processor» должен взять какой-то объект данных и вернуть обработанный объект данных. Я хочу, чтобы функции, которые были переданы, подчинялись этим интерфейсам и могли утверждать, что они делают это до их вызова. Вы можете сделать это с помощью классов (assert isinstance (data_getter, DataGetter)), но я ищу эквивалент, когда объект data_getter является частичной функцией, а интерфейс DataGetter указывает на ввод/вывод функции, а не методы для класс. – Peter

+1

На самом деле это не то, что вы делаете на Python.Обычно мы придерживаемся утиного ввода: если вы передаете что-то, что имеет соответствующий интерфейс, все работает, если не вы получаете 'TypeError' (или другое исключение). Нет никакой разницы между функциями и методами. – jonrsharpe

ответ

1

Python 3.5 имеет новую (необязательную) систему подсказок типа, которая может делать то, что вы хотите. Он не проверяется во время выполнения интерпретатором Python, но позволяет вам делать утверждения о типах аргументов и значениях возвращаемых функций. В коде может быть запущена отдельная программа статического анализатора, например mypy, для поиска ошибок ввода.

Для вашего plot_data_processing функции, я думаю, что вы хотите объявить вещи что-то вроде этого:

from typing import Callable, TypeVar 

DataType = TypeVar("DataType") 
ProcessedDataType = TypeVar("ProcessedDataType") # could be the same as DataType 

def plot_data_processing(data_getter: Callable[[], DataType], 
         data_processor: Callable[[DataType], ProcessedDataType], 
         plotter: Callable[[ProcessedDataType], None]) -> None: 
    ... 

Вы могли бы быть в состоянии уйти с только один DataType, а не два, если функция data_processer возвращает то же самое обработанные данные с использованием того же типа, что и исходные данные. Вы также можете указать эти типы более конкретно (например, с помощью Sequence[float] или что угодно, вместо использования TypeVar), если вам не нужен общий подход.

См. PEP 484 и документацию the typing module для более подробной информации.

Смежные вопросы