IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

fonction non itérable


Sujet :

Python

  1. #1
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut fonction non itérable
    Bonsoir,

    J'essaye de rendre une fonction directement iterable. Le code ci-dessous marche pour une instance mais pas pour une fonction ! Pourquoi ? Voyez vous comment faire ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    def iterbug():
        ipipo= lambda: iter((1,))
        class X:pass
        x= X()
        x.__iter__= ipipo
        iter(x) # ca marche
        def f():pass
        f.__iter__= ipipo
        iter(f) # TypeError: 'function' object is not iterable
    Merci d'avance

  2. #2
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Je ne crois pas que rendre une fonction iterable ait beaucoup de sens… Je crois qu’il vaut mieux soit créer un objet “callable” qui soit aussi iterable, soit faire de la fonction un générateur (avec le mot-clé yield)!

  3. #3
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 072
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 072
    Par défaut
    Je plussoie mont29, j'allais répondre la même chose, on peut itérer soit des conteneurs, soit des générateurs, voir la documentation...

  4. #4
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    Je ne crois pas que rendre une fonction iterable ait beaucoup de sens
    Pourquoi ? Quelle différence avec le callable iterable que tu suggères ?

    Dans mon cas, j'ai un framework qui itère récursivement sur des "taches" renvoyant des "taches" (par exemple des generateurs). Pouvoir inclure les fonctions dans ce framework d'itération a pour moi du sens. Cela étant, je reconnais que je pourrai transformer mon framework d'iterables en un framework de callable (et y inclure des iterable via leur .next)

    Quoi qu'il en soit, j'ai un objet qui supporte le protocole iterable et que python ne reconnait pas comme tel. Comment ? Pourquoi ? Le protocole est il restreint aux instances ? coin-coin ?

  5. #5
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 072
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 072
    Par défaut
    C'est logique, x est une instance d'une classe créée par tes soins, où tu implémentes une méthode spéciale __iter__.

    Seulement une fonction est un objet spécifique python qui n'est pas créer avec la méthode spéciale __iter__.

    Si tu veux créér un objet représentant une fonction sur mesure, il faudra là fabriquer sans doute avec la méthode __call__.

  6. #6
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    Je ne trouve pas cela si logique ! Je n'ai pas vraiment implémenté une méthode __iter__, j'ai juste mis un attribut __iter__ (différent des autres méthodes spéciales nécessitant une vrai méthode et pas juste un attribut). Une fonction n'est pas un objet ? La doc précise
    objects of any classes you define with an __iter__() or __getitem__()
    Je pensais que le duck typing était la règle pour savoir ce qui est itérable ou pas en Python. Ce n'est donc pas aussi simple que hasattr(x,'__iter__') !

    Merci à tous les deux pour vos réponses !

  7. #7
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Les fonctions sont des objets très particuliers, qui ne sont pas sensés contenir de données. Donc itérer dessus n’a pas de sens, et l’interpréteur considère que cette classe (function) n’est pas iterable (pas plus que ne le sera un int, un float, etc.). La seule différence, c’est que tu peux attribuer des membres arbitraires à une fonction, alors que c’est impossible pour les types basiques comme int ou float.

  8. #8
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    qui ne sont pas sensés contenir de données
    Elle peut avoir des attributs ! Et quand bien même elle n'en contient pas, elle en renvoie (d'ailleurs un générateur ne contient pas de données non plus!)
    Du coup, comment déterminer en Python si un objet est itérable ?

  9. #9
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 072
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 072
    Par défaut
    Voilà un exemple constituant un objet callable.

    Je ne sais pas si c'est cela que tu cherches à faire, mais je vois rien d'autre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    >>> class A(object):
    ...     def __init__(self, function):
    ...         self.func = function
    ...     def __call__(self, *args):
    ...         self.args = args
    ...         return self.func(*self.args)
    ...     def __iter__(self):
    ...         return iter(self.args)
    ... 
    >>> a = A(lambda x:x)
    >>> a(12)
    12
    >>> iter(a)
    <tupleiterator object at 0xb7441ccc>
    >>> iter(a).next()
    12

  10. #10
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    C'est a peu près ce que je cherche à faire, avec la méthode __iter__
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    def __iter__(self):
        return iter(itertools.starmap(self.__call__,((),)))

  11. #11
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 072
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 072
    Par défaut
    Un peu dans ce genre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class A:
        def __init__(self, func):
            self.func = func
        def __call__(self, *args):
            self.args = args
            for arg in self.args:
                yield self.func(arg)
        def __iter__(self):
            return iter(self.__call__(*self.args))
     
    a = A(lambda x:x)
    iterator = iter(a(12, 5, 9))
    for val in iterator:
        print val

  12. #12
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    Oui, je veux transformer une fonction en un générateur qui yield une fois f().

    Mais ce sont surtout les performances qui m'inquiètent : un __call__ + un générateur complètement inutile car on appelle la fonction une fois (même si on l'appelait plusieurs fois, ça serait à l'identique, sans avoir besoin d'en conserver la mémoire interne)...

    iter(itertools.starmap(f,((),))) sera infiniment plus rapide que la generatorfunction A(f).__call__() que tu proposes; surtout si f est builtin

  13. #13
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 072
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 072
    Par défaut
    itertools.starmap(f,((),)) est strictement identique à ma méthode spéciale __call__, la résultante est un générateur.

    iter(itertools.starmap(f,((),)), on en revient strictement au même que iter(self.__call__(*self.args)), seulement oui la fonction iter() est inutile dans un sens comme dans l'autre, car on l'itère avec une boucle for ou un next().

    Du coup si tu dégages ta fonction iter() et ma méthode spéciale __iter__, le résultat sera strictement identique.

    Sois tu crées un itérateur, soit un générateur, mais pas les deux...

    Si tu le fais sur ma méthode spéciale __iter__, dans ce cas on écrase la fonction next pour la mettre à sa sauce.

    Si tu veux un générateur, tu utilises le mot clé yield.

  14. #14
    Membre Expert
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Par défaut
    Je n'ai pas l'impression !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    import time
    from collections import *
    from itertools import *
    class A:
        def __init__(self, func):
            self.func = func
        def __call__(self, *args):
            self.args = args
            for arg in self.args:
                yield self.func(arg)
    f= object #builtin fonction à itérer
     
    N= 10000
    t0= time.clock()
    [deque(A(f)(),maxlen=0) for k in xrange(N)]
    t1= time.clock()
    print "class", t1-t0
    t0= time.clock()
    [deque(starmap(f,((),)),maxlen=0) for k in xrange(N)]
    t1= time.clock()
    print "starmap" t1-t0
    N= 10000
    class 0.0178
    starmap 0.0099

    N= 1000000
    class 2.15
    starmap 1.27
    Et même sans ré-instancier A(f) à chaque fois, ça n'est pas équivalent. Tu payes forcément l'accès à l'attribut func et le wrappage dans __call__ + le fait que ce soit un generateur

    Par contre je n'arrive pas à faire marcher
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class B:
        def __init__(self, func):
            self.func = func
        def __iter__(self):
            return self        
        def next(self):
            return self.func()
    from functools import partial
    exhaust= partial(deque,maxlen=0)
    [exhaust(iter(B(f))) for k in xrange(N)]

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [LDAP] Fonctions non reconnues
    Par Wormus dans le forum Bibliothèques et frameworks
    Réponses: 3
    Dernier message: 10/01/2006, 13h46
  2. fonction non définie
    Par ston dans le forum Access
    Réponses: 11
    Dernier message: 12/12/2005, 16h02
  3. Verifier formulaire - fonction non appelée
    Par nerick dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 08/12/2005, 17h44
  4. [Configuration] Installation de PHP + Pbm de fonctions non reconnues
    Par BARBIER dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 12
    Dernier message: 23/11/2005, 10h54
  5. Le linker ignore les fonctions non implémentées
    Par Rodrigue dans le forum C++Builder
    Réponses: 5
    Dernier message: 02/03/2005, 13h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo