“Det mindste Forundret”, og Foranderlig Standard Argument

Nogen fifle med Python længe nok er blevet bidt (eller revet i stykker) af følgende spørgsmål:

def foo(a=[]):
    a.append(5)
    return a

Python for nybegyndere ville forvente, at denne funktion altid returnere en liste med kun ét element: [5]. Resultatet er i stedet meget forskellige, og meget overraskende (for en novice):

>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()

En leder af mine venner engang havde sit første møde med denne funktion, og kaldte det “et dramatisk design-fejl” i sproget. Jeg svarede, at den opførsel var en underliggende forklaring, og det er faktisk meget forvirrende og uventet, hvis du ikke forstår det indvendige. Men jeg var ikke i stand til at besvare (til mig selv) følgende spørgsmål: hvad er grunden til, bindende standard argument i funktionen definitionen, og ikke på funktion udførelse? Jeg tvivler på, at den erfarne adfærd har en praktisk anvendelse (som virkelig har brugt statiske variabler i C, uden avl fejl?)

Redigere:

Baczek lavet et interessant eksempel. Sammen med de fleste af dine kommentarer og Utaal s i særdeleshed, vil jeg yderligere uddybet:

>>> def a():
...     print("a executed")
...     return []
... 
>>>            
>>> def b(x=a()):
...     x.append(5)
...     print(x)
... 
a executed
>>> b()
[5]
>>> b()
[5, 5]

Til mig, det ser ud til, at design afgørelse var i forhold til, hvor til at sætte omfanget af parametre: inde i funktionen eller “sammen” med det?

At gøre den bindende inde i den funktion, der ville betyde, at x er effektivt bundet til den angivne standard, når funktionen kaldes, er ikke defineret noget, der ville præsentere en dyb fejl: def linje vil være “hybrid” i den forstand, at en del af de bindende (for funktionen objekt) der ville ske ved definition, og en del (tildeling af standard parametre) på funktion aktivering tid.

Den faktiske adfærd er mere konsekvent: alt for, at line bliver evalueret, når denne linje er udført, hvilket betyder at der på funktion definition.

Supplerende spørgsmål – Gode anvendelsesmuligheder for foranderlig standard argumenter
Jeg er ikke i tvivl om, foranderlig argumenter, der overtræder mindst forbavselse princippet for en gennemsnitlig person, og jeg har set begyndere at træde der, så heroisk udskiftning af postlister med mailing elementer. Ikke desto mindre foranderlig argumenter er stadig i overensstemmelse med Python Zen (Pep 20) og falder i “indlysende for hollandsk” (forstået/udnyttes af hard core python-programmører) bestemmelse. Den anbefalede løsning med doc streng er den bedste, men alligevel modstand til doc strygere og enhver (skrevet) docs er ikke så ualmindeligt i dag. Personligt ville jeg foretrække en dekoratør (siger @fixed_defaults).
Mit argument om, når jeg kommer på tværs af dette er: “Hvorfor har du brug for at oprette en funktion, der returnerer en foranderlig, der kunne eventuelt være en foranderlig du vil gå til den funktion? Enten det ændrer en foranderlig eller opretter en ny. Hvorfor har du brug for at gøre begge dele med én funktion? Og hvorfor skal tolken være omskrevet til at tillade dig at gøre det uden at tilføje tre linjer til din kode?” Fordi vi taler om at omskrive den måde, at tolken håndterer funktion definitioner og en antydning her. Der er en masse at gøre for en knap, der er nødvendige use case.
“Python for nybegyndere ville forvente, at denne funktion altid returnere en liste med kun ét element: [5].” Jeg er en Python-novice, og jeg ville ikke forvente dette, fordi selvfølgelig foo([1]) vil vende tilbage [1, 5], ikke [5]. Hvad du mente med at sige, at en novice ville forvente, at den funktion kaldt uden en parameter jeg vil altid vende tilbage [5].
eksempel i Python tutorial, hvorfor er if L is None: behov for? Jeg har fjernet denne test, og det gjorde ingen forskel

OriginalForfatteren Stefano Borini | 2009-07-15

30 svar

  1. 1407

    Faktisk, dette er ikke en design fejl, og det er ikke på grund af indvendige dele, eller ydeevne.

    Det kommer simpelthen fra det faktum, at funktioner i Python er første klasses objekter, og ikke kun et stykke kode.

    Så snart du kommer til at tænke ind i denne måde, så er det helt fornuftigt: en funktion er et objekt, der bliver evalueret på sin definition; standard parametre er en form for “medlem data”, og derfor, at deres tilstand kan ændre sig fra en opfordring til andre – præcis som i ethvert andet objekt.

    I alle tilfælde, Effbot har en meget flot forklaring af årsagerne til denne adfærd i Default Parameter-Værdier i Python.

    Jeg fandt det meget klart, og jeg kan virkelig foreslår, at du læser det for en bedre viden om, hvordan funktionen objekter arbejde.

    Til alle, der har læst ovenstående svar, jeg stærkt anbefaler, at du tager dig tid til at læse igennem knyttet Effbot artikel. Samt alle andre nyttige oplysninger, den del om, hvordan dette sprog, og denne funktion kan bruges til resultatet caching/memoisation er meget praktisk at vide!
    Selv hvis det er en første-klasse objekt, kan man stadig forestille sig et design, hvor – kode for hver standard værdi, der er gemt sammen med objektet og re-evalueret hver gang funktionen kaldes. Jeg siger ikke, at det ville være bedre, bare at funktioner bliver first-class-objekter, der ikke fuldt ud er til hinder for det.
    Undskyld, men noget, der betragtes som “Den største WTF i Python” absolut en design fejl. Dette er en kilde til fejl til alle på et tidspunkt, da ingen forventer, at en adfærd i starten – hvilket betyder, at det ikke burde have været designet på den måde til at begynde med. Jeg er ligeglad med, hvad hoops de var nødt til at springe gennem, de skal har designet Python, så at standard argumenterne er ikke-statisk.
    Hvorvidt det er en design fejl, dit svar synes at antyde, at denne adfærd er en måde, der er nødvendige, naturlige og indlysende i betragtning af, at funktioner er første klasses objekter, og der er simpelthen ikke tilfældet. Python har lukninger. Hvis du erstatter standard argument med en opgave på den første linje af funktionen, er det udtrykket evalueres hvert opkald (eventuelt ved hjælp af navne, der er anmeldt i en vedlagt anvendelsesområde). Der er ingen grund til, at det ikke ville være muligt eller rimeligt at have standard argumenter evalueret hver gang funktionen kaldes på nøjagtig samme måde.
    Design som ikke direkte følger af functions are objects. I din paradigme, forslag ville være at gennemføre funktioner’ default-værdier, da egenskaber, snarere end egenskaber.

    OriginalForfatteren rob

  2. 238

    Antag at du har følgende kode

    fruits = ("apples", "bananas", "loganberries")
    
    def eat(food=fruits):
        ...

    Når jeg ser den erklæring, spise, mindst forbløffende ting er at tænke, at hvis den første parameter er ikke givet, at det vil være lig med den tupel ("apples", "bananas", "loganberries")

    Men skulle senere i den kode, jeg gør noget, som

    def some_random_function():
        global fruits
        fruits = ("blueberries", "mangos")

    så hvis standard parametre blev bundet på funktion udførelse snarere end funktion erklæring, så ville jeg blive forbavset over (i en meget dårlig måde) at opdage, at frugt var blevet ændret. Dette ville være mere forbløffende IMO end at opdage, at din foo funktion ovenfor blev muterer listen.

    Det virkelige problem ligger med foranderlig variabler, og alle sprog har dette problem til en vis grad. Her er et spørgsmål: antag, at i Java, som jeg har følgende kode:

    StringBuffer s = new StringBuffer("Hello World!");
    Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
    counts.put(s, 5);
    s.append("!!!!");
    System.out.println( counts.get(s) );  //does this work?

    Nu, er mit kort, skal du bruge værdien af den StringBuffer nøglen, når det blev lagt ind på kortet, eller gør det, skal du gemme nøglen som reference? Enten måde, en person er forbavset; enten den person, der forsøgte at få objektet ud af Map ved hjælp af en værdi svarende til den, man sætter det i med, eller den person, der ikke synes de kan hente deres objekt, selvom nøglen, de bruger, er bogstaveligt talt det samme objekt, som blev brugt til at sætte det ind på kortet (det er faktisk derfor, Python tillader ikke sin foranderlig indbyggede datatyper, der skal bruges som ordbog nøgler).

    Dit eksempel er en god en på en sag, hvor Python for nybegyndere vil blive overrasket og bidt. Men jeg vil argumentere for, at hvis vi “faste” dette, så er der kun ville skabe en anden situation, hvor de ville blive bidt i stedet, og at man ville være endnu mindre intuitiv. Desuden er, dette er altid tilfældet, når der beskæftiger sig med foranderlig variabler, du altid løbe ind i de tilfælde, hvor en person kunne intuitivt forventer, at den ene eller den modsatte opførsel, afhængigt af, hvilken kode de skriver.

    Jeg personligt gerne Python ‘ s nuværende fremgangsmåde: standard funktion argumenter er evalueret, når funktionen er defineret, og at objektet er altid standard. Jeg formoder, at de kunne særlige tilfælde ved hjælp af en tom liste, men den slags særlige kabinet vil medføre endnu mere forbavset, for ikke at nævne være bagud kompatible.

    Jeg tror, det er et spørgsmål om forhandling. At du handler på en global variabel. Enhver evaluering udføres hvor som helst i din kode, der involverer din globale variabel vil nu (korrekt) henviser til (“blåbær”, “mango”). standard parameter kunne bare være ligesom alle andre tilfælde.
    Faktisk, jeg tror ikke, at jeg er enig i dit første eksempel. Jeg er ikke sikker på, jeg kan godt lide tanken om at ændre en startværdi som det i første omgang, men hvis jeg gjorde, ville jeg forvente, at det at opføre sig præcis, som du beskriver — at ændre standardværdien til ("blueberries", "mangos").
    Standard parameter er som enhver anden sag. Hvad er uventet, er, at parameteren er en global variabel, og ikke en lokal. Som igen er fordi den kode, der er udført på funktion definition, ikke ringe. Når du får den, og at det samme gælder for klasser, det er helt klart.
    Jeg kan finde de eksempel vildledende snarere end genial. Hvis some_random_function() føjer til fruits i stedet for at tildele det, adfærd eat() vil skift. Så meget for den nuværende vidunderlig design. Hvis du bruger et standard argument, der er opført andre steder, og så ændre henvisningen fra uden for den funktion, du beder om problemer. Den virkelige WTF er, når mennesker definerer en frisk standard argument (en liste bogstavelig eller et opkald til en constructor), og stadig jeg får lidt.
    Du skal bare udtrykkeligt er erklæret global og overført tupel – der er absolut intet overraskende, hvis eat virker forskelligt efter det.

    OriginalForfatteren Eli Courtwright

  3. 205

    AFAICS ingen har endnu udgivet den relevante del af dokumentation:

    Default parameter-værdier er beregnet, når den funktion, definition, der er udført. Dette betyder, at de udtryk, der evalueres én gang, når funktionen er defineret, og at den samme “pre-beregnes” value er brugt for hvert opkald. Dette er især vigtigt at forstå, når en standard parameter er en foranderlig objekt, såsom en liste eller i en ordbog: hvis funktionen ændrer objektet (fx ved at tilføje et element til en liste), standardværdien er i kraft ændret. Dette er som regel ikke, hvad der var hensigten. En måde at omgå dette på er at bruge Ingen som standard, og udtrykkeligt test for det i kroppen af funktionen […]

    De sætninger “dette er ikke normalt, hvad der var hensigten” og “en måde at omgå dette på, er at” lugte, som om de er at dokumentere en design fejl.
    Jeg er godt klar over, men det er ikke værd faldgrube. Du vil generelt se, style guides og linters ubetinget flag foranderlig standard værdier, som er forkert i forhold til denne grund. Den eksplicitte måde at gøre de samme ting, er at fylde en attribut, der er på funktionen (function.data = []) eller bedre endnu, gøre et objekt.
    Faldgruber er der konstateret og dokumenteret, hvilket er grunden til, at dette spørgsmål er god, og har modtaget så mange upvotes. På samme tid, faldgruber, behøver ikke nødvendigvis at være fjernet. Hvor mange Python begyndere har bestået en liste til en funktion, der ændrede den, og blev chokeret over at se de ændringer, der dukker op i den oprindelige variabel? Endnu bevægelige objekt typer er vidunderlig, når du forstår at bruge dem. Jeg tror det bare koges ned til udtalelse om netop denne faldgrube.
    Sætningen “dette er ikke normalt, hvad der var hensigten” betyder “ikke er, hvad den programmør, der faktisk ville ske,” ikke “ikke hvad Python er meningen at gøre.”
    Måske du måske ønsker at sende en forespørgsel om det. Måske kan du gøre noget anderledes end, hvad der var hensigten…

    OriginalForfatteren glglgl

  4. 101

    Jeg ved intet om Python-fortolker indre funktioner (og jeg er ikke ekspert i oversættere og tolke, der enten er), så du ikke bebrejde mig, hvis jeg foreslår noget unsensible eller umuligt.

    Forudsat at python-objekter er foranderlig jeg tror, at dette bør tages i betragtning ved udformningen af den standard argumenter ting.
    Når du instantiere en liste:

    a = []

    du forvente at få en nye liste, der refereres til af en.

    Hvorfor skal a=[] i

    def x(a=[]):

    instantiere en ny liste på funktion definitionen, og ikke om aktivering?
    Det er lige som at du beder om “, hvis brugeren ikke give det argument, så instantiere en ny liste og bruge det som om det var produceret af den, der ringer”.
    Jeg tror, det er tvetydige i stedet:

    def x(a=datetime.datetime.now()):

    bruger, ønsker du en til standard for at datetime tilsvarende, når du er ved at definere eller udførelse x?
    I dette tilfælde, som i det foregående, vil jeg holde den samme adfærd, som hvis standard-argument “opgave” var den første instruktion af funktionen (datetime.nu() kaldes på funktion aktivering).
    På den anden side, hvis brugeren ønsker definitionen-tid kortlægning han kunne skrive:

    b = datetime.datetime.now()
    def x(a=b):

    Jeg ved, jeg ved, at en lukning. Alternativt Python kan give et søgeord for at tvinge definition-tid bindende:

    def x(static a=b):
    Du kan gøre: def x(a=None): Og så, hvis a er Ingen, sæt a=datetime.datetime.nu()
    Jeg ved, det var bare et eksempel for at forklare, hvorfor jeg foretrækker execution-time bindende.
    Tak for dette. Jeg kunne ikke helt sætte min finger på, hvorfor det irriterer mig at ingen ende. Du har gjort det smukt med et minimum af besvær og forvirring. Som en person, der kommer fra systemer, programmering i C++ og til tider naivt at “oversætte” sprog funktioner, er denne falske ven sparket mig i den bløde del af hovedet kæmpe, ligesom klassens attributter. Jeg forstår, hvorfor ting er på denne måde, men jeg kan ikke hjælpe, men kan ikke lide det, uanset hvad der positivt kan komme af med det. Det er i det mindste så i modsætning til min oplevelse, at jeg vil sandsynligvis (forhåbentlig) aldrig glemme det…
    når du bruger Python længe nok, vil du begynde at se, hvor logisk det er for Python til at fortolke ting, som klasse-attributter, som den måde den gør – det er kun på grund af særlige præferencer og begrænsninger i sprog som C++ (og Java og C#…), at det giver nogen mening for indholdet af class {} blok til at blive fortolket som tilhører tilfælde 🙂 Men, når undervisningen er første klasses objekter, naturligvis er det naturlige ting er, at deres indhold (hukommelse) for at afspejle deres indhold (kode).
    Normativ struktur er ikke særhed eller begrænsning i min bog. Jeg ved, at det kan være klodset og styg, men du kan kalde det en “definition” af noget. Det dynamiske sprog synes en smule som anarkister til mig Sikker på, at alle er gratis, men du har brug for struktur for at få nogen til at tømme papirkurven og bane vejen. Tror jeg er gammel… 🙂

    OriginalForfatteren Utaal

  5. 75

    Grunden er ganske enkelt, at bindinger er færdig, når koden køres, og den funktion definition er udført, godt… når de funktioner, der er defineret.

    Undersøg dette:

    class BananaBunch:
        bananas = []
    
        def addBanana(self, banana):
            self.bananas.append(banana)

    Denne kode, der lider af præcis de samme uventet tilfældighed. bananer er en class attribut, og dermed, når du føjer ting til det, den er føjet til alle forekomster af denne klasse. Årsagen er præcis den samme.

    Det er bare “Hvordan Det Fungerer”, og gøre det til at fungere anderledes i den funktion tilfælde ville sandsynligvis være kompliceret, og i klassen tilfælde sandsynligvis umuligt, eller i det mindste bremse objekt instantiering en masse, som du ville have til at holde klassen kode omkring og udføre den, når objekter er oprettet.

    Ja, det er uventet. Men når krone falder, det passer perfekt med, hvordan Python værker i almindelighed. I virkeligheden, det er en god hjælp, og når du forstå, hvorfor dette sker, vil du grok python meget bedre.

    Der sagde, at det bør indtage en fremtrædende plads i enhver god Python tutorial. Fordi, som du nævner, alle løber ind i dette problem før eller senere.

    Hvis det er forskellige for hvert eksempel, det er ikke en class-attribut. Klasse attributter er attributter på KLASSEN. Deraf navnet. Derfor er de samme for alle forekomster.
    Han var ikke beder om en beskrivelse af Python ‘ s adfærd, han spørger til de rationale. Intet i Python er bare “Hvordan Det Fungerer”; det hele er, hvad det gør for en grund.
    Og jeg gav den rationale.
    Jeg ville ikke sige, at dette “det er en god hjælp”, fordi det ikke er.
    Bortset fra at det er. Det hjælper dig med at forstå en masse ting i Python.

    OriginalForfatteren Lennart Regebro

  6. 53

    Jeg plejede at tænke at oprette objekter på runtime ville være den bedste tilgang. Jeg er mindre sikker nu, da du ikke mister nogle nyttige funktioner, selvom det kan være værd at det, uanset blot for at forhindre newbie forvirring. De ulemper af at gøre det er:

    1. Performance

    def foo(arg=something_expensive_to_compute())):
        ...

    Hvis call-evaluering er brugt, så er de dyre funktion kaldes, hver gang funktionen bruges uden et argument. Du må enten betale en dyr pris på hvert opkald, eller er nødt til manuelt at cache værdien eksternt, forurenende din namespace og tilføje oplysningerne.

    2. Tvinger bundet parametre

    Et nyttigt trick er at binde parametre for en lambda til nuværende bindende af en variabel, når lambda er oprettet. For eksempel:

    funcs = [ lambda i=i: i for i in range(10)]

    Denne returnerer en liste af funktioner, som returnerer 0,1,2,3… hhv. Hvis adfærden er ændret, de vil i stedet binde i til call-tid værdi af i, så vil du få en liste over funktioner, der alle vendte tilbage 9.

    Den eneste måde at gennemføre dette på anden måde ville være at skabe en yderligere lukning med det jeg er bundet, dvs:

    def make_func(i): return lambda: i
    funcs = [make_func(i) for i in range(10)]

    3. Introspektion

    Overveje kode:

    def foo(a='test', b=100, c=[]):
       print a,b,c

    Vi kan få oplysninger om de argumenter og standarder hjælp inspect modul, som

    >>> inspect.getargspec(foo)
    (['a', 'b', 'c'], None, None, ('test', 100, []))

    Disse oplysninger er meget nyttigt for ting som dokument generation, metaprogramming, malere etc.

    Antag nu, at den adfærd af standardindstillinger kan ændres, så det svarer til:

    _undefined = object()  # sentinel value
    
    def foo(a=_undefined, b=_undefined, c=_undefined)
        if a is _undefined: a='test'
        if b is _undefined: b=100
        if c is _undefined: c=[]

    Dog, at vi har mistet evnen til at introspect, og se, hvad den standard argumenter er. Fordi de objekter, der ikke har været konstrueret, og vi kan ikke altid få fat i dem uden egentlig at kalde funktionen. Det bedste, vi kan gøre, er at gemme off kildekoden og afkast, der som en streng.

    du kan opnå introspektion også, hvis der for hver der var en funktion til at oprette standard-argument i stedet for en værdi. undersøg modul vil blot kalde denne funktion.
    Jeg taler om, hvis adfærd blev ændret til at oprette det igen – skabe det engang er den nuværende adfærd, og hvorfor den bevægelige standard, at problemet eksisterer.
    Det forudsætter, at byggeriet er sikker på at det er tilfældet (dvs har ingen bivirkninger). Introspecting den args bør ikke har for noget, men evaluering af vilkårlig kode, der godt kunne ende med at have en effekt.
    Et andet sprog design ofte bare betyder at skrive tingene anderledes. Dit første eksempel, kunne sagtens være skrevet som: _expensive = dyrt(); def foo(arg=_expensive), hvis du specifikt ikke jeg vil have det revurderet.
    det er, hvad jeg hentydede til med “cache variablen eksternt” – det er en smule mere detaljeret, og du ende op med ekstra variabler i din namespace selv.

    OriginalForfatteren Brian

  7. 52

    5 point i forsvaret af Python

    1. Enkelhed: Den adfærd, der er enkel i følgende forstand:
      De fleste mennesker falder i denne fælde kun én gang, ikke flere gange.

    2. Sammenhæng: Python altid passerer objekter, ikke navne.
      Standard parameter er, naturligvis, en del af funktion
      overskrift (ikke funktionen kroppen). Det burde derfor være evalueret
      på modul load tid (og kun på modul load tid, medmindre indlejret), ikke
      på funktionen opkald tid.

    3. Nytten: Som Frederik Lundh påpeger i sin forklaring
      af “Default Parameter-Værdier i Python”,
      nuværende adfærd kan være ganske nyttigt for avanceret programmering.
      (Brug den sparsomt.)

    4. Tilstrækkelig dokumentation: I den mest grundlæggende dokumentation Python,
      den tutorial, spørgsmålet er højlydt bekendtgjorde, som
      en “Vigtig advarsel” i første underafsnit af Afsnit
      “Mere på at Definere Funktioner”.
      Advarslen selv bruger markeret med fed skrift,
      der er sjældent anvendt uden for overskrifter.
      RTFM: Læs den fine vejledning.

    5. Meta-læring: at Falde i den fælde, det er faktisk en meget
      nyttige øjeblik (i hvert fald hvis du er en reflekterende lærende),
      fordi du efterfølgende vil bedre forstå pointen
      “Konsistens” ovenfor, og der vil
      lære dig en hel del om Python.

    Det tog mig et år at finde denne adfærd er, at rode op i min kode på produktion, endte fjerne en komplet funktion, indtil jeg stødte ind i dette design fejl ved en tilfældighed. Jeg bruger Django. Da staging miljø ikke har mange anmodninger, at denne fejl aldrig haft nogen indvirkning på QA. Da vi gik live og har modtaget mange samtidige anmodninger – nogle nyttefunktioner begyndte at overskrive hinandens parametre! Gør sikkerhed huller, fejl og hvad der ikke er.
    no offense, men jeg spekulerer på, hvordan du har lært Python uden at løbe ind i det før. Jeg er bare lære Python nu, og det er muligt faldgrube er, der er nævnt i den officielle Python tutorial sammen med den første omtale af standard argumenter. (Som nævnt i punkt 4 i dette svar). Jeg formoder, at moralen er—snarere unsympathetically—at læse officielle dokumenter for det sprog, du bruger til at skabe produktion software.
    Også, det ville være overraskende (for mig) hvis en funktion af ukendt kompleksitet blev kaldt i tillæg til den funktion opkald, jeg gør.

    OriginalForfatteren Lutz Prechelt

  8. 48

    Hvorfor ikke du introspect?

    Jeg er virkelig overrasket over ingen har udført den indsigtsfulde introspektion, der tilbydes af Python (2 og 3 gælder) på callables.

    Et enkelt lille funktion func defineret som:

    >>> def func(a = []):
    ...    a.append(5)

    Når Python møder det, den første ting, der vil gøre, er at udarbejde det for at skabe en code objektet for denne funktion. Mens denne samling skridt er gjort, Python vurderer* og derefter butikker standard argumenter (en tom liste [] her) i den funktion, der er objekt i sig selv. Som den øverste svar er nævnt: listen a kan nu betragtes som en medlem af funktionen func.

    Så lad os gøre nogle introspektion, en før og efter at undersøge, hvordan listen bliver udvidet inde funktionen objekt. Jeg bruger Python 3.x for dette, for Python 2 det samme gælder (brug __defaults__ eller func_defaults i Python 2; ja, to navne for den samme ting).

    Funktion Før Udførelse:

    >>> def func(a = []):
    ...     a.append(5)
    ...     

    Efter Python udfører denne definition vil det tage nogen standard parametre, der er angivet (a = [] her) og proppe dem i __standard__ – attributten for den funktion, objekt (relevante afsnit: Callables):

    >>> func.__defaults__
    ([],)

    O. k, så en tom liste som den enkelt post i __defaults__, lige som forventet.

    Funktion Efter Udførelse:

    Lad os nu udføre denne funktion:

    >>> func()

    Nu, lad os se dem __defaults__ igen:

    >>> func.__defaults__
    ([5],)

    Forbavset? Værdien inde i objektet ændringer! På hinanden følgende opkald til funktionen vil nu blot føje til, at indlejrede list objekt:

    >>> func(); func(); func()
    >>> func.__defaults__
    ([5, 5, 5, 5],)

    Så der har du det, grunden til dette ‘fejl’ der sker, er det, fordi standard argumenter er en del af den funktion, objekt. Der er ikke noget underligt i gang her, det hele er bare en smule overraskende.

    Fælles løsning til at bekæmpe det på er at bruge None som standard, og derefter initialisere i funktionen krop:

    def func(a = None):
        # or: a = [] if a is None else a
        if a is None:
            a = []

    Da funktionen krop er gennemført på ny, hver gang, får du altid en frisk ny tom liste, hvis noget argument, der blev vedtaget for a.


    Til yderligere at kontrollere, at listen i __defaults__ er den samme som den, der anvendes i funktionen func kan du bare ændre din funktion til at returnere det id af listen a bruges inde i funktionen kroppen. Derefter sammenligne det til listen i __defaults__ (position [0] i __defaults__), og du vil se, hvordan disse er faktisk refererer til den samme liste eksempel:

    >>> def func(a = []): 
    ...     a.append(5)
    ...     return id(a)
    >>>
    >>> id(func.__defaults__[0]) == func()
    True

    Alle med magt af introspektion!


    * for at bekræfte, At Python vurderer standard argumenter under udarbejdelsen af funktion, kan du prøve at udføre følgende:

    def bar(a=input('Did you just see me without calling the function?')): 
        pass  # use raw_input in Py2

    som du vil opdage, input() kaldes, før processen med at opbygge funktion og binde den til navnet bar er lavet.

    Er id(...) er nødvendige for, at sidste kontrol, eller ville is operatør besvare de samme spørgsmål?
    ville gøre sig fint, jeg har lige brugt id(val), fordi jeg tror, det kan være mere intuitivt.
    Jeg vil gerne tilføje en prompt til input. som input('Did you just see me without calling me?'). Det gør det klarere, imo.
    Jeg kan lide det! Tak for at pege det ud.

    OriginalForfatteren Jim Fasarakis Hilliard

  9. 42

    Denne adfærd er let forklares ved at:

    1. funktion (klasse etc.) erklæring udføres kun én gang, for at skabe alle standard værdi objekter
    2. alt er gået som reference

    Så:

    def x(a=0, b=[], c=[], d=0):
        a = a + 1
        b = b + [1]
        c.append(1)
        print a, b, c
    1. a ikke ændre på – hver eneste opgave opkald skaber nye int objekt nyt objekt er trykt
    2. b ikke ændre på – new array er bygget op fra standard-værdi og trykt
    3. c ændringer – operation udføres på samme objekt, og det er trykt
    Din #4 kunne være forvirrende for folk, da heltal er uforanderlige og så, at “hvis” er ikke sandt. For eksempel, med d sat til 0, d.__tilføj__(1) returnerer 1, men d vil stadig være 0.
    (Faktisk, tilføj er et dårligt eksempel, men heltal være uforanderlige stadig er min vigtigste pointe.)
    ja, det var ikke godt eksempel
    Indså, at det til min store ærgrelse efter kontrol for at se, at den med b indstillet til [], b.__tilføj__([1]) returnerer [1], men også blade, b [] selv om listerne er foranderlig. Min dårlige.
    der er __iadd__, men det gør ikke arbejde sammen med int. Selvfølgelig. 🙂

    OriginalForfatteren ymv

  10. 32

    Hvad du beder om, hvorfor dette:

    def func(a=[], b = 2):
        pass

    er ikke internt, der svarer til dette:

    def func(a=None, b = None):
        a_default = lambda: []
        b_default = lambda: 2
        def actual_func(a=None, b=None):
            if a is None: a = a_default()
            if b is None: b = b_default()
        return actual_func
    func = func()

    undtagen i tilfælde af eksplicit at kalde func(Ingen Ingen), hvilke vi vil ignorere.

    Med andre ord, i stedet for at vurdere standard parametre, hvorfor så ikke gemme hver af dem, og vurdere dem, når funktionen kaldes?

    Et svar er nok lige der–det faktisk ville vende hver funktion med standard parametre til en lukning. Selv hvis det hele er gemt væk i tolk og ikke en fuld-blæst lukning, er de data, der er nødt til at være gemt et eller andet sted. Det ville være langsommere og bruger mere hukommelse.

    Ville det ikke behøver at være en lukning – en bedre måde at tænke på, at det ville simpelthen at gøre bytecode skabe defaults den første linje af kode – efter alt du er udarbejdelse af kroppen på det tidspunkt alligevel – der er ingen reel forskel mellem kode i de argumenter og kode i kroppen.
    Sandt, men det ville stadig langsom Python ned, og det ville faktisk være ganske overraskende, medmindre du gør det samme for klasse-definitioner, hvilket vil gøre det dumt langsomt, som du ville have til at re-run hele klassen definition, hver gang du instantierer en klasse. Som nævnt fix ville være mere overraskende, end det problem.
    Der er aftalt med Lennart. Som Guido er glad for at sige, for hvert sprog har eller standard-bibliotek, der er nogen derude, som bruger det.
    At ændre det nu ville være vanvid–vi bare at udforske, hvorfor det er sådan, det er. Hvis det gjorde sent i standard evaluering til at begynde med, er det ikke nødvendigvis ville være overraskende. Det er helt sikkert sandt, at en sådan kerne en forskel for parsing ville have flot, og sikkert mange obskure, effekter på sproget som helhed.

    OriginalForfatteren Glenn Maynard

  11. 31

    1) Den såkaldte problemet med “Mutable Standard-Argument” er generelt et særligt eksempel viser, at:

    “Alle funktioner med dette problem lider også fra lignende bivirkning problem på den aktuelle parameter,”

    Det er imod reglerne i funktionel programmering, normalt undesiderable og bør være fast sammen.

    Eksempel:

    def foo(a=[]):                 # the same problematic function
        a.append(5)
        return a
    
    >>> somevar = [1, 2]           # an example without a default parameter
    >>> foo(somevar)
    [1, 2, 5]
    >>> somevar
    [1, 2, 5]                      # usually expected [1, 2]

    Løsning: en kopi

    En helt sikker løsning er at copy eller deepcopy input objekt først, og så til at gøre, hvad med at kopiere.

    def foo(a=[]):
        a = a[:]     # a copy
        a.append(5)
        return a     # or everything safe by one line: "return a + [5]"

    Mange indbyggede bevægelige typer har en kopi metode som some_dict.copy() eller some_set.copy() eller kan kopieres let som somelist[:] eller list(some_list). Hvert objekt kan også være kopieret af copy.copy(any_object) eller mere grundig med copy.deepcopy() (sidstnævnte nyttigt, hvis den bevægelige objekt er sammensat af bevægelige objekter). Nogle objekter er grundlæggende baseret på bivirkninger som “fil” objekt og kan ikke meningsfuldt gengives ved kopiering. kopiering

    Eksempel problem for et lignende spørgsmål, SÅ

    class Test(object):            # the original problematic class
      def __init__(self, var1=[]):
        self._var1 = var1
    
    somevar = [1, 2]               # an example without a default parameter
    t1 = Test(somevar)
    t2 = Test(somevar)
    t1._var1.append([1])
    print somevar                  # [1, 2, [1]] but usually expected [1, 2]
    print t2._var1                 # [1, 2, [1]] but usually expected [1, 2]

    Det bør ikke være hverken gemt i en offentlige attribut af en instans, der returneres af denne funktion. (Under forudsætning af, at private attributter for eksempel bør ikke ændres fra ydersiden af denne klasse eller underklasser af konventionen. dvs _var1 er en privat attribut )

    Konklusion:

    Input parametre objekter bør ikke være ændret (muteret), og heller ikke de bør ikke være binded til et objekt, der returneres af funktionen. (Hvis vi foretrækker programmering uden bivirkninger, som er stærkt anbefales. se Wiki om “bivirkning” (De to første afsnit er relevante i denne sammenhæng.)
    .)

    2)

    Kun hvis den side effekt på den faktiske parameter er påkrævet, men uønskede på standard parameter, så den brugbare løsning er def ...(var1=None): if var1 is None: var1 = [] Mere..

    3) I nogle tilfælde er mutable adfærd af standard parametre nyttigt.

    Jeg håber du er klar over, at Python er ikke et funktionelt programmeringssprog.
    Ja, Python er en multi-paragigm sprog med nogle funktionelle egenskaber. (“Du må ikke gøre ethvert problem at ligne et søm, bare fordi du har en hammer.”) Mange af dem er i Python bedste praksis med hensyn. Python har en interessant HOWTO Funktionel Programmering Andre funktioner er lukninger og currying, som ikke er nævnt her.
    Jeg vil også tilføje, på dette sene tidspunkt, at Python ‘ s opgave semantik er blevet designet specifikt til at undgå kopiering af data, hvor det er nødvendigt, så oprettelse af kopier (og især dybt kopier) vil påvirke både køre-tid og hukommelse forbrug negativt. De bør derfor kun bruges når det er nødvendigt, men tilflyttere ofte har svært ved at forstå, når det er.
    Jeg er enig. En midlertidig kopi er den mest sædvanlige måde, og nogle gange den eneste mulige måde, til at beskytte den oprindelige foranderlig data fra en fremmed funktion, der ændrer dem potentielt kan. Heldigvis er en funktion, der på urimelig måde ændrer data betragtes som en fejl, og derfor er ualmindeligt.
    Jeg er enig med dette svar. Og jeg forstår ikke, hvorfor de def f( a = None ) konstruere anbefales, hvis du virkelig mener noget andet. Kopiering er ok, fordi du ikke skal mutere argumenter. Og når du gør if a is None: a = [1, 2, 3], du må kopiere listen alligevel.

    OriginalForfatteren hynekcer

  12. 26

    Dette har faktisk intet at gøre med standardværdier, andet end at det kommer ofte op som en uventet adfærd, når du skriver opgaver, ændres standardindstillingerne.

    >>> def foo(a):
        a.append(5)
        print a
    
    >>> a  = [5]
    >>> foo(a)
    [5, 5]
    >>> foo(a)
    [5, 5, 5]
    >>> foo(a)
    [5, 5, 5, 5]
    >>> foo(a)
    [5, 5, 5, 5, 5]

    Ingen standardværdier i sigte i denne kode, men du får præcis det samme problem.

    Problemet er, at foo er ændre en foranderlig variabel videre fra den, der ringer, når de ringer ikke forventer det. Kode som denne ville være fint, hvis den funktion blev kaldt noget som append_5; den, der ringer ville være at kalde funktionen for at ændre den værdi, de går i, og den adfærd, der forventes. Men sådan en funktion ville være meget usandsynligt, at tage en standard argument, og sandsynligvis vil ikke returnere listen (da de ringer allerede har en reference til listen; den ene er det bare gået i).

    Din oprindelige foo, med en standard argument, burde det ikke være at ændre a, om det udtrykkeligt blev vedtaget i eller fik den standard værdi. Din kode bør forlade foranderlig argumenter alene, medmindre det er klart fra konteksten/navn/dokumentation for, at de argumenter, der formodes at blive ændret. Ved hjælp af bevægelige værdier, der sendes i som argumenter som lokale temporaries er en meget dårlig idé, uanset om vi er i Python eller ej, og om der er standard argumenter, der er involveret eller ej.

    Hvis du har brug for til at manipulere en lokal, midlertidig i løbet af computing noget, og du er nødt til at starte din manipulation fra et argument værdi, du har brug for til at lave en kopi.

    Selv om relaterede, jeg tror, det er forskellige adfærd (som vi forventer append at ændre a “in-sted”). At en standard ændres ikke er re-instantieres på hver ring til er det “uventede” lidt… i det mindste for mig. 🙂
    hvis funktionen er forventet til at ændre argument, hvorfor ville det give mening at have en standard?
    det eneste eksempel, jeg kan tænke på er cache={}. Men jeg tror, at dette “mindst rædsel” kommer op, er, når du har ikke jeg forventer (eller ønsker) den funktion, du vil kalde op til at mutere argumentet.
    Jeg forlod mit eget svar her med en udvidelse af, at stemningen. Lad mig vide hvad du synes. Jeg kan tilføje dit eksempel cache={} ind i det for fuldstændighedens skyld.
    Pointen i mit svar er, at hvis du nogensinde er forbavset over ved et uheld muterer standardværdien for et argument, så har du en anden fejl, som er at din kode kan komme til at mutere en opkalds-værdi, når den standard ikke, der bruges. Og bemærk, at brug None og tildele den rigtige standard, hvis arg er None ikke løse dette problem. (jeg betragter det som en anti mønster af den grund). Hvis du retter andre fejl ved at undgå at mutere argument værdier, hvorvidt eller ikke de har standardværdier, så vil du aldrig mærke eller bekymre sig om denne “forbløffende” adfærd.

    OriginalForfatteren Ben

  13. 25

    Det er en optimering. Som et resultat af denne funktionalitet, hvilken af disse to funktionskald tror du er hurtigere?

    def print_tuple(some_tuple=(1,2,3)):
        print some_tuple
    
    print_tuple()        #1
    print_tuple((1,2,3)) #2

    Jeg vil give dig et vink. Her er det demontering (se http://docs.python.org/library/dis.html):

    #1

    0 LOAD_GLOBAL              0 (print_tuple)
    3 CALL_FUNCTION            0
    6 POP_TOP
    7 LOAD_CONST               0 (None)
    10 RETURN_VALUE

    #2

     0 LOAD_GLOBAL              0 (print_tuple)
     3 LOAD_CONST               4 ((1, 2, 3))
     6 CALL_FUNCTION            1
     9 POP_TOP
    10 LOAD_CONST               0 (None)
    13 RETURN_VALUE

    Jeg tvivler på, at den erfarne adfærd har en praktisk anvendelse (som virkelig har brugt statiske variabler i C, uden avl fejl ?)

    Som du kan se, er der er en performance fordel, når du bruger uforanderlige standard argumenter. Dette kan gøre en forskel, hvis det er en ofte kaldes funktionen eller standard-argumentet tager lang tid at konstruere. Også, huske på, at Python er ikke C. I C du har konstanter, der er stort set gratis. I Python du ikke har denne fordel.

    hvordan kan du få den dissasembly?
    Brug dis-modulet: docs.python.org/library/dis.html
    Hvor meget af en forskel kan 3 LOAD_CONST 4 ((1, 2, 3)) gøre over endda millioner af gentagelser? ^0^. Måske vil jeg profil og rapportere tilbage…

    OriginalForfatteren Jason Baker

  14. 23

    Allerede travlt emne, men ud fra hvad jeg læser her, følgende hjulpet mig med at indse, hvordan det er at arbejde internt:

    def bar(a=[]):
         print id(a)
         a = a + [1]
         print id(a)
         return a
    
    >>> bar()
    4484370232
    4484524224
    [1]
    >>> bar()
    4484370232
    4484524152
    [1]
    >>> bar()
    4484370232 # Never change, this is 'class property' of the function
    4484523720 # Always a new object 
    [1]
    >>> id(bar.func_defaults[0])
    4484370232
    faktisk kan dette være en smule forvirrende for nybegyndere, som a = a + [1] overbelastninger a… overveje at ændre det til b = a + [1] ; print id(b) og tilføje en linje a.append(2). Det vil gøre det mere indlysende, at + på to lister, som altid skaber en ny liste (tildelt b), mens en modificeret a kan stadig have den samme id(a).

    OriginalForfatteren Stéphane

  15. 19

    Python: Den Bevægelige Standard Argument

    Standard argumenter at få evalueret på det tidspunkt den funktion, der er samlet i en funktion objekt. Når de anvendes af den funktion, flere gange af den funktion, de er og forbliver det samme objekt.

    Når de er mutable, når muterede (for eksempel ved at tilføje et element til det), de fortsat er muteret på hinanden følgende opkald.

    De ophold muteret, fordi de er de samme objekt hver gang.

    Tilsvarende kode:

    Da listen er bundet til funktionen, når funktionen objekt er kompileret og instantieres, er dette:

    def foo(mutable_default_argument=[]): # make a list the default argument
        """function that uses a list"""

    er næsten præcis svarer til dette:

    _a_list = [] # create a list in the globals
    
    def foo(mutable_default_argument=_a_list): # make it the default argument
        """function that uses a list"""
    
    del _a_list # remove globals name binding

    Demonstration

    Her er en demonstration – du kan kontrollere, at de er de samme objekt, hver gang de er refereret af

    • se, at listen er oprettet før funktion er færdig med at kompilere til en funktion, objekt,
    • observere, at id er det samme, hver gang den liste, der henvises til,
    • at bemærke, at listen bliver ændret, når den funktion, der bruger det der hedder en anden gang,
    • at iagttage den rækkefølge, hvori produktionen er udskrevet fra kilden (som jeg bekvemt nummererede for dig):

    example.py

    print('1. Global scope being evaluated')
    
    def create_list():
        '''noisily create a list for usage as a kwarg'''
        l = []
        print('3. list being created and returned, id: ' + str(id(l)))
        return l
    
    print('2. example_function about to be compiled to an object')
    
    def example_function(default_kwarg1=create_list()):
        print('appending "a" in default default_kwarg1')
        default_kwarg1.append("a")
        print('list with id: ' + str(id(default_kwarg1)) + 
              ' - is now: ' + repr(default_kwarg1))
    
    print('4. example_function compiled: ' + repr(example_function))
    
    
    if __name__ == '__main__':
        print('5. calling example_function twice!:')
        example_function()
        example_function()

    og køre det med python example.py:

    1. Global scope being evaluated
    2. example_function about to be compiled to an object
    3. list being created and returned, id: 140502758808032
    4. example_function compiled: <function example_function at 0x7fc9590905f0>
    5. calling example_function twice!:
    appending "a" in default default_kwarg1
    list with id: 140502758808032 - is now: ['a']
    appending "a" in default default_kwarg1
    list with id: 140502758808032 - is now: ['a', 'a']

    Er dette i strid med princippet om “det Mindste Forundret”?

    Denne rækkefølge udførelse er ofte forvirrende for nye brugere af Python. Hvis du forstår, Python udførelse model, så bliver det helt forventet.

    Den sædvanlige instruktion på ny Python brugere:

    Men dette er grunden til, at den sædvanlige instruktion for nye brugere at oprette deres standard argumenter som dette i stedet:

    def example_function_2(default_kwarg=None):
        if default_kwarg is None:
            default_kwarg = []

    Denne bruger Ingen singleton som en sentinel-objekt til at fortælle den funktion, om vi har fået et argument andet end standard. Hvis vi får ikke noget argument, så vi rent faktisk ønsker at bruge en ny tom liste, [], som standard.

    Som i afsnittet selvstudium på kontrol flow siger:

    Hvis du ikke ønsker, at den standard for at være delt mellem efterfølgende opkald,
    du kan skrive funktionen som denne i stedet:

    def f(a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L

    OriginalForfatteren Aaron Hall

  16. 19

    Den korteste svar nok være, “definition er udførelse”, derfor er hele argumentet gør ingen streng forstand. Som en mere konstrueret eksempel, kan du nævne dette:

    def a(): return []
    
    def b(x=a()):
        print x

    Forhåbentlig er det nok at vise, at der ikke udfører den standard argument udtryk på den tid af def erklæring ikke er let eller ikke giver mening, eller begge dele.

    Jeg er enig i det er en gotcha, når du forsøger at bruge standard konstruktører, selv om.

    OriginalForfatteren Baczek

  17. 18

    En enkel løsning ved hjælp af Ingen

    >>> def bar(b, data=None):
    ...     data = data or []
    ...     data.append(b)
    ...     return data
    ... 
    >>> bar(3)
    [3]
    >>> bar(3)
    [3]
    >>> bar(3)
    [3]
    >>> bar(3, [34])
    [34, 3]
    >>> bar(3, [34])
    [34, 3]

    OriginalForfatteren hugo24

  18. 18

    Denne adfærd er ikke overraskende, hvis du tager følgende i betragtning:

    1. Adfærd read-only klassen attributter ved tildeling forsøg, og at
    2. Funktioner, der er objekter (forklaret i det accepterede svar).

    Rolle (2) er blevet dækket grundigt i denne tråd. (1) er sandsynligvis den forbavselse, der forårsager faktor, som denne adfærd er ikke “intuitiv”, når de kommer fra andre sprog.

    (1) er beskrevet i Python tutorial om klasser. I et forsøg på at tildele en værdi til en read-only attributten class:

    …alle variabler, der er fundet uden for den inderste omfang er
    read-only (et forsøg på at skrive en sådan variabel vil du blot oprette en
    nye lokale variable i den inderste omfang, der forlader ens
    opkaldt ydre variabel uændret
    ).

    Se tilbage til det oprindelige eksempel og overveje ovenstående punkter:

    def foo(a=[]):
        a.append(5)
        return a

    Her foo er et objekt, og a er en egenskab ved foo (findes på foo.func_defs[0]). Da a er en liste, a er foranderlig, og er således en læse-skrive-attribut af foo. Det er initialiseret til den tomme liste, som er angivet af den signatur, når funktionen er instantieret, og er til rådighed for læsning og skrivning, så længe funktionen objektet findes.

    Ringer foo uden at tilsidesætte en standard bruger som standard værdien fra foo.func_defs. I dette tilfælde, foo.func_defs[0] bruges til a i funktion objektets kode anvendelsesområde. Ændringer til a ændre foo.func_defs[0], som er en del af foo objekt, og fortsætter mellem udførelse af kode i foo.

    Nu sammenligne dette eksempel fra den dokumentation på efterligne den standard argument adfærd i andre sprog, sådan, at den funktion signatur standarder bruges, hver gang funktionen er udført:

    def foo(a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L

    Tage (1) og (2) i betragtning, kan man se, hvorfor dette opnår den ønskede adfærd:

    • Når foo funktion objekt instantieres, foo.func_defs[0] er indstillet til None, et uforanderligt objekt.
    • Når funktionen er udført med standard (med ingen parameter er angivet til L i funktionen opkald), foo.func_defs[0] (None) er til rådighed i det lokale omfang som L.
    • L = [], opgaven ikke lykkes på foo.func_defs[0], fordi denne egenskab er read-only.
    • Per (1), en ny lokal variabel også opkaldt L er oprettet i den lokale anvendelsesområde, og som anvendes til den resterende del af funktionen. foo.func_defs[0] således uændret for fremtidige kald af foo.

    OriginalForfatteren Dmitry Minkovsky

  19. 17

    Løsninger her er:

    1. Bruge None som din standard-værdi (eller en nonce object), og tænd den til at oprette dine værdier på runtime, eller
    2. Bruge en lambda som din standard parameter, og kalder det inden for en try-blok for at få den standard værdi (det er den slags ting, der lambda abstraktion er for).

    Den anden mulighed er rart, fordi brugerne af den funktion kan bestå i en konverterbar, som kan være allerede eksisterende (såsom en type)

    OriginalForfatteren Marcin

  20. 16

    Jeg nogle gange udnytte denne adfærd som et alternativ til følgende mønster:

    singleton = None
    
    def use_singleton():
        global singleton
    
        if singleton is None:
            singleton = _make_singleton()
    
        return singleton.use_me()

    Hvis singleton er kun anvendes af use_singleton, jeg kan godt lide følgende mønster som en erstatning:

    # _make_singleton() is called only once when the def is executed
    def use_singleton(singleton=_make_singleton()):
        return singleton.use_me()

    Jeg har brugt denne for at instantiere klient klasser at få adgang til eksterne ressourcer, og også for at skabe dicts eller lister for memoization.

    Da jeg ikke tror, at dette mønster er velkendt, og det jeg har lagt en kort kommentar i at sikre sig mod misforståelser.

    Jeg foretrækker at tilføje en dekoratør for memoization, og sætte memoization cache på funktion objekt i sig selv.
    Dette eksempel ikke erstatte den mere komplekse mønster, du er vis, fordi du kalder _make_singleton på def tid i standard argument eksempel, men på opfordring gang i den globale eksempel. En sand substitution ville bruge nogle form for bevægelig kasse for standard argument værdi, men det argument giver mulighed for at passere alternative værdier.

    OriginalForfatteren bgreen-litl

  21. 16

    Jeg kommer til at vise en alternativ struktur for at passere en standard liste værdi til en funktion (det virker lige så godt med ordbøger).

    Som andre har flittigt kommenteret, liste parameter er bundet til den funktion, når det er defineret som modsætning til, når det er udført. Fordi lister og ordbøger er foranderlig, enhver ændring af denne parameter vil påvirke andre kalder denne funktion. Som et resultat, efterfølgende kald til funktionen vil modtage denne fælles liste, som kan være blevet ændret af andre opkald til funktionen. Værre endnu, der er to parametre, der bruger denne funktion er fælles parameter på samme tid ligeglade med de ændringer, der er foretaget af andre.

    Forkert Metode (sandsynligvis…):

    def foo(list_arg=[5]):
        return list_arg
    
    a = foo()
    a.append(6)
    >>> a
    [5, 6]
    
    b = foo()
    b.append(7)
    # The value of 6 appended to variable 'a' is now part of the list held by 'b'.
    >>> b
    [5, 6, 7]  
    
    # Although 'a' is expecting to receive 6 (the last element it appended to the list),
    # it actually receives the last element appended to the shared list.
    # It thus receives the value 7 previously appended by 'b'.
    >>> a.pop()             
    7

    Du kan kontrollere, at de er én og samme objekt ved hjælp id:

    >>> id(a)
    5347866528
    
    >>> id(b)
    5347866528

    Per Brett Slatkin “Effektiv Python: 59 Specifikke Måder at Skrive Bedre Python”, Punkt 20: Brug None Dokumentationsstrenge, og til at angive dynamisk standard argumenter (s. 48)

    Konventionen for at opnå det ønskede resultat i Python er at
    angive en standardværdi for None og til at dokumentere den faktiske adfærd
    i docstring.

    Gennemførelsen sikrer, at alle kald til funktionen enten modtager standard liste eller anden liste, der overføres til funktionen.

    Foretrukne Metode:

    def foo(list_arg=None):
       """
       :param list_arg:  A list of input values. 
                         If none provided, used a list with a default value of 5.
       """
       if not list_arg:
           list_arg = [5]
       return list_arg
    
    a = foo()
    a.append(6)
    >>> a
    [5, 6]
    
    b = foo()
    b.append(7)
    >>> b
    [5, 7]
    
    c = foo([10])
    c.append(11)
    >>> c
    [10, 11]

    Der kan være legitime use cases for de “Forkerte” – Metoden, hvorved programmøren tilsigtede standard-liste parameter til at være fælles, men det er mere sandsynligt, at undtagelsen end reglen.

    OriginalForfatteren Alexander

  22. 15

    Du kan omgå dette ved at erstatte objekt (og dermed uafgjort med anvendelsesområdet):

    def foo(a=[]):
        a = list(a)
        a.append(5)
        return a

    Grimme, men det virker.

    Dette er en god løsning i de tilfælde, hvor du bruger automatisk dokumentation generation af software til at dokumentere, hvilke typer af argumenter, der forventes af funktion. At sætte a=Ingen, og derefter en indstilling til [til], hvis a er Ingen ikke hjælper læseren til at forstå et overblik, hvad der er forventet.
    Bedre at bruge en docstring.
    Cool idé: rebinding, at navnet garanti for, at det kan aldrig ændres. Jeg kan virkelig godt lide det.
    Det er netop den måde at gøre det på. Python gør ikke en kopi af den parameter, så det er op til dig at gøre kopien eksplicit. Når du har en kopi, det er din at ændre, som du uden nogen uventede bivirkninger.

    OriginalForfatteren jdborg

  23. 14

    Når vi gør dette:

    def foo(a=[]):
        ...

    … tildeler vi argument a til en unavngivne listen, hvis den, der ringer, ikke passere værdien af en.

    At gøre tingene enklere for denne diskussion, lad os midlertidigt give den unavngivne listen et navn. Hvordan omkring pavlo ?

    def foo(a=pavlo):
       ...

    Til enhver tid, hvis den, der ringer ikke fortælle os, hvad a er, vi genbruger pavlo.

    Hvis pavlo er foranderlig (kan ændres), og foo ender med at ændre det, for en effekt, vi lægger mærke til det næste gang foo kaldes uden at angive a.

    Så dette er hvad du ser (Husk, pavlo er initialiseret til []):

     >>> foo()
     [5]

    Nu, pavlo er [5].

    Ringer foo() igen ændrer pavlo igen:

    >>> foo()
    [5, 5]

    Angivelse a, når du ringer foo() sikrer pavlo er ikke rørt.

    >>> ivan = [1, 2, 3, 4]
    >>> foo(a=ivan)
    [1, 2, 3, 4, 5]
    >>> ivan
    [1, 2, 3, 4, 5]

    Så, pavlo er stadig [5, 5].

    >>> foo()
    [5, 5, 5]

    OriginalForfatteren Saish

  24. 11

    Det kan være rigtigt, at:

    1. Nogen bruger alle sprog/biblioteket, og
    2. Med at skifte adfærd, her ville være en dårlig idé, men

    det er helt konsekvent at holde begge ovennævnte funktioner, og stadig gøre et andet punkt:

    1. Det er en forvirrende funktion, og det er uheldigt i Python.

    De andre svar, eller i det mindste nogle af dem enten gør punkt 1 og 2 men ikke 3, eller foretage punkt 3 og nedtone punkt 1 og 2. Men alle tre er sandt.

    Det kan være rigtigt, at skifte heste midt i vadestedet her, ville være at spørge om væsentlige brud, og at der kunne være flere problemer, der er skabt ved at ændre Python til intuitivt at håndtere Stefano ‘ s indledende uddrag. Og det kan være rigtigt, at nogen, der vidste, Python interne godt kunne forklare et minefelt af konsekvenser. Men

    Den nuværende adfærd er ikke Pythonic, og Python er en succes, fordi meget lidt om det sprog, der er i strid med den mindste forundring hvor som helst nær det dårligt. Det er et reelt problem, om ikke det ville være klogt at udrydde det. Det er en design fejl. Hvis du forstår det sprog meget bedre, ved at forsøge at spore den adfærd, jeg kan sige, at C++ gør alt dette og mere, og du lærer en masse ved at navigere, for eksempel, subtile pointer fejl. Men dette er ikke Pythonic: mennesker, der bekymrer sig om Python nok til at holde fast i lyset af denne adfærd er mennesker, der er udarbejdet til det sprog, fordi Python har langt færre overraskelser end andre sprog. Dabblers og nysgerrig blive Pythonistas, når de er forbavsede over, hvor lidt tid det tager at få noget arbejde-ikke på grund af en design fl–jeg mener, skjulte logik puslespil,–der skærer mod intuitioner af programmører, der er udarbejdet til Python, fordi det Virker Bare.

    -1 Selvom et forsvarligt perspektiv, dette er ikke et svar, og er jeg uenig. Alt for mange specielle undtagelser avle deres eget hjørne tilfælde.
    Så så, det er “utroligt uvidende” at sige, at i Python, det ville give mere mening for en standard-argument [] til at blive [], hver gang funktionen kaldes?
    Og det er ignorant at betragte som et uheldigt udtryk at sætte en standard argument om, at Ingen, og derefter i selve kroppen af funktionen indstilling, hvis argumentet == None: argumentet = []? Er det uvidende til at overveje dette formsprog uheldigt, da det ofte er mennesker, der ønsker det, en naiv nybegynder ville forvente, at hvis du vil tildele f(argument = []), argument vil automatisk en værdi for []?
    Men i Python, en del af ånden i det sprog, er, at du ikke behøver at tage alt for mange dybe dyk; – array.sortere() virker, og virker uanset, hvor lidt du forstår om sortering, big-O, og konstanter. Skønheden i Python i array sortering mekanisme, til at give et af utallige eksempler, er, at du er ikke forpligtet til at tage et dybt dyk ned i tårnene. Og for at sige det anderledes, skønheden i Python er, at man er der normalt ikke er forpligtet til at tage et dybt dyk ned i gennemførelsen at få noget der Bare Virker. Og der er en løsning (…hvis argumentet == None: argumentet = []), MISLYKKES.
    Som et enkeltstående, erklæring x=[] betyder “at oprette en tom liste objekt, og binde navn ‘x’ til det.” Så, i def f(x=[]), en tom liste er også oprettet. Det behøver ikke altid få, der er bundet til x, så i stedet bliver det bundet til standard surrogat. Senere, når f() kaldes, standard er trukket ud og bundet til x. Da det var den tomme liste selv, der var squirreled væk, det samme liste, er de eneste tilgængelige til at binde sig til x, om noget har været fast inde i det eller ej. Hvordan kunne det være anderledes?

    OriginalForfatteren JonathanHayward

  25. 9

    Denne “bug” gav mig en masse overarbejde timer! Men jeg er begyndt at se en potentiel brug af det (men jeg ville gerne have det til at være ved den tid, stadig)

    Jeg vil give dig, hvad jeg ser som et nyttigt eksempel.

    def example(errors=[]):
        # statements
        # Something went wrong
        mistake = True
        if mistake:
            tryToFixIt(errors)
            # Didn't work.. let's try again
            tryToFixItAnotherway(errors)
            # This time it worked
        return errors
    
    def tryToFixIt(err):
        err.append('Attempt to fix it')
    
    def tryToFixItAnotherway(err):
        err.append('Attempt to fix it by another way')
    
    def main():
        for item in range(2):
            errors = example()
        print '\n'.join(errors)
    
    main()

    udskriver følgende

    Attempt to fix it
    Attempt to fix it by another way
    Attempt to fix it
    Attempt to fix it by another way

    OriginalForfatteren Norfeldt

  26. 8

    Jeg tror, at svaret på dette spørgsmål ligger i, hvordan python videregive data til parameter (forbi værdi eller ved henvisning), ikke foranderlighed, eller hvordan python håndtere “def” – sætning.

    En kort introduktion. For det første, at der er to typer af data typer i python, er en helt elementære data type, som numre, og en anden datatype er objekter. For det andet, når overførsel af data til parametre, python-pass elementære data type af værdi, dvs, en lokal kopi af en værdi i en lokal variabel, men passerer objektet ved henvisning, dvs, henvisninger til objektet.

    At indrømme, at de to ovennævnte punkter, så lad os forklare, hvad der skete til python-kode. Det er kun på grund af forbipasserende reference til objekter, men har intet at gøre med foranderlig/uforanderlige, eller nok det faktum, at “def” – sætning udføres kun én gang, når den er defineret.

    [] er et objekt, så python passere reference [] for at a, dvs, a er kun en pointer til [], som ligger i hukommelsen, som et objekt. Der er kun én kopi af [],, dog, mange referencer til det. For det første foo(), liste [] er ændret til En ved at tilføje metode. Men Bemærk, at der kun er én kopi af listen objekt og objektet bliver nu En. Når du kører den anden foo(), hvad effbot webside siger (poster er ikke vurderet af nogen mere) er forkert. a er vurderet til at være på listen objekt, men nu er indholdet af objektet er En. Det er effekten af forbipasserende reference! Resultatet af foo(3) let kan omregnes til på samme måde.

    Til yderligere at validere mine svar, så lad os tage et kig på yderligere to koder.

    ====== Nej 2 ========

    def foo(x, items=None):
        if items is None:
            items = []
        items.append(x)
        return items
    
    foo(1)  #return [1]
    foo(2)  #return [2]
    foo(3)  #return [3]

    [] er et objekt, så er None (det tidligere er foranderlig, mens sidstnævnte er uforanderlige. Men foranderlighed har intet at gøre med spørgsmålet). Ingen er et eller andet sted i rummet, men vi ved, at det er der, og der er kun en kopi af noget. Så hver gang foo er gjort gældende, at poster, der er evalueret (i modsætning til nogle svar, at det er kun evalueres én gang) til at være Intet, at være klar reference (eller adresse) none (Ingen). Så i foo, elementet ændres til [], dvs, – point til en anden genstand, der har en anden adresse.

    ====== No. 3 =======

    def foo(x, items=[]):
        items.append(x)
        return items
    
    foo(1)    # returns [1]
    foo(2,[]) # returns [2]
    foo(3)    # returns [1,3]

    Aktiveringen af foo(1) at gøre elementer, der peger på en liste object [] med en adresse, sige, 11111111. indholdet af listen er ændret til En i foo funktion i efterfølgeren, men adressen er ikke blevet ændret, stadig 11111111. Så foo(2,[]) er på vej. Selv om den [] i foo(2,[]) har samme indhold som standard parameter [], når du ringer foo(1), deres adresse er forskellige! Da vi give den parameter, der udtrykkeligt, items har til at tage adressen af denne nye [], siger 2222222, og returnerer det efter at foretage nogle ændringer. Nu foo(3) er udført. da kun x er forudsat, at poster, der er til at tage dens standard værdi igen. Hvad er default værdi? Det er set, når der definerer foo funktion: liste over objekt, der ligger i 11111111. Så de elementer, der er vurderet til at være den adresse 11111111 at have et element 1. Listen ligger på 2222222 også indeholder et element 2, men det er ikke påpeget af elementer mere. Det er derfor En append af 3 vil gøre items [1,3].

    Fra ovenstående forklaringer, kan vi se, at effbot webside, der anbefales i den accepterede svar undladt at give relevante svar på dette spørgsmål. Hvad er mere, jeg tror på et punkt i effbot webside er forkert. Jeg tror, at koden med hensyn til BRUGERGRÆNSEFLADEN.Knappen er korrekt:

    for i in range(10):
        def callback():
            print "clicked button", i
        UI.Button("button %s" % i, callback)

    Hver knap kan holde en klar callback-funktion, som vil vise forskellige værdien af i. Jeg kan give et eksempel for at vise dette:

    x=[]
    for i in range(10):
        def callback():
            print(i)
        x.append(callback) 

    Hvis vi udføre x[7]() vi får 7 som forventet, og x[9]() vil giver 9, en anden værdi af i.

    Dit sidste punkt er forkert. Prøv det og du vil se, at x[7]() er 9.
    “python passere elementære data type af værdi, dvs, en lokal kopi af en værdi i en lokal variabel,” det er helt forkert. Jeg er forundret over, at nogen kan naturligvis vide, Python meget godt, men har så forfærdelig misforståelse af nøgletal. 🙁
    Var -1 grund af tonen, eller tror du, hvad jeg siger, er forkert?

    OriginalForfatteren user2384994

  27. 6

    Dette er ikke en designfejl. Alle, der rejser over dette er for at gøre noget forkert.

    Der er 3 tilfælde, jeg kan se, hvor du kan løbe ind i problemet:

    1. Du har til hensigt at ændre argument som en bivirkning af funktion. I dette tilfælde er det aldrig giver mening at have en standard argument. Den eneste undtagelse er, når du er misbruger det argument, at have funktion attributter, fx cache={}, og du ville ikke være forventet at kalde funktionen med et virkeligt argument overhovedet.
    2. Du har til hensigt at forlade argument ej, men du ved et uheld gjorde ændre det. Det er en fejl, ordne det.
    3. Du har til hensigt at ændre argument for at bruge inde i funktionen, men ikke forvente, at den ændring, der skal ses uden for funktionen. I så fald er du nødt til at gøre en kopi af argumentet, om det var den standard, eller ikke! Python er ikke et call-by-value sprog, så det gør ikke kopien for dig, er du nødt til at være eksplicit omkring det.

    Eksemplet i spørgsmål kunne falde ind under kategori 1 eller 3. Det er mærkeligt, at det både ændrer bestået listen, og giver den tilbage; du bør vælge det ene eller det andet.

    “At gøre noget forkert” er den diagnose. Som sagt, jeg tror, der er tidspunkter, var =Ingen mønster er nyttigt, men generelt behøver du ikke ønsker at ændre, hvis bestået en foranderlig i dette tilfælde (2). Den cache={} mønster er virkelig en interview-eneste løsning, i real-kode, som du sandsynligvis ønsker @lru_cache!

    OriginalForfatteren Mark Ransom

  28. 4

    Bare ændre den funktion at være:

    def notastonishinganymore(a = []): 
        '''The name is just a joke :)'''
        a = a[:]
        a.append(5)
        return a

    OriginalForfatteren ytpillai

  29. 3
    >>> def a():
    >>>    print "a executed"
    >>>    return []
    >>> x =a()
    a executed
    >>> def b(m=[]):
    >>>    m.append(5)
    >>>    print m
    >>> b(x)
    [5]
    >>> b(x)
    [5, 5]
    manglende forklaring, hvad sker der?

    OriginalForfatteren jai

  30. -4

    Arkitektur

    Tildeler default-værdier i en funktion opkald er en kode, der lugter.

    def a(b=[]):
        pass

    Dette er en underskrift af en funktion, der er op til noget godt. Ikke kun på grund af de problemer, der er beskrevet af andre svar. Jeg vil ikke gå ind i det her.

    Denne funktion har til formål at gøre to ting. Oprette en ny liste, og udføre en funktionalitet, mest sandsynligt på denne liste.

    Funktioner, der gør to ting er dårlige funktioner, som vi kan lære af ren kode praksis.

    At angribe dette problem med polymorfi, vi vil udvide python liste eller en wrap i en klasse, derefter udføre vores funktion ved det.

    Men vent du siger, jeg kan godt lide mit one-liners.

    Godt, gæt hvad. Koden er mere end bare en måde at styre adfærden af hardware.
    Det er en måde:

    • at kommunikere med andre udviklere, der arbejder på den samme kode.

    • være i stand til at ændre adfærd på hardware, når nye behov opstår.

    • være i stand til at forstå strømmen af programmet, efter du hente koden igen efter to år til at lave de forandringer, der er nævnt ovenfor.

    Ikke forlade tid-bomber for dig selv at afhente senere.

    Der adskiller denne funktion i de to ting, er det ikke, vi har brug for en klasse

    class ListNeedsFives(object):
        def __init__(self, b=None):
            if b is None:
                b = []
            self.b = b
    
        def foo():
            self.b.append(5)

    Udført af

    a = ListNeedsFives()
    a.foo()
    a.b

    Og hvorfor er det bedre end moset alle ovenstående kode i en enkelt funktion.

    def dontdothis(b=None):
        if b is None:
            b = []
        b.append(5)
        return b

    Hvorfor ikke gøre det?

    Medmindre du ikke i dit projekt, din kode vil leve på. Mest sandsynligt, er din funktion vil være at gøre mere end dette. Den rigtige måde at gøre vedligeholdelsesvenlig kode er at adskille koden i atomare dele med en korrekt begrænset omfang.

    Konstruktøren af en klasse er et meget almindeligt anerkendt komponent for alle, der har gjort til Objekt-Orienteret Programmering. At placere den logik, der håndterer liste instantiering i konstruktøren gør den kognitive belastning af forståelse af, hvad koden betyder mindre.

    Metoden foo() ikke vender tilbage til den liste, hvorfor ikke?

    Tilbage i en stand alone-liste, vil du kunne antage, at det er sikkert at gøre, hvad nogensinde du har lyst til det. Men kan det ikke være, da det også er fælles med objektet a. Tvinger brugeren til at henvise til det som a.b minder dem om, hvor listen tilhører. Enhver ny kode, der ønsker at ændre a.b vil naturligt blive placeret i den klasse, hvor den hører til.

    den def dontdothis(b=None): signatur funktion har ingen af disse fordele.

    Jeg synes din kommentar “Kode, der er mere end bare en måde at styre adfærden af hardware” er fremragende, men at flade ud sige, at standard argumenter, er en kode, lugte virker lidt for dramatisk. Der er helt fint grunde til at have standard argumenter.
    Tja… jeg ærligt skrev dette svar, da jeg var i lidt af en mørk stemning… regnede på, hvad dælen, jeg kan bare buse ud med et rant om min mening. Spørgsmålet er ganske stædig alligevel… Så.. ja, tak for den ros, jeg tager skylden for at hjertet så godt om

    OriginalForfatteren firelynx

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *