Цепочка функций в 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__
поведениеint
s.для более сложных операций, однако, вы должны определить другие 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)
Это не тот ответ, который вы ищете, и вы можете это знать, но я думал, что дам его в любом случае, потому что если кто-то хотел бы сделать это не из любопытства, а для работы. Они, вероятно,должны иметь" правильный ответ".