에 Codewars.com 나는 다음과 같은 작업을 발생 :
add
연속적으로 호출 될 때 숫자를 더하는 함수 를 만듭니다 . 그래서add(1)
return1
,add(1)(2)
return1+2
, …
나는 파이썬의 기초에 익숙하지만, 연속적 f(x)
으로 호출 될 수 있는 함수, 즉 f(x)(y)(z)...
. 지금까지이 표기법을 어떻게 해석해야할지 모르겠습니다.
수학자로서, 그 의심 것 f(x)(y)
그 모든에 할당하는 기능입니다 x
함수 g_{x}
다음 반환 g_{x}(y)
마찬가지로위한 f(x)(y)(z)
.
이 해석이 옳다면 파이썬은 나에게 매우 흥미로워 보이는 함수를 동적으로 만들 수있게 해줄 것입니다. 지난 한 시간 동안 웹을 검색했지만 올바른 방향으로 리드를 찾을 수 없었습니다. 그러나이 프로그래밍 개념이 어떻게 호출되는지 모르기 때문에 이것은 그리 놀라운 일이 아닙니다.
이 개념을 어떻게 부르고 그것에 대한 자세한 내용을 어디에서 읽을 수 있습니까?
답변
이것이 콜 러블 체이닝 만큼 함수 체이닝 인지는 모르겠지만, 함수 가 콜 러블 이기 때문에 해를 끼치 지 않는 것 같습니다. 어느 쪽이든, 이렇게 생각할 수있는 두 가지 방법이 있습니다.
하위 분류 int
및 정의 __call__
:
첫 번째 방법은 업데이트 된 값으로 자신의 새 인스턴스를 반환하는 int
것을 정의 하는 사용자 정의 하위 클래스를 사용하는 것입니다 __call__
.
class CustomInt(int):
def __call__(self, v):
return CustomInt(self + v)
add
이제 CustomInt
자신의 업데이트 된 값을 반환하는 콜 러블로서 연속적으로 호출 할 수 있는 인스턴스 를 반환하도록 함수 를 정의 할 수 있습니다.
>>> def add(v):
... return CustomInt(v)
>>> add(1)
1
>>> add(1)(2)
3
>>> add(1)(2)(3)(44) # and so on..
50
또한 int
하위 클래스 로서 반환 된 값 은 s 의 __repr__
및 __str__
동작을 유지합니다 int
. 하지만 더 복잡한 작업의 경우 다른 던더를 적절하게 정의해야합니다 .
@Caridorc가 주석에서 언급했듯이 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
)를 반환 하며, a val
가 제공되면 증분 ( _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)
편집 : 좋아, 어떻게 작동합니까? 코드는 @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)
이것은 당신이 찾고있는 대답이 아니고 당신도 이것을 알고 있을지도 모르지만, 누군가가 호기심 때문이 아니라 일을 위해 이것을하는 것에 대해 궁금해한다면 나는 그것을 줄 것이라고 생각했습니다. 그들은 아마도 “올바른 일”에 대한 답을 가지고있을 것입니다.
답변
()
결과를 검색하기 위해 추가를 수락하려면 다음을 사용할 수 있습니다 functools.partial
.
from functools import partial
def add(*args, result=0):
return partial(add, result=sum(args)+result) if args else result
예를 들면 :
>>> add(1)
functools.partial(<function add at 0x7ffbcf3ff430>, result=1)
>>> add(1)(2)
functools.partial(<function add at 0x7ffbcf3ff430>, result=3)
>>> add(1)(2)()
3
이를 통해 한 번에 여러 숫자를 지정할 수도 있습니다.
>>> add(1, 2, 3)(4, 5)(6)()
21
단일 번호로 제한하려면 다음을 수행 할 수 있습니다.
def add(x=None, *, result=0):
return partial(add, result=x+result) if x is not None else result
당신이 원하는 경우 add(x)(y)(z)
쉽게 결과를 반환 하고 있을 더 호출 한 후 하위 클래스라는이 int
길을 가야하는 것입니다.
답변
간단히:
class add(int):
def __call__(self, n):
return add(self + n)