programing

루프(또는 이해)에서 함수(또는 람다) 만들기

topblog 2023. 7. 12. 22:11
반응형

루프(또는 이해)에서 함수(또는 람다) 만들기

루프 내부에 함수를 만들려고 합니다.

functions = []

for i in range(3):
    def f():
        return i

    # alternatively: f = lambda: i

    functions.append(f)

문제는 결국 모든 기능이 같다는 것입니다.0, 1 및 2를 반환하는 대신 세 가지 기능이 모두 2를 반환합니다.

print([f() for f in functions])
# expected output: [0, 1, 2]
# actual output:   [2, 2, 2]

왜 이런 일이 발생하고, 각각 0, 1, 2를 출력하는 3개의 다른 함수를 얻으려면 어떻게 해야 합니까?

바인딩이 늦어지는 문제가 발생했습니다. 각 함수가 검색됩니다.i가능한 한 늦게(따라서 루프가 종료된 후 호출될 때)i됩니다.2).

바인딩을 : 바을강적제용쉽여게정수하초변로기 : 경인딩.def f():def f(i=i):다음과 같이:

def f(i=i):
    return i

)ii=i는 인수 이름 " " " 의 입니다.i어느 쪽이 왼손입니까?ii=i를 올려다 봅니다.def시간, 시간이 아닌call시간, 그래서 본질적으로 그것들은 구체적으로 조기 바인딩을 찾는 방법입니다.

.f추가 인수를 얻음(그리고 잠재적으로 잘못 불림), 폐쇄를 "기능 공장"으로 사용하는 것과 관련된 더 정교한 방법이 있습니다.

def make_f(i):
    def f():
        return i
    return f

그리고 당신의 루프 사용에서.f = make_f(i) def진술.

설명

여기서 문제가 되는 것은 다음과 같다는 것입니다.i함수가 저장되지 않습니다.f생성됩니다.오히려.f는 가치를찾니다의 합니다.i이 있을 때는

생각해보면, 이 행동은 완벽하게 이치에 맞습니다.사실, 이것은 기능이 작동할 수 있는 유일한 합리적인 방법입니다.다음과 같은 글로벌 변수에 액세스하는 기능이 있다고 가정합니다.

global_var = 'foo'

def my_function():
    print(global_var)

global_var = 'bar'
my_function()

이 를 읽을 때, "foo가 아니라 "bar"로 될 것이라고 것입니다. "", "foo"의 은 "bar"의 값이기 때문입니다. 왜냐하면, 값이global_var함수가 선언된 후 변경되었습니다.사용자 코드에서도 동일한 현상이 발생합니다.에 전화할 때까지.fi변경되어 다음으로 설정되었습니다.2.

해결책

이 문제를 해결하는 방법은 실제로 여러 가지가 있습니다.다음은 몇 가지 옵션입니다.

  • 바적딩용인의 강제 조기 i합니다.

    변수: 폐변수달예리와쇄예▁()와 i때됩니다.:), 기 인 수 는 될 즉 평 때 가 됩 니 다 시

    for i in range(3):
        def f(i=i):  # <- right here is the important bit
            return i
    
        functions.append(f)
    

    이것이 작동하는 방식/이유에 대한 약간의 통찰력을 주기 위해: 함수의 기본 인수는 함수의 속성으로 저장됩니다. 따라서 현재 값은i스냅샷이 생성되고 저장됩니다.

    >>> i = 0
    >>> def f(i=i):
    ...     pass
    >>> f.__defaults__  # this is where the current value of i is stored
    (0,)
    >>> # assigning a new value to i has no effect on the function's default arguments
    >>> i = 5
    >>> f.__defaults__
    (0,)
    
  • 함수 팩토리를 사용하여 현재 값을 캡처합니다.i끝으로

    당신의 문제의 근본은i변경할 수 있는 변수입니다.우리는 절대 변하지 않을 것이 보장된 또 다른 변수를 만들어 이 문제를 해결할 수 있습니다. 이를 위한 가장 쉬운 방법은 종결입니다.

    def f_factory(i):
        def f():
            return i  # i is now a *local* variable of f_factory and can't ever change
        return f
    
    for i in range(3):           
        f = f_factory(i)
        functions.append(f)
    
  • 사용하다functools.partial현재 가치를 구속하는i로.f

    functools.partial 기존 함수에 인수를 연결할 수 있습니다.어떤 면에서, 그것 또한 일종의 기능 공장입니다.

    import functools
    
    def f(i):
        return i
    
    for i in range(3):    
        f_with_i = functools.partial(f, i)  # important: use a different variable than "f"
        functions.append(f_with_i)
    

주의: 이러한 솔루션은 변수에 새 값을 할당한 경우에만 작동합니다.변수에 저장된 개체를 수정하면 다음과 같은 문제가 다시 발생합니다.

>>> i = []  # instead of an int, i is now a *mutable* object
>>> def f(i=i):
...     print('i =', i)
...
>>> i.append(5)  # instead of *assigning* a new value to i, we're *mutating* it
>>> f()
i = [5]

방법에 주목i기본 인수로 전환했음에도 불구하고 여전히 변경되었습니다!코드가 변형된 경우 i그러면 당신은 의 복사본을 제본해야 합니다.i다음과 같이 기능합니다.

  • def f(i=i.copy()):
  • f = f_factory(i.copy())
  • f_with_i = functools.partial(f, i.copy())

@Aran-Fey의 훌륭한 답변에 추가하기 위해, 두 번째 솔루션에서는 키워드로 수행할 수 있는 함수 내부의 변수를 수정하기를 원할 수도 있습니다.nonlocal:

def f_factory(i):
    def f(offset):
      nonlocal i
      i += offset
      return i  # i is now a *local* variable of f_factory and can't ever change
    return f

for i in range(3):           
    f = f_factory(i)
    print(f(10))

은 각각의 저합니야다장해각각을 .i메모리의 별도 공간에 값을 입력합니다. 예:

class StaticValue:
    val = None

    def __init__(self, value: int):
        StaticValue.val = value

    @staticmethod
    def get_lambda():
        return lambda x: x*StaticValue.val


class NotStaticValue:
    def __init__(self, value: int):
        self.val = value

    def get_lambda(self):
        return lambda x: x*self.val


if __name__ == '__main__':
    def foo():
        return [lambda x: x*i for i in range(4)]

    def bar():
        return [StaticValue(i).get_lambda() for i in range(4)]

    def foo_repaired():
        return [NotStaticValue(i).get_lambda() for i in range(4)]

    print([x(2) for x in foo()])
    print([x(2) for x in bar()])
    print([x(2) for x in foo_repaired()])

Result:
[6, 6, 6, 6]
[6, 6, 6, 6]
[0, 2, 4, 6]

다음과 같이 시도할 수 있습니다.

l=[]
for t in range(10):
    def up(y):
        print(y)
    l.append(up)
l[5]('printing in 5th function')

마지막 행을 수정합니다.

functions.append(f())

는 다음과 같은 이유 입니다.f함수입니다. 파이썬은 함수를 일등 시민으로 취급하며 나중에 호출할 변수로 전달할 수 있습니다.그래서 당신의 원래 코드가 하는 일은 함수 자체를 목록에 추가하는 것이고, 당신이 하고 싶은 일은 함수의 결과를 목록에 추가하는 것인데, 이것은 위의 줄이 함수를 호출함으로써 달성하는 것입니다.

언급URL : https://stackoverflow.com/questions/3431676/creating-functions-or-lambdas-in-a-loop-or-comprehension

반응형