2010-12-14 30 views
7

J'ai une fonction qui, dans les cas les plus simples, fonctionne sur un itérable d'éléments.Gérer élégamment différents types de paramètres

def foo(items): 
    for item in items: 
     # do stuff 

Parfois, je ne veux pas passer un itérable d'objets directement, mais plutôt un objet qui fournit une méthode pour obtenir les itératives:

def foo(obj): 
    for item in obj.iteritems(): 
     # do same stuff as above 

Je peux fusionner ces deux cas comme celui-ci :

def foo(obj): 
    try: 
     items = obj.iteritems() 
    except ValueError: 
     items = obj 
    for item in items: 
     # do stuff 

Cela fonctionne très bien. Maintenant, je reçois un troisième cas d'utilisation qui ressemble à ceci:

def foo(objs): 
    for item in itertools.chain.from_iterable(obj.iteritems() for obj in objs): 
     # do same stuff again 

je peux toujours utiliser l'approche try-except, étant donné que les interfaces sont incompatibles. Cependant, la prise d'essai imbriquée commencerait à devenir très laide. Plus encore quand je veux ajouter un quatrième cas d'utilisation. Est-il possible de résoudre ce problème sans imbriquer les blocs try?

+1

Ah, je * souhaite * qu'il y ait des surcharges de fonctions correctes en Python! –

+3

@ André: presque toujours en Python, si vous voulez ce type de surcharge de fonction/méthode, vous le faites mal. –

+0

Mais quel est le problème avec l'envoi explicite de l'itérable? trop verbeux? – tokland

Répondre

2

En l'état actuel, vous devriez probablement utiliser au moins deux méthodes ici, la troisième il suffit d'appeler le premier avec le résultat itertools.chain.from_iterable. Vous pourriez aussi potentiellement avoir une utilisation pour * args; cela dépend de votre cas précis (fournir un exemple réel est utile). Vous pouvez également utiliser une fonction d'assistance simple pour renvoyer le bon type d'itérateur.

Peut-être cela pourrait fonctionner:

def _foo_iter(obj): 
    try: 
     return obj.iteritems() 
    except AttributeError: 
     return obj 

def foo(*objs): 
    for obj in objs: 
     for item in _foo_iter(obj): 
+0

Le code que j'ai fourni est assez proche de la réalité, seuls les noms sont changés. –

+0

@ Space_C0wb0y: mais vous n'avez pas expliqué comment vous l'utilisez avec précision et pour quoi faire. Pour certaines choses, il pourrait y avoir un meilleur moyen qui ne vous est pas arrivé (cela arrive souvent, par ex.dans une question j'utilisais une fonction personnalisée quand 'x en y' aurait fait aussi bien mais pour une raison quelconque cela ne m'est pas venu à l'esprit) –

+0

La fonction est un générateur qui calcule une certaine propriété pour chacun des items. –

0

Pour vérifier si un objet est itérable:

import collections 
isinstance(obj, collections.Iterable) 

Cela devrait vous débarrasser de certains problèmes.

En outre, un moyen de gérer ces problèmes est à l'aide récursivité (ne sais pas si elle est applicable si):

def foo(obj): 
    if isinstance(obj, collections.Iterable): 
     for el in obj: 
      foo(el) 
    # now do stuff 
0

hasattr(object, name)

if hasattr(obj, '__iter__'): 
     return [o for o in obj] 
    elif hasattr(obj, 'iteritems'): 
     return [o for o in obj.iteritems()] 
    elif isinstance(obj, list) or isinstance(obj, tuple): 
     return [o.iteritems() for o in [o for o in obj]] 
    else: 
     raise TypeError 

class Bar(list): 
    def iteritems(self): 
     return self.__iter__() 

print foo([1,2,3]) 
print foo(Bar([1,2,3])) 
print foo([[1,2,3], [1,2,3]]) 
print foo(1) 

[1, 2, 3] 
[1, 2, 3] 
[[1, 2, 3], [1, 2, 3]] 
Traceback (most recent call last): 
    File "test.py", line 20, in <module> 
    print foo(1) 
    File "test.py", line 11, in foo 
    raise TypeError 
TypeError 
+0

'hasattr' est essentiellement juste un' try' autour de 'getattr'. –

+0

@chris Non, il ne force pas à imbriquer des exceptions, par exemple, il est plus lisible. Ce qui est ce que la question demande. –

1

suis d'accord avec Chris: la magie d'entrée comprend-tout va tourner autour et vous mordre. Si vous lui passez un itérable des objets-avec-itérables de itérations, comment spécifiez-vous à quel niveau commencer réellement le traitement des données? Il vaut mieux s'en tenir à "soit une liste ou un générateur" comme entrée, puis pré-traiter vos appels à la fonction.