Грубо говоря, partial
делает что-то вроде этого (кроме поддержки аргументов ключевых слов и т. Д.):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper
Поэтому, вызывая, partial(sum2, 4)
вы создаете новую функцию (точнее, вызываемую), которая ведет себя как sum2
, но имеет на один позиционный аргумент меньше. Это недостающее аргумент всегда замещено 4
, так чтоpartial(sum2, 4)(2) == sum2(4, 2)
Что касается того, почему это необходимо, есть множество случаев. Предположим, вам нужно передать функцию где-нибудь, где ожидается, что она имеет 2 аргумента:
class EventNotifier(object):
def __init__(self):
self._listeners = []
def add_listener(self, callback):
''' callback should accept two positional arguments, event and params '''
self._listeners.append(callback)
# ...
def notify(self, event, *params):
for f in self._listeners:
f(event, params)
Но функция, которая у вас уже есть, нуждается в доступе к какому-либо третьему context
объекту, чтобы выполнить свою работу:
def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
Итак, есть несколько решений:
Пользовательский объект:
class Listener(object):
def __init__(self, context):
self._context = context
def __call__(self, event, params):
self._context.log_event("Something happened %s, %s", event, params)
notifier.add_listener(Listener(context))
Lambda:
log_listener = lambda event, params: log_event(context, event, params)
notifier.add_listener(log_listener)
С частями:
context = get_context() # whatever
notifier.add_listener(partial(log_event, context))
Из этих трех partial
самый короткий и самый быстрый. (Для более сложного случая вам может понадобиться пользовательский объект).
extra_args
extra_args
это то, что прошло частичный вызывающий, в примере сp = partial(func, 1); f(2, 3, 4)
ним есть(2, 3, 4)
.callback
иmy_callback
частичные случаи невероятно полезны.
Например, в «конвейерной» последовательности вызовов функций (в которой возвращаемое значение из одной функции является аргументом, передаваемым следующей).
Иногда функция в таком конвейере требует одного аргумента , но функция сразу после нее возвращает два значения .
В этом случае
functools.partial
может позволить вам сохранить этот конвейер функции без изменений.Вот конкретный изолированный пример: предположим, что вы хотите отсортировать некоторые данные по расстоянию каждой точки данных от некоторой цели:
Чтобы отсортировать эти данные по расстоянию от цели, конечно, вы хотели бы сделать следующее:
но вы не можете - ключевой параметр метода сортировки принимает только функции, которые принимают один аргумент.
поэтому переписать
euclid_dist
как функцию, принимающую единственный параметр:p_euclid_dist
теперь принимает один аргумент,так что теперь вы можете отсортировать данные, передав частичную функцию для ключевого аргумента метода сортировки:
Или, например, один из аргументов функции изменяется во внешнем цикле, но фиксируется во время итерации во внутреннем цикле. При использовании частичного вам не нужно передавать дополнительный параметр во время итерации внутреннего цикла, потому что модифицированная (частичная) функция этого не требует.
создать частичную функцию (используя ключевое слово arg)
Вы также можете создать частичную функцию с позиционным аргументом
но это сгенерирует (например, создание партиала с аргументом ключевого слова, затем вызов с использованием позиционных аргументов)
Другой вариант использования: написание распределенного кода с использованием
multiprocessing
библиотеки Python . Пул процессов создается с помощью метода Pool:Pool
имеет метод map, но он принимает только одну итерацию, поэтому, если вам нужно передать функцию с более длинным списком параметров, переопределите функцию как частичную, чтобы исправить все, кроме одного:источник
краткий ответ,
partial
дает значения по умолчанию для параметров функции, которая в противном случае не имела бы значений по умолчанию.источник
partial
и так далееЧастицы могут использоваться для создания новых производных функций, которым предварительно назначены некоторые входные параметры
Чтобы увидеть реальное использование партиалов, обратитесь к этому действительно хорошему сообщению в блоге:
http://chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/
Простые , но опрятные начинающий пример из блог, обложки , как можно было бы использовать
partial
на ,re.search
чтобы сделать код более читаемым.re.search
подпись метода:Применяя,
partial
мы можем создать несколько версий регулярного выраженияsearch
в соответствии с нашими требованиями, например:Теперь
is_spaced_apart
иis_grouped_together
две новые функции, полученные изre.search
этого, имеютpattern
примененный аргумент (такpattern
как это первый аргумент вre.search
сигнатуре метода).Сигнатура этих двух новых функций (вызываемых):
Вот как вы могли бы использовать эти частичные функции для некоторого текста:
Вы можете обратиться по ссылке выше, чтобы получить более глубокое понимание предмета, так как он охватывает этот конкретный пример и многое другое.
источник
is_spaced_apart = re.compile('[a-zA-Z]\s\=').search
? Если так, есть ли гарантия, чтоpartial
идиома скомпилирует регулярное выражение для более быстрого повторного использования?На мой взгляд, это способ реализации карри в Python.
Результат 3 и 4.
источник
Также стоит отметить, что когда частичная функция передает другую функцию, где мы хотим «жестко закодировать» некоторые параметры, это должен быть самый правый параметр.
но если мы сделаем то же самое, но изменим параметр вместо
будет выдано сообщение об ошибке: «TypeError: func () получил несколько значений для аргумента« a »»
источник
prt=partial(func, 7)
Этот ответ является скорее примером кода. Все приведенные выше ответы дают хорошие объяснения относительно того, почему следует использовать частичное. Я приведу свои наблюдения и буду использовать случаи частичного.
Вывод приведенного выше кода должен быть:
Обратите внимание, что в приведенном выше примере был возвращен новый вызываемый объект, который будет принимать параметр (c) в качестве аргумента. Обратите внимание, что это также последний аргумент функции.
Вывод вышеуказанного кода также:
Обратите внимание, что * использовался для распаковки аргументов без ключевого слова, и возвращаемый вызываемый элемент, в котором он может принимать аргумент, такой же, как указано выше.
Другое наблюдение: Пример ниже демонстрирует, что partal возвращает вызываемый объект, который примет необъявленный параметр (a) в качестве аргумента.
Вывод приведенного выше кода должен быть:
Так же,
Выше кода печатает
Я должен был использовать его, когда я использовал
Pool.map_async
метод изmultiprocessing
модуля. Вы можете передать только один аргумент в рабочую функцию, поэтому мне пришлось использовать,partial
чтобы моя рабочая функция выглядела как вызываемая только с одним входным аргументом, но на самом деле моя рабочая функция имела несколько входных аргументов.источник