Цепочка функций в Python
ВКЛ codewars.com я столкнулся со следующей задачей:
создать функцию
add, которая добавляет цифры вместе, когда называют в последовательности. Так чтоadd(1)должен возвратить1,add(1)(2)должен возвратить1+2, ...
хотя я знаком с основами Python, я никогда не сталкивался с функцией, которая может быть вызвана в такой последовательности, т. е. функция f(x) Это можно назвать как f(x)(y)(z).... До сих пор, я даже не знаю как интерпретировать эту запись.
как математик, я подозреваю, что f(x)(y) - это функция, которая присваивает каждому x функция g_{x} и затем возвращает g_{x}(y) и аналогично для f(x)(y)(z).
если эта интерпретация будет правильной, Python позволит мне динамически создавать функции, которые мне кажутся очень интересными. Я искал в интернете в течение последнего часа, но не смог найти зацепку в правильном направлении. Так как я не знаю, как это программирование концепция называется, однако, это не может быть слишком удивительно.
как вы называете эту концепцию и где я могу прочитать больше об этом?
5 ответов:
я не знаю, является ли это функции цепочка столько, сколько это вызвать цепочка, но, так как функции are callables я думаю, что нет никакого вреда. В любом случае, есть два способа, которыми я могу думать об этом:
суб-причислять
intи определения__call__:первый способ будет с таможней
intподкласс, который определяет__call__который возвращает новый экземпляр с обновлено значение:class CustomInt(int): def __call__(self, v): return CustomInt(self + v)функции
addтеперь можно определить, чтобы вернуть aCustomIntэкземпляр, который, как вызываемый, который возвращает обновленное значение самого себя, может быть вызван последовательно:>>> def add(v): ... return CustomInt(v) >>> add(1) 1 >>> add(1)(2) 3 >>> add(1)(2)(3)(44) # and so on.. 50кроме того, как
intподкласс, возвращаемое значение сохраняется__repr__и__str__поведениеints.для более сложных операций, однако, вы должны определить другие dunders соответствующим образом.как @Caridorc отметил в a комментарий
addтакже может быть просто написано как:add = CustomIntпереименование класса в
addвместоCustomIntтакже работает аналогично.
определите закрытие, требуется дополнительный вызов для получения значения:
единственный другой способ, который я могу придумать, включает вложенную функцию, которая требует дополнительного пустого вызова аргумента, чтобы вернуть результат. Я не используя
nonlocalи выбрать для присоединения атрибутов к объектам функции, чтобы сделать он переносится между питонами:def add(v): def _inner_adder(val=None): """ if val is None we return _inner_adder.v else we increment and return ourselves """ if val is None: return _inner_adder.v _inner_adder.v += val return _inner_adder _inner_adder.v = v # save value return _inner_adderэто постоянно возвращает себя (
_inner_adder) который, если avalпоставляется, увеличивает его (_inner_adder += val) и если нет, возвращает значение, как это. Как я уже упоминал, это требует дополнительного()вызов для возврата увеличенного значения:>>> add(1)(2)() 3 >>> add(1)(2)(3)() # and so on.. 6
вы можете ненавидеть меня, но вот один-лайнер :)
add = lambda v: type("", (int,), {"__call__": lambda self, v: self.__class__(self + v)})(v)Edit: хорошо, как это работает? Код идентичен ответу @Jim, но все происходит в одной строке.
typeможет использоваться для построения новых типов:type(name, bases, dict) -> a new type. Ибоnameмы предоставляем пустую строку, так как имя действительно не нужно в этом случае. Ибоbases(кортеж) мы предлагаем(int,), что идентично наследованиюint.dictявляются атрибутами класса, куда мы прикрепляем элемент__call__лямда.self.__class__(self + v)идентиченreturn CustomInt(self + v)- новый тип строится и возвращается в пределах внешней лямбды.
Если вы хотите определить функцию, которая будет вызываться несколько раз, сначала вам нужно каждый раз возвращать вызываемый объект (например, функцию), иначе вам нужно создать свой собственный объект, определив
__call__атрибут, чтобы он был вызываемым.следующий момент заключается в том, что вам нужно сохранить все аргументы, что в данном случае означает, что вы можете использовать сопрограммы или рекурсивная функция. Но обратите внимание, что сопрограммы гораздо больше оптимизированные / гибкие, чем рекурсивные функции, специально для таких задач.
вот пример функции, использующей сопрограммы, которая сохраняет последнее состояние себя. Обратите внимание, что он не может быть вызван несколько раз, так как возвращаемое значение является
integerкоторый не вызывается, но вы можете подумать о превращении этого в ваш ожидаемый объект; -).def add(): current = yield while True: value = yield current current = value + current it = add() next(it) print(it.send(10)) print(it.send(2)) print(it.send(4)) 10 12 16
питонский способ сделать это будет использовать динамические аргументы:
def add(*args): return sum(args)Это не тот ответ, который вы ищете, и вы можете это знать, но я думал, что дам его в любом случае, потому что если кто-то хотел бы сделать это не из любопытства, а для работы. Они, вероятно,должны иметь" правильный ответ".