web-dev-qa-db-fra.com

Génériques / modèles en python?

Comment python gère-t-il les scénarios de type générique/modèle? Disons que je veux créer un fichier externe "BinaryTree.py" et le faire gérer les arbres binaires, mais pour tout type de données.

Je pouvais donc lui passer le type d'un objet personnalisé et avoir un arbre binaire de cet objet. Comment cela se fait-il en python?

60
keys

Python utilise typage canard , il n'a donc pas besoin de syntaxe spéciale pour gérer plusieurs types.

Si vous venez d'un arrière-plan C++, vous vous souviendrez que, tant que les opérations utilisées dans la fonction/classe de modèle sont définies sur un type T (au niveau de la syntaxe), vous pouvez utiliser ce type T dans le modèle.

Donc, fondamentalement, cela fonctionne de la même manière:

  1. définir un contrat pour le type d'éléments que vous souhaitez insérer dans l'arborescence binaire.
  2. documenter ce contrat (c'est-à-dire dans la documentation de la classe)
  3. implémenter l'arbre binaire en utilisant uniquement les opérations spécifiées dans le contrat
  4. prendre plaisir

Vous remarquerez cependant qu'à moins d'écrire une vérification de type explicite (ce qui est généralement déconseillé), vous ne pourrez pas imposer qu'un arbre binaire ne contienne que des éléments du type choisi.

59
André Caron

En fait, vous pouvez maintenant utiliser des génériques dans Python 3.5+. Voir PEP-484 et documentation de la bibliothèque de saisie .

Selon ma pratique, ce n'est pas très transparent et clair, surtout pour ceux qui connaissent les Java Generics, mais toujours utilisables.

16
8bitjoey

Après avoir trouvé quelques bonnes idées sur la création de types génériques en python, j'ai commencé à chercher d'autres qui avaient la même idée, mais je n'en ai pas trouvé. Alors voilà. J'ai essayé et ça marche bien. Il nous permet de paramétrer nos types en python.

class List( type ):

        def __new__( type_ref, member_type ):

            class List( list ):

                def append( self, member ):

                    if not isinstance( member, member_type ):
                        raise TypeError( 'Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
                            type( member ).__name__,
                            type( self ).__name__,
                            member_type.__) )

                    list.append( self, member )

            return List 

Vous pouvez désormais dériver des types de ce type générique.

class TestMember:
        pass

class TestList( List( TestMember ) ):

    def __init__( self ):
        super().__init__()


test_list = TestList()
test_list.append( TestMember() )
test_list.append( 'test' ) # This line will raise an exception

Cette solution est simpliste et a ses limites. Chaque fois que vous créez un type générique, il crée un nouveau type. Ainsi, plusieurs classes héritant de List( str ) en tant que parent hériteraient de deux classes distinctes. Pour surmonter cela, vous devez créer un dict pour stocker les différentes formes de la classe interne et renvoyer la classe interne créée précédemment, plutôt que d'en créer une nouvelle. Cela empêcherait la création de types en double avec les mêmes paramètres. Si vous êtes intéressé, une solution plus élégante peut être faite avec des décorateurs et/ou des métaclasses.

8
Ché on The Scene

Puisque python est typé dynamiquement, c'est super facile. En fait, vous devrez faire un travail supplémentaire pour que votre classe BinaryTree ne fonctionne avec aucun type de données.

Par exemple, si vous souhaitez que les valeurs clés utilisées pour placer l'objet dans l'arborescence soient disponibles dans l'objet à partir d'une méthode comme key() vous appelez simplement key() sur les objets. Par exemple:

class BinaryTree(object):

    def insert(self, object_to_insert):
        key = object_to_insert.key()

Notez que vous n'avez jamais besoin de définir le type de classe object_to_insert. Tant qu'il a une méthode key(), cela fonctionnera.

L'exception est si vous voulez qu'il fonctionne avec des types de données de base comme des chaînes ou des entiers. Vous devrez les envelopper dans une classe pour les faire fonctionner avec votre BinaryTree générique. Si cela vous semble trop lourd et que vous voulez l'efficacité supplémentaire de simplement stocker des chaînes, désolé, ce n'est pas le cas Python est bon.

3
Leopd

Parce que Python est typé dynamiquement, les types d'objets n'ont pas d'importance dans de nombreux cas. C'est une meilleure idée d'accepter n'importe quoi.

Pour démontrer ce que je veux dire, cette classe d'arbre acceptera n'importe quoi pour ses deux branches:

class BinaryTree:
    def __init__(self, left, right):
        self.left, self.right = left, right

Et cela pourrait être utilisé comme ceci:

branch1 = BinaryTree(1,2)
myitem = MyClass()
branch2 = BinaryTree(myitem, None)
tree = BinaryTree(branch1, branch2)
2
Andrea

Regardez comment les conteneurs intégrés le font. dict et list et ainsi de suite contiennent des éléments hétérogènes de tous les types que vous aimez. Si vous définissez, par exemple, une fonction insert(val) pour votre arbre, elle fera à un moment donné quelque chose comme node.value = val et Python s'occupe du reste.

1
John Zwinck

Heureusement, il y a eu quelques efforts pour la programmation générique dans python. Il y a une bibliothèque: generic

Voici la documentation pour cela: http://generic.readthedocs.org/en/latest/

Il n'a pas progressé au fil des ans, mais vous pouvez avoir une idée approximative de l'utilisation et de la création de votre propre bibliothèque.

À votre santé

1
igaurav

Si vous utilisez Python 2 ou si vous souhaitez réécrire Java. Leur solution n'est pas réelle pour cela. Voici ce que je travaille en une nuit: - https://github.com/FlorianSteenbuck/python-generics Je n'ai toujours pas de compilateur donc vous l'utilisez actuellement comme ça:

class A(GenericObject):
    def __init__(self, *args, **kwargs):
        GenericObject.__init__(self, [
            ['b',extends,int],
            ['a',extends,str],
            [0,extends,bool],
            ['T',extends,float]
        ], *args, **kwargs)

    def _init(self, c, a, b):
        print "success c="+str(c)+" a="+str(a)+" b="+str(b)

TODO

  • Compilateur
  • Faire fonctionner les classes et types génériques (pour des choses comme <? extends List<Number>>)
  • Ajouter super support
  • Ajouter ? soutien
  • Nettoyage de code
1
Florian Steenbuck