Opdracht binnen lambda-expressie in Python

Ik heb een lijst van objecten en wil ik verwijder alle objecten die zijn leeg, behalve voor één, met behulp van filter en een lambda expressie.

Bijvoorbeeld als de invoer is:

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

…dan moet de output:

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

Is er een manier om een opdracht toevoegen aan een lambda expressie? Bijvoorbeeld:

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
)
  • Nee. Maar je hoeft dit niet nodig. Eigenlijk denk ik dat het zou een vrij obscure manier om achive dit zelfs als het werkte.
  • Waarom niet gewoon een reguliere oude functie in het filter?
  • Ik wilde het gebruik van lambda gewoon dus het zou echt een compacte oplossing. Ik herinner me in OCaml ik kon ketting print-statements voor de terugkeer van de expressie, dacht dat dit zou kunnen worden gerepliceerd in Python
InformationsquelleAutor Cat | 2011-06-08



12 Replies
  1. 198

    De opdracht expressie operator := toegevoegd in Python 3.8 ondersteunt opdracht aan de binnenkant van de lambda expressies. Deze operator kan alleen worden weergegeven binnen een tekenreeks tussen haakjes (...), tussen haakjes [...], of verstijfd {...} uitdrukking voor syntactische redenen. Bijvoorbeeld, kunnen we het volgende schrijven:

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

    In Python 2, was het mogelijk om het uitvoeren van lokale opdrachten als bijwerking van de lijst comprehensions.

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

    Echter, het is niet mogelijk om een van deze in uw voorbeeld, omdat de variabele flag is in een buitenste bereik, niet de lambda’s bereik. Dit heeft niet te maken met lambda, het is het algemene gedrag in Python 2. Python 3 kunt u dit met de nonlocal trefwoord binnenkant van defs, maar nonlocal kan niet worden gebruikt in lambdas.

    Er is een tijdelijke oplossing (zie hieronder), maar terwijl we op het onderwerp…


    In sommige gevallen kunt u dit alles doen in een 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()])()

    Een cilinder met een straal van 10,0 cm en een hoogte van 20,0 cm en heeft een volume van 6283.2cm3.

    Een cilinder met een straal van 20,0 cm en een hoogte van 40,0 cm en heeft een volume van 50265.5cm3.

    Een cilinder met een straal van 30,0 cm en een hoogte van 60.0 cm en heeft een volume van 169646.0cm3.

    Alsjeblieft niet.


    …terug naar het oorspronkelijke voorbeeld: hoewel je niet kunt uitvoeren van opdrachten aan de flag variabele in de buitenste bereik, u kunt gebruik maken van functies voor het wijzigen van de eerder toegekende waarde.

    Bijvoorbeeld flag kan een object waarvan de .value we gebruik van 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')]

    Als we wilden past in de bovengenoemde thema, kunnen we gebruik maken van een lijst-comprehensie in plaats van setattr:

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

    Maar echt, in ernstige code die u moet altijd gebruik maken van een reguliere functie definitie in plaats van een lambda als je dan gaat doen buitenste opdracht.

    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)
    • Het laatste voorbeeld in dit antwoord levert niet hetzelfde resultaat op als het voorbeeld, maar het lijkt mij als het voorbeeld output is onjuist.
    • in het kort komt dit neer op: gebruik .setattr() en schijn (woordenboeken moeten doen, bijvoorbeeld) te hacken bijwerkingen in functionele code hoe dan ook, koel-code door @JeremyBanks was te zien 🙂
  2. 33

    Je niet echt onderhouden van de status in een filter/lambda expressie (tenzij het misbruik van de global namespace). U kunt echter het bereiken van iets wat vergelijkbaar is met het gecumuleerde resultaat wordt doorgegeven rond in een reduce() uitdrukking:

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

    U kunt, natuurlijk, tweak de conditie een beetje. In dit geval is het filtert duplicaten, maar u kunt ook gebruik maken van a.count(""), bijvoorbeeld om alleen beperken lege tekenreeksen.

    Onnodig te zeggen, kunt u dit doen maar je moet echt niet. 🙂

    Ten slotte, je kan alles doen in Python lambda: http://vanderwijk.info/blog/pure-lambda-calculus-python/

  3. 16

    Er is geen noodzaak voor het gebruik van een lambda, als je het kan verwijderen alle de null-ones, en leg terug als de input verandert:

    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=""))
    • Ik denk dat je een kleine fout in je code. De tweede lijn moet worden output = [x for x in input if x.name].
    • recht, vast, bedankt voor het opmerken
    • Volgorde van de elementen van belang kan zijn.
  4. 11

    Normale opdracht (=) is niet mogelijk binnen een lambda uitdrukking, maar het is mogelijk om het uitvoeren van verschillende trucs met setattr en vrienden.

    Het oplossen van uw probleem, echter, is eigenlijk heel simpel:

    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
        )

    die geeft je

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

    Zoals u kunt zien, is het houden van de eerste blanco exemplaar in plaats van de laatste. Als u de laatste plaats, om de lijst te gaan in filter, en achteruit de lijst komt uit filter:

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

    die geeft je

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

    Één ding bewust te worden van: in volgorde om dit te laten werken met willekeurige objecten, de objecten dienen correct uit te voeren __eq__ en __hash__ zoals uitgelegd hier.

    • de beste oplossing veruit, en precies op het punt van OP
  5. 6

    UPDATE:

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

    of met behulp van filter en lambda:

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

    Vorige Antwoord

    OK, bent u vast op het gebruik van filter en lambda?

    Het lijkt erop dat dit zou beter gediend zijn met een woordenboek begrip,

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

    Ik denk dat de reden dat Python verbiedt opdracht in een lambda is vergelijkbaar met de reden waarom het niet mogelijk een opdracht in een begrip en dat heeft iets te maken met het feit dat deze dingen worden geëvalueerd op de C kant en dus kan een toename van de snelheid. Althans dat is mijn indruk na het lezen van een van Guido ‘ s essays.

    Mijn gok is dat dit zou ook indruisen tegen de filosofie van het hebben van een juiste manier van doen een ding in Python.

    • Dus dit is niet volledig juist. Het zal niet toelaten, noch zal het behouden duplicaten van niet-lege-snarige objecten.
    • zie ook mijn updates
    • De filosofie is Een voor de hand liggende Manier, niet één goede manier is.
  6. 5

    Als in plaats van flag = True kunnen we doen een import in plaats daarvan, dan denk ik dat deze voldoet aan de criteria:

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

    Of misschien de filter is beter geschreven als:

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

    Of gewoon voor een eenvoudige boolean, zonder enige invoer:

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

    TL;DR: Bij gebruik van functionele talen is het beter om te schrijven functionele code

    Als veel mensen hebben er op gewezen, in Python lambdas opdracht is niet toegestaan. In het algemeen bij gebruik van functionele idioom je beter denken in een functionele manier wat betekent dat waar mogelijk geen bijwerkingen en geen opdrachten.

    Hier is een functionele oplossing die gebruik maakt van een lambda. Ik heb toegewezen aan de lambda te fn voor de duidelijkheid (en omdat ik een beetje lang-ish).

    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 != '')

    U kunt ook maak deze deal met iterators eerder dan de lijsten door het veranderen van dingen een beetje rond. Je hebt ook een aantal andere invoer.

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

    Kunt u altijd reoganize de code voor het verminderen van de lengte van de jaarrekening.

  8. 5

    De pythonic manier om bij te houden staat tijdens de iteratie is met generatoren. De itertools manier is het heel moeilijk om te begrijpen IMHO en proberen te hacken lambdas om dit te doen is gewoon dom. Ik zou proberen:

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

    Algemene, de leesbaarheid troeven compactheid elke keer.

  9. 3

    Nee, u kunt een opdracht binnen een lambda vanwege zijn eigen definitie. Als u werkt met functioneel programmeren, dan moet u ervan uitgaan dat uw waarden zijn niet veranderlijk.

    Een oplossing zou zijn de volgende code:

    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 )
  10. 3

    Als u behoefte aan een lambda om te onthouden tussen de status van gesprekken, ik zou aanraden een functie gedeclareerd in de lokale naamruimte of een klasse met een overbelaste __call__. Nu dat al mijn waarschuwingen tegen wat je probeert te doen is uit de weg, we kunnen krijgen om een effectief antwoord op uw vraag.

    Als je echt nodig hebt om uw lambda om wat geheugen vrij te maken tussen de oproepen die u kunt definiëren, zoals:

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

    Dan hoeft u alleen maar te geven f te filter(). Als je echt moet, kan je de waarde van flag met het volgende:

    f.__defaults__[0]["flag"]

    Als alternatief kunt u de global namespace door het wijzigen van het resultaat van globals(). Helaas is het niet wijzigen van de lokale naamruimte op dezelfde manier als het wijzigen van het resultaat van locals() heeft geen invloed op de lokale naamruimte.

    • Of gebruik gewoon de originele Lisp: (let ((var 42)) (lambda () (setf var 43))).
  11. 3

    Kunt u gebruik maken van een binding functie voor het gebruik van een pseudo-multi-verklaring van lambda. Dan kunt u gebruik maken van een wrapper-klasse voor een Vlag in te schakelen opdracht.

    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)
  12. 0

    eerste , je hoeft niet gebruik te maken van een lokale type opdracht voor je werk, check gewoon de bovenstaande antwoord

    tweede, het eenvoudige gebruik van de lokale bevolking() en globals() om de variabelen tabel en verander dan de waarde

    check dit voorbeeld code:

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

    als je nodig hebt om het toevoegen van een globale variabele voor je omgeving, proberen te vervangen de lokale bevolking() met globals()

    python ‘ s lijst comp is cool, maar de meeste van de triditional project niet accepteren deze(zoals kolf :[)

    hoop dat het zou helpen

    • U kunt geen gebruik locals(), wordt dit nadrukkelijk zegt in de documentatie van dat het veranderen van het niet veranderen, de lokale scope (of op zijn minst niet altijd). globals() aan de andere kant werkt zoals verwacht.
    • probeer het gewoon, dont follow the document blindelings. en de opdracht in lambda het breken van de regel al
    • Helaas werkt alleen in de globale naamruimte, in welk geval u moet echt met globals(). pastebin.com/5Bjz1mR4 (getest in zowel 2.6 en 3.2) bewijst het.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *