Python中lambdaexpression式的赋值

我有一个对象的列表,我想删除除了一个空的所有对象,使用filterlambdaexpression式。

例如,如果input是:

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

…那么输出应该是:

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

有没有办法给lambdaexpression式添加一个赋值? 例如:

 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 2中执行本地分配,作为列表parsing的副作用。

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

然而,在你的例子中不可能使用这个variables,因为你的variablesflag位于外部范围,而不是lambda的范围。 这与lambda无关,这是Python 2中的一般行为.Python 3让你可以使用nonlocal关键字来解决这个问题,但nonlocal不能在lambda里面使用,而Python 3会移除这个方面列表parsing的效果,所以这在Python 3中是不可能的。

有一个解决方法(见下文),但是当我们在这个话题上…


在某些情况下,您可以使用它来完成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.0厘米,高度为20.0厘米的圆柱体积为6283.2立方厘米。
一个半径为20.0厘米,高度为40.0厘米的圆柱体积为50265.5立方厘米。
半径30.0厘米,高60.0厘米的圆柱体积为169646.0立方厘米。

请不要。


…回到你原来的例子:虽然你不能在外部范围内对flagvariables进行赋值,但是你可以使用函数来修改以前赋值的值。

例如, 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) 
 [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 / lambdaexpression式中的状态(除非滥用全局名称空间)。 但是,您可以使用reduce()expression式中传递的累积结果来获得类似的结果:

 >>> 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("")来限制空白string。

不用说,你可以做到这一点,但你真的不应该这样做。 🙂

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

没有必要使用lambdaexpression式,当你可以删除所有的空字符时,如果input大小发生变化,就把它放回去:

 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="")) 

更新

 [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) 

以前的答案

好的,你坚持使用filter和lambda?

这似乎是更好的字典理解服务,

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

我认为Python不允许在lambda中赋值的原因类似于为什么它不允许在理解中赋值,这与这些事情在C端被评估并因此可以给我们速度增加。 读完Guido的散文后,至less这是我的印象。

我的猜测是,这也违背了Python中做任何事情的正确方法。

普通赋值( = )在lambdaexpression式中是不可能的,尽pipe可以用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 ) 

这会给你

 [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__ 这里解释。

如果不是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写得更好:

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

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

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

迭代过程中跟踪状态的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)) 

总的来说,可读性胜过每一次紧凑。

如果你需要一个lambda来记住调用之间的状态,我会推荐一个在本地命名空间中声明的函数或者一个带有重载__call__的类。 现在,我对所做的所有事情的注意事项都已经排除在外,我们可以为您的查询find实际的答案。

如果你真的需要让你的lambda在调用之间有一些内存,你可以像这样定义它:

 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()的结果一样修改本地命名空间,不会影响本地命名空间。

您可以使用绑定函数来使用伪多语句lambda。 然后你可以使用一个包装类的标志来启用分配。

 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) 

TL; DR:当使用function性语言时,最好编写function性代码

正如很多人所指出的那样,在Python中lambdaexpression式是不允许的。 一般来说,当使用function性习语时,您最好以function性的方式思考,这意味着在任何可能的情况下都没有副作用,也没有任务。

这是使用lambda的function解决scheme。 为了清晰起见,我已经将lambda分配给了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 != '') 

你也可以用迭代器而不是列表来处理这个问题。 你也有一些不同的import。

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

您可以随时对代码进行reoganize以减less语句的长度。

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

一个解决scheme是以下代码:

 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 ) 

首先,你不需要使用当地的工作分配,只需检查上面的答案

其次,它使用简单的locals()和globals()来获取variables表,然后改变它的值

检查这个示例代码:

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

如果你需要改变添加一个全局variables到你的环境,尝试用globals()replacelocals ()

python的列表比较酷,但大部分的三项目不接受这个(像瓶:[)

希望它可以帮助