在Python中使用lambda表达式进行赋值

Cat*_*Cat 94 python lambda expression variable-assignment

我有一个对象列表,我想删除所有空的对象,除了一个,使用filterlambda表达式.

例如,如果输入是:

[Object(name=""), Object(name="fake_name"), Object(name="")]
Run Code Online (Sandbox Code Playgroud)

...那么输出应该是:

[Object(name=""), Object(name="fake_name")]
Run Code Online (Sandbox Code Playgroud)

有没有办法为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
)
Run Code Online (Sandbox Code Playgroud)

小智 201

:=2019年末在Python 3.8中使用的新赋值表达式运算符将支持lambda表达式内的赋值.(出于语法原因,此运算符只能出现在带括号(...),括号[...]或括号的{...}表达式中.)例如,我们将能够编写以下内容:

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

在Python 2中,可以执行本地分配作为列表推导的副作用.

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

但是,在您的示例中不可能使用其中任何一个,因为您的变量flag位于外部作用域,而不是lambda作用域.这与lambdaPython 2中的一般行为没有关系.Python 3允许您使用s中的nonlocal关键字来解决这个问题def,但nonlocal不能在lambdas中使用.

有一个解决方法(见下文),但我们正在谈论这个话题......


在某些情况下,您可以使用它来执行以下操作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()])()
Run Code Online (Sandbox Code Playgroud)

半径为10.0厘米,高度为20.0厘米的圆柱体积为6283.2立方厘米.
半径20.0cm,高40.0cm的圆柱体积为50265.5cm³.
半径为30.0cm,高度为60.0cm的圆柱体积为169646.0cm³.

请不要.


...回到原始示例:虽然您无法flag对外部作用域中的变量执行赋值,但您可以使用函数来修改先前指定的值.

例如,flag可以是.value我们使用setattr以下设置的对象:

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)
Run Code Online (Sandbox Code Playgroud)
[Object(name=''), Object(name='fake_name')]
Run Code Online (Sandbox Code Playgroud)

如果我们想要符合上述主题,我们可以使用列表理解而不是setattr:

    [None for flag.value in [bool(o.name)]]
Run Code Online (Sandbox Code Playgroud)

但实际上,在严格的代码中,lambda如果您要进行外部赋值,则应始终使用常规函数定义而不是a .

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)
Run Code Online (Sandbox Code Playgroud)


Ivo*_*ijk 35

你不能真正维护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']
>>> 
Run Code Online (Sandbox Code Playgroud)

当然,你可以稍微调整一下这个条件.在这种情况下,它会过滤掉重复项,但您也可以使用a.count(""),例如,仅限制空字符串.

不用说,你可以做到这一点,但你真的不应该这样做.:)

最后,你可以用纯Python做任何事情lambda:http://vanderwijk.info/blog/pure-lambda-calculus-python/


Gab*_*aru 17

当你可以删除所有 null值时,不需要使用lambda,如果输入大小改变,则不需要使用lambda :

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=""))
Run Code Online (Sandbox Code Playgroud)


Eth*_*man 13

尽管可以和朋友一起执行各种技巧,但是=lambda表达式中不能进行正常赋值()setattr.

但是,解决您的问题实际上非常简单:

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
    )
Run Code Online (Sandbox Code Playgroud)

哪个会给你

[Object(Object(name=''), name='fake_name')]
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它保留了第一个空白实例而不是最后一个实例.如果你需要最后一个,反转进入的列表filter,并反转出来的列表filter:

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

哪个会给你

[Object(name='fake_name'), Object(name='')]
Run Code Online (Sandbox Code Playgroud)

需要注意的一件事是:为了使其与任意对象一起工作,这些对象必须正确实现,__eq____hash__此处说明.


mil*_*man 7

更新:

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

或使用filterlambda:

flag = {}
filter(lambda o: bool(o.name) or flag.setdefault("", o) == o, lst)
Run Code Online (Sandbox Code Playgroud)

上一个答案

好吧,你坚持使用过滤器和lambda?

用字典理解这似乎会更好.

{o.name : o for o in input}.values()
Run Code Online (Sandbox Code Playgroud)

我认为Python不允许在lambda中进行赋值的原因类似于它不允许在理解中进行赋值的原因,并且这与这些事物在C侧面被评估的事实有关,因此可以给我们一个提高速度.至少这是我在阅读Guido的一篇文章后的印象.

我的猜测是,这也违背了在Python中用一种正确的方法做任何事情的哲学.


die*_*dha 6

TL; DR:使用功能惯用法时,最好编写功能代码

正如许多人指出的那样,在Python中不允许进行lambda赋值。通常,在使用功能性习语时,您最好以一种功能性的方式进行思考,这意味着在没有可能的情况下,也没有副作用。

这是使用lambda的功能解决方案。fn为了清晰起见,我将lambda分配给了它(因为它有点长)。

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 != '')
Run Code Online (Sandbox Code Playgroud)

您还可以通过迭代一些事情来处理迭代器而不是列表。您也有一些不同的进口。

from itertools import chain, islice, ifilter, ifilterfalse
fn = lambda l, pred: chain(ifilter(pred, iter(l)), islice(ifilterfalse(pred, iter(l)), 1))
Run Code Online (Sandbox Code Playgroud)

您始终可以重新组织代码以减少语句的长度。


Jon*_*nts 6

如果不是flag = True我们可以进行导入,那么我认为这符合标准:

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

或者可能更好地编写过滤器:

>>> filter(lambda L, blank_count=count(1): L or next(blank_count) == 1, a)
Run Code Online (Sandbox Code Playgroud)

或者,只是一个简单的布尔值,没有任何导入:

filter(lambda L, use_blank=iter([True]): L or next(use_blank, False), a)
Run Code Online (Sandbox Code Playgroud)


小智 6

在迭代期间跟踪状态的pythonic方法是使用生成器.itertools方式很难理解恕我直言,并试图破解lambdas这样做是非常愚蠢的.我试试:

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))
Run Code Online (Sandbox Code Playgroud)

总的来说,可读性每次都胜过紧凑.


Bal*_*arq 5

不,您不能将赋值放入 lambda 中,因为它有自己的定义。如果您使用函数式编程,那么您必须假设您的值是不可变的。

一种解决方案是以下代码:

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 )
Run Code Online (Sandbox Code Playgroud)


JPv*_*rwe 5

如果您需要 lambda 来记住调用之间的状态,我会推荐在本地命名空间中声明的函数或具有重载的__call__. 现在我对您正在尝试做的事情的所有警告都已消除,我们可以得到您查询的实际答案。

如果您确实需要让 lambda 在调用之间拥有一些内存,您可以将其定义为:

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

然后你只需要传递ffilter(). flag如果您确实需要,您可以通过以下方式取回 的值:

f.__defaults__[0]["flag"]
Run Code Online (Sandbox Code Playgroud)

或者,您可以通过修改 的结果来修改全局命名空间globals()。不幸的是,您无法像修改结果locals()不影响本地命名空间那样修改本地命名空间。


pyr*_*ade 5

您可以使用绑定函数来使用伪多语句 lambda。然后,您可以使用 Flag 的包装类来启用分配。

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)
Run Code Online (Sandbox Code Playgroud)