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?
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:
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.
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.
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.
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.
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)
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.
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é
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
<? extends List<Number>>
)super
support?
soutien