2014-01-10 4 views
14

익명의 함수로 놀고 싶었 기 때문에 간단한 프라임 파인더를 만들기로했습니다. 여기있다 : 내가 그러나 발견하면 lambda x:x%i==0i 매번 액세스 할 수 있다는 것입니다변수 외부에있는 람다 함수

tests = [] 
end = int(1e2) 
i = 3 
while i <= end: 
    a = map(lambda f:f(i),tests) 
    if True not in a: 
     tests.append(lambda x:x%i==0) 
     print i 
    print tests 
    print "Test: "+str(i) 
    print str(a) 
    i+=2 

, 내가 그것을 리터럴 숫자하려는있다. 어떻게하면 lambda x:x%3==0이 될 수 있습니까?

+1

공식 문서의 내용보다 더 자세한 설명은 [This Nasty Closures] (http://code.activestate.com/recipes/502271/)를 참조하십시오. 그러나 짧은 버전은이 테스트 함수 각각이 동일한 변수 'i'를 효과적으로 닫는 것이며 'i'는 값을 계속 변경한다는 것입니다. (이것은 전역 변수가 클로저에 실제로 저장 될 필요가 없기 때문에 매우 정확하지는 않지만 결과는 동일합니다.) – abarnert

답변

27

당신은 "캡처"할 수있는 i이 그것이 만들 때 i이었다 동일 무엇에 람다의 맥락에서 i을 설정합니다 람다에게

lambda x, i=i: x%i==0 

를 만들 때. 원하는 경우 lambda x, n=i: x%n==0이라고 말할 수도 있습니다. 정확히 캡처하지는 않지만 필요한 것을 얻을 수 있습니다. 당신이 보았 듯이이없이

은, 클로징 범위에 i 찾는 것


그것은 정의 함수에 다음과 유사한의 조회의 문제입니다 :

i = "original" 

def print_i1(): 
    print(i) # prints "changed" when called below 

def print_i2(s=i): #default set at function creation, not call 
    print(s) # prints "original" when called below 


i = "changed" 
print_i1() 
print_i2() 
+0

오, 영리합니다. – user2864740

+0

+1 매우 좋음 :) –

+2

기본 인수가 함수가 * created * 일 때 평가되기 때문에 이것은 작동합니다. 반면 변수 조회는 함수가 * 호출 될 때 수행됩니다 *. –

2

람다를 반환하는 새 함수를 만듭니다. 그런 다음 i을 인수로 전달하십시오. 이렇게하면 새 바인딩 범위가 만들어집니다.

def make_test (i): 
    # this i refers to the parameter (which evaluates to the /value/ passed) 
    return lambda x: x%i==0 

# .. 
# the /value/ resulting from evaluating the variable is passed 
tests.append(make_test(i)) 
6

tests에있는 해당 함수 각각이 i 변수를 참조하고 있습니다.

더 일반적으로 함수 내에서이 작업을 수행합니다.이 경우 These Nasty Closures에 설명 된 것처럼 클로저에 저장되는 i 변수를 로컬에 정의 할 수 있습니다.

하지만 여기서도 더 간단합니다. i은 전역 변수이므로 닫힘이 없습니다. 이 함수는 실행할 때 전역 변수로 i을 찾도록 컴파일됩니다. i이 변경 되었기 때문에 함수가 실행될 때 변경된 값이 표시됩니다. 그처럼 간단합니다.


정말 해킹 아니더라도, 다정하게 "기본 값 해킹"로 알려져있다 (모두 폐쇄 및 전역 작동)이 주변의 전통적인 방법. (the explanation in the FAQ를 참조하십시오.) 라이언 이닝의 대답은이 작업을 수행하는 방법에 대해 설명합니다

lambda x, i=i: x%i==0 

이 함수가 생성 될 때 i의 값과 동일한 기본 값으로, i라는 매개 변수를 만듭니다. 그런 다음 함수 내부에서 매개 변수 i에 액세스하면 해당 값을 얻습니다.


자바 스크립트 같은 언어를 사용하는 경우 더 익숙한 보일 수도 주변의 다른 방법으로는, 함수 만드는 함수를 작성하고, 그 기능 - 인수로 i의 가치를 전달하는 것입니다 user2864740의 대답으로, 함수를 만드는 :

(lambda i: lambda x: x%i)(i) 

이 (누군가가 실수로 인수를 전달할 수 있음) 추가 매개 변수와 함수의 서명을 "오염"하지만 생성하고 함수를 호출의 비용 피한다 아무 이유없이.


세 번째 방법은 partial입니다. 모든 작업을 수행하려고 할 때 부분적으로 함수를 적용하는 경우 래퍼 함수를 ​​정의하는 대신 partial을 사용하여 lambda을보다 명확하게 정의 할 수 있습니다.

불행히도이 경우 함수는 연산자 안에 숨겨져 있으며 노출하는 함수 operator.mod은 키워드 인수를 사용하지 않으므로 두 번째 피연산자를 부분적으로 부분적으로 나눌 수 없습니다. 그래서,이 경우에는 나쁜 해결책입니다. 당신이 정말로, 당신은 단지 동작 래퍼를 작성할 수 싶었다면 더 partial이 :이 경우

def opmod(a, b): 
    return a % b 

partial(operator.mod, b=i) 

, 당신이 다른 솔루션과 더 나을 것 같아요; 이 적절한 경우 인 경우 머리에 보관하십시오.