Python: la définition de nouvelles fonctions à la volée en utilisant « avec »

StackOverflow https://stackoverflow.com/questions/2200026

  •  18-09-2019
  •  | 
  •  

Question

Je veux convertir le code suivant:

...
urls = [many urls]
links = []
funcs = []
for url in urls:
   func = getFunc(url, links)
   funcs.append(func)
...

def getFunc(url, links):
   def func():
      page = open(url)
      link = searchForLink(page)
      links.append(link)
   return func

dans le code beaucoup plus pratique:

urls = [many urls]
links = []
funcs = []
for url in urls:
   <STATEMENT>(funcs):
        page = open(url)
        link = searchForLink(page)
        links.append(link)

J'espérais faire avec la déclaration de with. Comme je l'ai fait remarquer ci-dessous, j'espérais réaliser:

def __enter__():
    def func():

..code in the for loop..

def __exit__():
  funcs.append(func)

Bien sûr, cela ne fonctionne pas.

Liste compréhensions n'est pas bon pour les cas ont été les searchForLink d'action est non seulement une fonction, mais de nombreuses fonctions. Il se transformerait en un code extrêmement illisible. Par exemple, même ce serait un problème avec la liste compréhensions:

for url in urls:
  page = open(url)
  link1 = searchForLink(page)
  link2 = searchForLink(page)
  actionOnLink(link1)
  actionOnLink(link2)
  .... many more of these actions...
  links.append(link1)
Était-ce utile?

La solution

Un peu de l'ordinaire, mais vous pouvez avoir un décorateur et enregistrer le func lier toutes les variables de boucle comme arguments par défaut:

urls = [many urls]
links = []
funcs = []

for url in urls:
    @funcs.append
    def func(url=url):
        page = open(url)
        link = searchForLink(page)
        links.append(link)

Autres conseils

Il est absurde d'utiliser with ici. Utilisez plutôt une compréhension de la liste:

funcs = [getFunc(url, links) for url in urls]

Il n'y a que deux façons de créer des fonctions: def et lambda. Lambdas sont destinés à de petites fonctions, de sorte qu'ils ne peuvent pas être très approprié pour votre cas. Cependant, si vous voulez vraiment, vous pouvez joindre deux lambdas dans l'autre:

urls = [many urls]
links = []
funcs = [(lambda x:
            lambda:
              links.append(searchForLink(open(x))))(u)
         for u in urls]

Un peu trop à mon goût lispien.

perdre la <STATEMENT>(funcs): de ligne

Edit:

Je veux dire: pourquoi voudriez-vous faire? Pourquoi définir une nouvelle fonction pour chaque page? Pourquoi ne pas le faire cela?

urls = [many urls]
links = []
for url in urls:
    page = open(url)
    link = searchForLink(page)
    links.append(link) 

Vous ne devriez pas utiliser « avec » pour le faire (même si, étant donné que c'est Python, vous avez presque certainement pu, en utilisant une étrange effet secondaire et la dynamicism Python).

Le but de « avec » en Python est, comme décrit dans les docs , « pour envelopper l'exécution d'un bloc avec des méthodes définies par un gestionnaire de contexte. Cela permet à essayer ... sauf ... commun, enfin, des modèles d'utilisation à encapsuler pour une réutilisation pratique. »

Je pense que vous confondez "avec" le Javascript / VisualBasic « avec », qui peut être cosmétiquement similaire, mais qui est effectivement sans rapport.

Le bon vieux itertools .

from itertools import imap
links.extend(imap(searchForLink, imap(open, urls)))

Bien que, peut-être préférez-vous fonctionnelle.

from functional import *
funcs = [partial(compose(compose(links.append, searchForLink), open), url) for url in urls]
for func in funcs: func()

Je ne pense pas que cela vaut la peine de créer une classe pour un usage with: il est plus de travail pour créer et __enter__ __exit__ que d'écrire simplement une fonction d'aide

.

Vous pouvez mieux utiliser des générateurs pour réaliser le calcul retardé que vous êtes après.

def MakeLinks(urls):
    for url in urls:
        page = open(url)
        link = searchForLink(page)
        yield link

links = MakeLinks(urls)

Si vous voulez des liens:

for link in links:
    print link

Les urls seront examinés au cours de cette boucle, et non à la fois (ce qui vous ressemble êtes Tring à éviter).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top