Cum pot apela o funcție de două sau mai multe ori consecutiv? (Programare, Python, Funcție, Scurtătură, Secvențial)

alwbtc a intrebat.

Există o modalitate scurtă de a apela o funcție de două sau mai multe ori consecutiv în Python? De exemplu:

do()
do()
do()

poate ca:

3*do()

Comentarii

  • Rețineți că 3 * do() este o expresie Python validă cu un rezultat foarte bine definit: aceasta multiplică valoarea de returnare a apelării lui do o singură dată cu 3. Ar fi posibil, totuși, să se scrie un decorator care să permită scrierea unor lucruri precum (3 * do)() – cu o variantă de răspuns la stackoverflow.com/questions/8998997/product-of-two-functions/… – -.  > Por jsbueno.
  • Am ajuns aici pentru că mă întrebam dacă există o modalitate de a face acest lucru similar cu ceea ce ați sugerat… Probabil că nu este suficient de folosit pentru a justifica un nou „operator de multiplicare a apelului de funcție”, totuși, având în vedere că utilizarea acestuia în loc de o buclă for economisește doar aproximativ 17 taste. –  > Por Chelonian.
  • Adăugarea de zahăr sintactic într-un limbaj de programare precum 3*do() nu este niciodată un lucru bun. Va cauza o mulțime de probleme și probleme mai târziu, fără a mai menționa că are un înțeles total diferit în acest context. –  > Por tonga.
  • întotdeauna este interesant să vezi opiniile diferite despre zahărul sintactic.. Unora nu le place, alții îl acceptă. avem opinia python (cel puțin de la o voce) de mai sus… între timp, în ruby aveți 2.times do {block} (NU este o încercare de a face un argument „ruby este mai bun”, ci doar de a observa diferențele în modul în care acest lucru este privit de diferite comunități de codare) – –  > Por Chuck van der Linden.
9 răspunsuri
Greg Hewgill

Eu aș face-o:

for _ in range(3):
    do()

The _ este o convenție pentru o variabilă a cărei valoare nu vă interesează.

S-ar putea, de asemenea, să vedeți că unii oameni scriu:

[do() for _ in range(3)]

însă acest lucru este puțin mai costisitor, deoarece creează o listă care conține valorile de retur ale fiecărei invocări a lui do() (chiar dacă este None), iar apoi aruncă lista rezultată. Nu v-aș sugera să folosiți acest lucru decât dacă sunteți folosiți lista de valori de returnare.

Comentarii

    18

  • Cred că nu este niciodată o idee bună să folosiți o înțelegere de listă doar pentru a repeta acțiuni –  > Por juliomalegria.
  • Este adevărat. Ar trebui să fiu mai explicit că nu sunt de fapt recomand asta. –  > Por Greg Hewgill.
  • Da, List Comprehensions sunt menite să creeze liste noi și nu ar trebui să fie utilizate pentru efecte secundare. –  > Por g.d.d.d.c.
  • Dacă vă interesează o agregare a rezultatelor, atunci o înțelegere de generator în interiorul funcției de agregare (cum ar fi sum de exemplu) funcționează foarte bine: sum(do() for _ in range(3)) sau sum(1 for _ in range(3) if do() > 3) pentru numărare condiționată etc. –  > Por flutefreak7.
juliomalegria

Ai putea defini o funcție care să repete funcția trecută de N ori.

def repeat_fun(times, f):
    for i in range(times): f()

Dacă vrei să o faci și mai flexibilă, poți chiar să treci argumente funcției care se repetă:

def repeat_fun(times, f, *args):
    for i in range(times): f(*args)

Utilizare:

>>> def do():
...   print 'Doing'
... 
>>> def say(s):
...   print s
... 
>>> repeat_fun(3, do)
Doing
Doing
Doing
>>> repeat_fun(4, say, 'Hello!')
Hello!
Hello!
Hello!
Hello!

VDV

Încă trei moduri de a face acest lucru:

(I) Cred că utilizarea map poate fi, de asemenea, o opțiune, deși necesită generarea unei liste suplimentare cu Nones în unele cazuri și are întotdeauna nevoie de o listă de argumente:

def do():
    print 'hello world'

l=map(lambda x: do(), range(10))

(II) itertools conțin funcții care pot fi utilizate pentru a itera și prin alte funcții https://docs.python.org/2/library/itertools.html

(III) Utilizarea listelor de funcții nu a fost menționată până acum cred (și este de fapt cea mai apropiată ca sintaxă de cea discutată inițial) :

it=[do]*10
[f() for f in it]

Sau, ca o singură replică:

[f() for f in [do]*10]

g.d.d.d.c

O simplă buclă for?

for i in range(3):
  do()

Sau, dacă te interesează rezultatele și vrei să le colectezi, cu bonusul de a fi un 1 liner:

vals = [do() for _ in range(3)]

Comentarii

  • for-loop ar putea fi, de asemenea, 1 liner: for i in range(3): do() –  > Por juliomalegria.
  • @julio.alegria one liners sunt considerate o practică proastă în Python – ghidurile de stil recomandă clar să nu se folosească. –  > Por Gareth Latty.
  • @Lattyware Știu asta, am vrut doar să arăt că o buclă for-loop poate avea și ea bonusul de a fi un one liner –  > Por juliomalegria.
  • de ce sunt considerate one-liners ca fiind o practică proastă? –  > Por alwbtc.
  • @alwbtc Ele sunt mai puțin clare pentru cititor – care este una dintre considerațiile principale ale ghidurilor de stil python, alături de faptul că sunt mai greu de adăugat ulterior. A se vedea python.org/dev/peps/pep-0008 –  > Por Gareth Latty.
cjerdonek

Iată o abordare care nu necesită utilizarea unui for buclă sau definirea unei funcții intermediare sau a unei funcții lambda (și este, de asemenea, un one-liner). Metoda combină următoarele două idei:

Punând acestea împreună, obținem:

next(islice(iter(do, object()), 3, 3), None)

(Ideea de a trece object() ca santinelă provine din acest răspuns acceptat de Stack Overflow).

Și iată cum arată acest lucru din promptul interactiv:

>>> def do():
...   print("called")
... 
>>> next(itertools.islice(iter(do, object()), 3, 3), None)
called
called
called

Joffer

Cei doi cenți ai mei:

from itertools import repeat 

list(repeat(f(), x))  # for pure f
[f() for f in repeat(f, x)]  # for impure f

Comentarii

  • Ați putea să detaliați puțin diferența dintre pur și impur în acest context ? –  > Por Dl_și_doamna_D.
  • sigur – să spunem că aveți o funcție „impură” care doar tipărește și nu face nimic altceva: dacă doriți să tipărească de x ori, veți folosi a doua abordare; prima ar duce la o singură tipărire. Pe de altă parte, dacă aveți o funcție pură, care nu execută efecte secundare, ați putea folosi oricare dintre abordări, dar prima ar duce la o singură aplicare a funcției, ceea ce ar putea fi semnificativ mai ieftin. –  > Por Joffer.
  • list(repeat(f(), x)) nu repetă apelul funcției, ci rezultatul funcției (așa cum s-a menționat în explicația despre „f pură/impură”). Prin urmare, nu este ceea ce a cerut OP (care a fost apelarea funcției de mai multe ori). –  > Por rlat.
  • @rlat adevărat, dar, din nou, de ce să o apelezi de mai multe ori dacă va da întotdeauna același rezultat și nu are efecte secundare? –  > Por Joffer.
  • … stai, pe ce internet sunt eu? –  > Por Joffer.
Domnul_și_doamna_D
from itertools import repeat, starmap

results = list(starmap(do, repeat((), 3)))

Vezi repeatfunc din modulul itertools, care este de fapt mult mai puternică. Dacă aveți nevoie doar să apelați metoda, dar nu vă pasă de valorile de returnare, o puteți utiliza într-o buclă for:

for _ in starmap(do, repeat((), 3)): pass

dar asta devine urât.

Alphard

Puteți încerca bucla while, așa cum se arată mai jos;

def do1():
    # Do something

def do2(x):
    while x > 0:
        do1()
        x -= 1

do2(5)

Astfel, faceți apelul funcției do1 de 5 ori.

Georgy

Puteți folosi itertools.repeat cu operator.methodcaller pentru a apela funcția __call__ a funcției N ori. Iată un exemplu de funcție generatoare care face acest lucru:

from itertools import repeat
from operator import methodcaller


def call_n_times(function, n):
    yield from map(methodcaller('__call__'), repeat(function, n))

Exemplu de utilizare:

import random
from functools import partial

throw_dice = partial(random.randint, 1, 6)
result = call_n_times(throw_dice, 10)
print(list(result))
# [6, 3, 1, 2, 4, 6, 4, 1, 4, 6]