पाइथन में लैम्ब्डा अभिव्यक्ति के अंदर असाइनमेंट


105

मेरे पास वस्तुओं की एक सूची है और मैं उन सभी वस्तुओं को हटाना चाहता हूं जो एक, उपयोग filterऔर एक lambdaअभिव्यक्ति को छोड़कर खाली हैं ।

उदाहरण के लिए यदि इनपुट है:

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

1
नहीं, लेकिन आपको इसकी आवश्यकता नहीं है। वास्तव में मुझे लगता है कि यह काम करने के लिए एक बहुत ही अस्पष्ट तरीका होगा, भले ही यह काम करे।

8
क्यों न केवल एक पुराने पुराने फंक्शन को फिल्टर में पास किया जाए?
dfb

5
मैं लैम्ब्डा का उपयोग करना चाहता था इसलिए यह वास्तव में एक कॉम्पैक्ट समाधान होगा। मुझे याद है कि OCaml में मैं रिटर्न एक्सप्रेशन से पहले स्टेटमेंट प्रिंट कर सकता था, सोचा कि इसे पायथन
कैट

एक जंजीर पिपिलीन विकसित करने के प्रवाह में होना काफी दर्दनाक है, फिर एहसास करें: "ओह, मैं प्रवाह को और अधिक स्पष्ट बनाने के लिए एक अस्थायी संस्करण बनाना चाहता हूं" या "मैं इस मध्यवर्ती कदम को लॉग इन करना चाहता हूं": और फिर आपको कूदना होगा इसे करने के लिए एक फ़ंक्शन बनाने के लिए कहीं और: और उस फ़ंक्शन को नाम दें और उसका ट्रैक रखें - भले ही इसका उपयोग केवल एक स्थान पर किया गया हो।
जावदबा

जवाबों:


215

:=पायथन 3.8 में जोड़ा गया असाइनमेंट एक्सप्रेशन ऑपरेटर लैम्ब्डा एक्सप्रेशन के अंदर असाइनमेंट का समर्थन करता है। यह ऑपरेटर सिंटिक कारणों के लिए केवल एक कोष्ठक (...), कोष्ठक [...]या लट में {...}अभिव्यक्ति के भीतर प्रकट हो सकता है। उदाहरण के लिए, हम निम्नलिखित लिख सकेंगे:

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

पायथन 2 में, सूची बोध के साइड इफेक्ट के रूप में स्थानीय असाइनमेंट करना संभव था।

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

हालाँकि, आपके उदाहरण में इनमें से किसी एक का उपयोग करना संभव नहीं है क्योंकि आपका चर flagएक बाहरी दायरे में है, न कि lambdaगुंजाइश। इसका कोई मतलब नहीं है lambda, यह Python 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()])()

10.0 सेमी की त्रिज्या और 20.0 सेमी की ऊंचाई वाले सिलेंडर में 6283.2 सेमी³ की मात्रा होती है।
20.0 सेमी की त्रिज्या और 40.0 सेमी की ऊंचाई वाले सिलेंडर में 50265.5 सेमी³ की मात्रा होती है।
30.0 सेमी की त्रिज्या और 60.0 सेमी की ऊंचाई वाले सिलेंडर में 169646.0 सेमी³ की मात्रा होती है।

कृपया नहीं।


... अपने मूल उदाहरण पर वापस: यद्यपि आप 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)
[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)

इस उत्तर में अंतिम उदाहरण उदाहरण के समान आउटपुट का उत्पादन नहीं करता है, लेकिन यह मुझे लगता है कि उदाहरण आउटपुट गलत है।
जेरेमी

संक्षेप में, यह करने पर निर्भर करता: उपयोग .setattr()और प्रतिमूर्तियाँ ( शब्दकोशों उदाहरण के लिए के रूप में अच्छी करना चाहिए,) वैसे भी कार्यात्मक कोड में साइड इफेक्ट को हैक करने, @JeremyBanks द्वारा शांत कोड दिखाया गया था :)
JNO

नोट पर Thx के लिए assignment operator!
जावदबा

37

आप वास्तव में 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("")उदाहरण के लिए, केवल खाली तारों को प्रतिबंधित करने के लिए भी उपयोग कर सकते हैं।

कहने की जरूरत नहीं है, आप ऐसा कर सकते हैं लेकिन आपको वास्तव में ऐसा नहीं करना चाहिए। :)

अंत में, आप शुद्ध पायथन में कुछ भी कर सकते हैं lambda: http://vanderwijk.info/blog/pure-lambda-calculus-python/


17

लैंबडा का उपयोग करने की कोई आवश्यकता नहीं है, जब आप सभी अशक्त लोगों को हटा सकते हैं, और इनपुट आकार में परिवर्तन होने पर एक वापस रख सकते हैं:

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

1
मुझे लगता है कि आपके कोड में एक छोटी सी गलती है। दूसरी लाइन होनी चाहिए output = [x for x in input if x.name]
हैलेक्स

तत्वों का क्रम महत्वपूर्ण हो सकता है।
MAnyKey

15

सामान्य असाइनमेंट ( =) एक 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
    )

जो आपको देगा

[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__जैसा कि यहां बताया गया है


7

अद्यतन :

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

या उपयोग filterऔर lambda:

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

पिछला उत्तर

ठीक है, क्या आप फ़िल्टर और लैम्ब्डा का उपयोग करने पर अटक गए हैं?

ऐसा लगता है कि यह एक शब्दकोश समझ के साथ बेहतर परोसा जाएगा,

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

मुझे लगता है कि यह कारण है कि पाइथन एक मेमने में असाइनमेंट की अनुमति नहीं देता है, यह इसी तरह है कि यह एक समझ में असाइनमेंट की अनुमति नहीं देता है और इस तथ्य के साथ कुछ करना है कि इन चीजों का मूल्यांकन Cपक्ष में किया जाता है और इस तरह हमें एक मौका दे सकता है गति में वृद्धि। गुइडो के एक निबंध को पढ़ने के बाद कम से कम मेरी धारणा यही है

मेरा अनुमान है कि यह भी पायथन में किसी एक चीज को करने का एक सही तरीका होने के दर्शन के खिलाफ होगा ।


तो यह पूरी तरह से सही नहीं है। यह आदेश को संरक्षित नहीं करेगा, न ही यह गैर-रिक्त-स्ट्रिंग ऑब्जेक्ट्स के डुप्लिकेट को संरक्षित करेगा।
JPvdMerwe

7

TL; DR: कार्यात्मक मुहावरों का उपयोग करते समय कार्यात्मक कोड लिखना बेहतर होता है

जैसा कि कई लोगों ने बताया है, पायथन लैम्ब्डा में असाइनमेंट की अनुमति नहीं है। सामान्य तौर पर जब कार्यात्मक मुहावरों का उपयोग करते हैं, तो एक कार्यात्मक तरीके से अपनी बेहतर सोच को रोकें, जिसका अर्थ है कि जहां भी संभव हो कोई साइड इफेक्ट और कोई असाइनमेंट न हो।

यहाँ कार्यात्मक समाधान है जो एक लैम्ब्डा का उपयोग करता है। मैंने 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))

आप हमेशा बयानों की लंबाई कम करने के लिए कोड को पुन: व्यवस्थित कर सकते हैं।


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']

या शायद फिल्टर के रूप में बेहतर लिखा है:

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

6

पुनरावृत्ति के दौरान राज्य को ट्रैक करने का पायथोनिक तरीका जनरेटर के साथ है। IMert को समझने के लिए itertools का तरीका काफी कठिन है और ऐसा करने के लिए लैम्ब्डा को हैक करने की कोशिश करना सादा मूर्खतापूर्ण है। मैं कोशिश करूँगा:

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

कुल मिलाकर, पठनीयता हर बार कॉम्पैक्टनेस को ट्रम्प करती है।


4

नहीं, आप अपनी परिभाषा के कारण लंबोदर के अंदर असाइनमेंट नहीं डाल सकते। यदि आप कार्यात्मक प्रोग्रामिंग का उपयोग करके काम करते हैं, तो आपको यह मान लेना चाहिए कि आपके मूल्य परस्पर नहीं हैं।

एक समाधान निम्नलिखित कोड होगा:

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 )

4

यदि आपको कॉल के बीच स्थिति को याद रखने के लिए एक लैम्ब्डा की आवश्यकता है, तो मैं या तो स्थानीय नेमस्पेस में घोषित एक फ़ंक्शन या ओवरलोड वाले एक वर्ग की सिफारिश करूंगा __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()नहीं कर सकते हैं जैसे कि स्थानीय नाम स्थान को प्रभावित नहीं करता है।


या बस मूल लिस्प का उपयोग करें (let ((var 42)) (lambda () (setf var 43))):।
काज

4

आप एक छद्म बहु-स्टेटमेंट लैम्बडा का उपयोग करने के लिए एक बाइंड फ़ंक्शन का उपयोग कर सकते हैं। तब आप असाइनमेंट को सक्षम करने के लिए फ़्लैग के लिए एक आवरण वर्ग का उपयोग कर सकते हैं।

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)

0

एक गंदे वर्कअराउंड की तरह, लेकिन लैम्ब्डा में असाइनमेंट वैसे भी अवैध है, इसलिए यह वास्तव में मायने नहीं रखता है। आप लैम्बडा के exec()अंदर से असाइनमेंट को चलाने के लिए बिलिन फ़ंक्शन का उपयोग कर सकते हैं , जैसे कि यह उदाहरण:

>>> val
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    val
NameError: name 'val' is not defined
>>> d = lambda: exec('val=True', globals())
>>> d()
>>> val
True

-2

सबसे पहले, आपको अपनी नौकरी के लिए एक स्थानीय एसिगमेंट का उपयोग करने की आवश्यकता नहीं है, बस उपरोक्त उत्तर की जांच करें

दूसरा, चर तालिका प्राप्त करने के लिए स्थानीय () और ग्लोबल्स () का उपयोग करने के लिए इसका सरल और फिर मूल्य बदल जाता है

इस नमूना कोड की जाँच करें:

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

अगर आपको अपने वैरिएबल में वैश्विक वैरिएबल को बदलने की आवश्यकता है, तो स्थानीय लोगों () को ग्लोबल्स () से बदलने की कोशिश करें।

अजगर की सूची COMP शांत है, लेकिन अधिकांश त्रिदोषजनक परियोजना इसे स्वीकार नहीं करती है (जैसे फ्लास्क: [)

आशा है कि यह मदद कर सकता है


2
आप उपयोग नहीं कर सकते locals(), यह स्पष्ट रूप से प्रलेखन में कहता है कि इसे बदलने से वास्तव में स्थानीय गुंजाइश नहीं बदलती (या कम से कम यह हमेशा नहीं होगा)। globals()दूसरी ओर उम्मीद के मुताबिक काम करता है।
JPvdMerwe

@JPvdMerwe बस इसे आज़माएं, दस्तावेज़ का आँख बंद करके पालन न करें। और लैम्ब्डा में असाइनमेंट पहले से ही नियम तोड़ रहा है
jyf1987

3
यह दुर्भाग्य से केवल वैश्विक नामस्थान में काम करता है, जिस स्थिति में आपको वास्तव में उपयोग करना चाहिए globals()pastebin.com/5Bjz1mR4 (2.6 और 3.2 दोनों में परीक्षण किया गया) इसे साबित करता है।
JPvdMerwe
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.