IT

Python에서 람다 식 내부 할당

lottoking 2020. 9. 4. 07:42
반응형

Python에서 람다 식 내부 할당


개체 목록이 있고 하나를 제외하고 비어있는 모든 개체, filterlambda을 제거하고 싶습니다 .

예를 들어 입력이 다음과 같은 경우 :

[Object(name=""), Object(name="fake_name"), Object(name="")]

... 출력은 다음과 같네요.

[Object(name=""), Object(name="fake_name")]

lambda식에 할당을 추가하는 방법이 있습니까? 예를 들면 :

flag = True 
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(
    (lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]),
    input
)

:=Python 3.8에 추가 된 할당 연산자 는 람다 내부의 할당을 지원합니다. 이 연산자는 구문상의 여러 괄호 (...), 괄호 [...]또는 괄호 로 묶인 {...}하나만 나타날 수 있습니다 . 예를 들어 다음과 같이 있고 있습니다.

import sys
say_hello = lambda: (
    message := "Hello world",
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

Python 2에서는 목록 이해의 부작용으로 로컬 할당을 수행 할 수 있습니다.

import sys
say_hello = lambda: (
    [None for message in ["Hello world"]],
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

그러나 변수 flag가의 범위가 아닌 외부 범위에 있기 때문에 이들 중 하나를 사용할 수 없습니다 lambda. 이것은 관련이 없습니다. 이것은 lambdaPython 2의 일반적인 동작입니다. 파이썬 3를 사용하면 nonlocal내부의 키워드 로이 문제를 해결할 수 def있지만의 내부에서는 nonlocal사용할 수 없습니다 lambda.

해결 방법이 제안 (아래 참조) 주제를 다루는 동안 ...


어떤 경우에는 내부의 모든 작업을 수행 할 수 있습니다 lambda.

(lambda: [
    ['def'
        for sys in [__import__('sys')]
        for math in [__import__('math')]

        for sub in [lambda *vals: None]
        for fun in [lambda *vals: vals[-1]]

        for echo in [lambda *vals: sub(
            sys.stdout.write(u" ".join(map(unicode, vals)) + u"\n"))]

        for Cylinder in [type('Cylinder', (object,), dict(
            __init__ = lambda self, radius, height: sub(
                setattr(self, 'radius', radius),
                setattr(self, 'height', height)),

            volume = property(lambda self: fun(
                ['def' for top_area in [math.pi * self.radius ** 2]],

                self.height * top_area))))]

        for main in [lambda: sub(
            ['loop' for factor in [1, 2, 3] if sub(
                ['def'
                    for my_radius, my_height in [[10 * factor, 20 * factor]]
                    for my_cylinder in [Cylinder(my_radius, my_height)]],

                echo(u"A cylinder with a radius of %.1fcm and a height "
                     u"of %.1fcm has a volume of %.1fcm³."
                     % (my_radius, my_height, my_cylinder.volume)))])]],

    main()])()

반지름이 10.0cm이고 높이가 20.0cm 인 원통의 부피는 6283.2cm³입니다.
반지름이 20.0cm이고 높이가 40.0cm 인 실린더의 부피는 50265.5cm³입니다.
반지름이 30.0cm이고 높이가 60.0cm 인 실린더의 부피는 169646.0cm³입니다.

하지 마십시오.


... 원래 예제로 돌아가서 : flag외부 범위 변수에 할당을 수행 할 수는 없지만 함수를 사용하여 이전에 할당 된 값을 수정할 수 있습니다.

예를 들어 다음을 사용하여 설정 flag한 객체 수 있습니다 ..valuesetattr

flag = Object(value=True)
input = [Object(name=''), Object(name='fake_name'), Object(name='')] 
output = filter(lambda o: [
    flag.value or bool(o.name),
    setattr(flag, 'value', flag.value and bool(o.name))
][0], input)
[Object(name=''), Object(name='fake_name')]

위의 테마에 맞추고 싶다면 setattr다음 대신 목록 이해력을 사용할 수 있습니다 .

    [None for flag.value in [bool(o.name)]]

그러나 실제로 심각한 코드에서는 lambda외부 할당을 수행 하려는 경우 대신 항상 정규 함수 정의를 사용해야 합니다.

flag = Object(value=True)
def not_empty_except_first(o):
    result = flag.value or bool(o.name)
    flag.value = flag.value and bool(o.name)
    return result
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(not_empty_except_first, input)


filter/ lambda표현식 에서 상태를 실제로 유지할 수 없습니다 (전역 네임 스페이스를 남용하지 않는 한). 그러나 reduce()표현식 에서 전달되는 누적 결과를 사용하여 비슷한 것을 얻을 수 있습니다 .

>>> f = lambda a, b: (a.append(b) or a) if (b not in a) else a
>>> input = ["foo", u"", "bar", "", "", "x"]
>>> reduce(f, input, [])
['foo', u'', 'bar', 'x']
>>> 

물론 조건을 약간 조정할 수 있습니다. 이 경우 중복을 필터링하지만 a.count("")예를 들어를 사용하여 빈 문자열 만 제한 할 수도 있습니다 .

말할 필요도없이, 당신은 이것을 할 수 있지만 정말로해서는 안됩니다. :)

마지막으로 순수한 Python으로 무엇이든 수 있습니다lambda . http://vanderwijk.info/blog/pure-lambda-calculus-python/


null을 모두 제거 하고 입력 크기가 변경되면 다시 넣을 수있는 경우 람다를 사용할 필요가 없습니다 .

input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = [x for x in input if x.name]
if(len(input) != len(output)):
    output.append(Object(name=""))

및 친구들과 다양한 트릭을 수행하는 것은 가능하지만 표현식 =내에서는 일반 할당 ( )이 불가능합니다 .lambdasetattr

그러나 문제를 해결하는 것은 실제로 매우 간단합니다.

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input
    )

당신에게 줄 것입니다

[Object(Object(name=''), name='fake_name')]

보시다시피 마지막 인스턴스 대신 첫 번째 빈 인스턴스를 유지합니다. 대신 마지막이 필요하면로 들어가는 filter목록을 뒤집고에서 나오는 목록을 뒤집습니다 filter.

output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input[::-1]
    )[::-1]

당신에게 줄 것입니다

[Object(name='fake_name'), Object(name='')]

한 가지 알아야 할 : 임의의 객체와 작업이 순서대로 해당 개체가 제대로 구현해야 __eq__하고 __hash__같은 설명 여기 .


업데이트 :

[o for d in [{}] for o in lst if o.name != "" or d.setdefault("", o) == o]

또는 사용 filterlambda:

flag = {}
filter(lambda o: bool(o.name) or flag.setdefault("", o) == o, lst)

이전 답변

좋습니다. 필터와 람다를 계속 사용하고 있습니까?

이것은 사전 이해력과 함께 제공되는 것이 더 나을 것 같습니다.

{o.name : o for o in input}.values()

저는 파이썬이 람다에서 할당을 허용하지 않는 이유가 이해에서 할당을 허용하지 않는 이유와 비슷하다고 생각합니다. 그리고 그것은 이러한 것들이 C측면에서 평가되어 우리에게 줄 수 있다는 사실 과 관련이 있습니다. 속도 증가. 적어도 그것은 귀도의 에세이 중 하나를 읽은 후의 인상 입니다.

내 생각 엔 이것은 또한 가지고있는 철학에 갈 것입니다 하나의 파이썬에서 어떤 한 가지 일을 올바른 방법.


대신 flag = True가져 오기를 수행 할 수 있다면 이것이 기준을 충족한다고 생각합니다.

>>> from itertools import count
>>> a = ['hello', '', 'world', '', '', '', 'bob']
>>> filter(lambda L, j=count(): L or not next(j), a)
['hello', '', 'world', 'bob']

또는 필터가 다음과 같이 더 잘 작성 될 수 있습니다.

>>> filter(lambda L, blank_count=count(1): L or next(blank_count) == 1, a)

또는 가져 오기가없는 단순한 부울의 경우 :

filter(lambda L, use_blank=iter([True]): L or next(use_blank, False), a)

요약 : 기능적 관용구를 사용할 때는 기능적 코드를 작성하는 것이 좋습니다.

많은 사람들이 지적했듯이 Python에서는 람다 할당이 허용되지 않습니다. 일반적으로 기능적 관용구를 사용할 때 기능적 방식으로 생각하는 것이 좋습니다. 즉, 가능한 한 부작용이나 할당이 없다는 것을 의미합니다.

다음은 람다를 사용하는 기능적 솔루션입니다. fn명확성 위해 람다를 할당했습니다 (조금 길기 때문에).

from operator import add
from itertools import ifilter, ifilterfalse
fn = lambda l, pred: add(list(ifilter(pred, iter(l))), [ifilterfalse(pred, iter(l)).next()])
objs = [Object(name=""), Object(name="fake_name"), Object(name="")]
fn(objs, lambda o: o.name != '')

목록을 변경하는 대신 반복자를 사용하여이 거래를 수행 할 수도 있습니다. 수입품도 약간 다릅니다.

from itertools import chain, islice, ifilter, ifilterfalse
fn = lambda l, pred: chain(ifilter(pred, iter(l)), islice(ifilterfalse(pred, iter(l)), 1))

언제든지 코드를 재구성하여 명령문의 길이를 줄일 수 있습니다.


반복하는 동안 상태를 추적하는 파이썬적인 방법은 생성기를 사용하는 것입니다. itertools 방식은 IMHO를 이해하기가 매우 어렵고이를 위해 람다를 해킹하는 것은 어리석은 일입니다. 나는 시도 할 것이다 :

def keep_last_empty(input):
    last = None
    for item in iter(input):
        if item.name: yield item
        else: last = item
    if last is not None: yield last

output = list(keep_last_empty(input))

전반적으로 가독성은 항상 간결함을 능가합니다.


아니요, 자체 정의로 인해 람다 안에 할당을 넣을 수 없습니다. 함수형 프로그래밍을 사용하여 작업하는 경우 값이 변경되지 않는다고 가정해야합니다.

한 가지 해결책은 다음 코드입니다.

output = lambda l, name: [] if l==[] \
             else [ l[ 0 ] ] + output( l[1:], name ) if l[ 0 ].name == name \
             else output( l[1:], name ) if l[ 0 ].name == "" \
             else [ l[ 0 ] ] + output( l[1:], name )

호출 사이의 상태를 기억하기 위해 람다가 필요한 경우 로컬 네임 스페이스에 선언 된 함수 또는 오버로드 된 __call__. 이제 당신이하려는 일에 대한 나의 모든주의가 방해가 되었으니, 우리는 당신의 질문에 대한 실제 답을 얻을 수 있습니다.

호출 사이에 메모리를 확보하기 위해 람다가 정말로 필요하다면 다음과 같이 정의 할 수 있습니다.

f = lambda o, ns = {"flag":True}: [ns["flag"] or o.name, ns.__setitem__("flag", ns["flag"] and o.name)][0]

그럼 그냥 통과해야 f으로 filter(). 정말로 필요한 경우 flag다음 통해의 가치를 되 찾을 수 있습니다 .

f.__defaults__[0]["flag"]

또는의 결과를 수정하여 전역 네임 스페이스를 수정할 수 있습니다 globals(). 불행히도 결과를 수정 locals()해도 로컬 네임 스페이스에 영향 주지 않는 것과 같은 방식으로 로컬 네임 스페이스를 수정할 수 없습니다 .


bind 함수를 사용하여 의사 다중 문 람다를 사용할 수 있습니다. 그런 다음 플래그에 래퍼 클래스를 사용하여 할당을 활성화 할 수 있습니다.

bind = lambda x, f=(lambda y: y): f(x)

class Flag(object):
    def __init__(self, value):
        self.value = value

    def set(self, value):
        self.value = value
        return value

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
flag = Flag(True)
output = filter(
            lambda o: (
                bind(flag.value, lambda orig_flag_value:
                bind(flag.set(flag.value and bool(o.name)), lambda _:
                bind(orig_flag_value or bool(o.name))))),
            input)

먼저, 당신은 당신의 직업을 위해 지역 할당을 사용할 필요가 없습니다, 위의 대답을 확인하십시오

둘째, locals () 및 globals ()를 사용하여 변수 테이블을 얻은 다음 값을 변경하는 것이 간단합니다.

이 샘플 코드를 확인하십시오.

print [locals().__setitem__('x', 'Hillo :]'), x][-1]

환경에 전역 변수 추가를 변경해야하는 경우 locals ()globals () 로 바꾸십시오.

파이썬의 목록 작성은 멋지지만 대부분의 triditional 프로젝트는 이것을 받아들이지 않습니다 (예 : 플라스크 : [)

도움이 되길 바랍니다

참고 URL : https://stackoverflow.com/questions/6282042/assignment-inside-lambda-expression-in-python

반응형