J'ai récemment joué avec Python, et une chose que je trouve un peu étrange est l'utilisation intensive de `` méthodes magiques '', par exemple pour rendre sa longueur disponible, un objet implémente une méthode, def __len__(self)
puis il est appelé lorsque vous écrivez len(obj)
.
Je me demandais simplement pourquoi les objets ne définissent pas simplement une len(self)
méthode et ne la font pas appeler directement en tant que membre de l'objet, par exemple obj.len()
? Je suis sûr qu'il doit y avoir de bonnes raisons pour que Python le fasse comme il le fait, mais en tant que débutant, je n'ai pas encore compris ce qu'ils sont.
python
magic-methods
Greg Beech
la source
la source
len()
oureversed()
s'applique à de nombreux types d'objets, mais une méthode telle queappend()
ne s'applique qu'aux séquences, etc.Réponses:
AFAIK,
len
est spécial à cet égard et a des racines historiques.Voici une citation de la FAQ :
Les autres «méthodes magiques» (en fait appelées méthode spéciale dans le folklore Python) ont beaucoup de sens, et des fonctionnalités similaires existent dans d'autres langages. Ils sont principalement utilisés pour le code qui est appelé implicitement lorsqu'une syntaxe spéciale est utilisée.
Par exemple:
etc...
la source
Du Zen de Python:
Ceci est l' une des raisons - avec des méthodes personnalisées, les développeurs seraient libres de choisir un nom de méthode différente, comme
getLength()
,length()
,getlength()
ou que ce soit. Python applique une dénomination stricte afin que la fonction communelen()
puisse être utilisée.Toutes les opérations qui sont communes pour de nombreux types d'objets sont mis en méthodes magiques, comme
__nonzero__
,__len__
ou__repr__
. Cependant, ils sont pour la plupart facultatifs.La surcharge des opérateurs est également effectuée avec des méthodes magiques (par exemple
__le__
), il est donc logique de les utiliser également pour d'autres opérations courantes.la source
Python utilise le mot «méthodes magiques» , car ces méthodes font vraiment de la magie pour votre programme. L'un des plus grands avantages de l'utilisation des méthodes magiques de Python est qu'elles fournissent un moyen simple de faire en sorte que les objets se comportent comme des types intégrés. Cela signifie que vous pouvez éviter les méthodes laides, contre-intuitives et non standard d'exécuter des opérateurs de base.
Prenons l'exemple suivant:
Cela donne une erreur, car le type de dictionnaire ne prend pas en charge l'ajout. Maintenant, étendons la classe de dictionnaire et ajoutons la méthode magique "__add__" :
Maintenant, il donne la sortie suivante.
Ainsi, en ajoutant cette méthode, la magie s'est soudainement produite et l'erreur que vous aviez plus tôt a disparu.
J'espère que cela vous rend les choses claires. Pour plus d'informations, reportez-vous à:
Un guide des méthodes magiques de Python (Rafe Kettler, 2012)
la source
Certaines de ces fonctions font plus qu'une seule méthode pourrait implémenter (sans méthodes abstraites sur une superclasse). Par exemple,
bool()
agit un peu comme ceci:Vous pouvez également être sûr à 100% que
bool()
retournera toujours Vrai ou Faux; si vous vous fiez à une méthode, vous ne pouviez pas être entièrement sûr de ce que vous obtiendriez.Certaines autres fonctions qui ont des implémentations relativement compliquées (plus compliquées que les méthodes magiques sous-jacentes sont susceptibles de l'être) sont
iter()
etcmp()
, et toutes les méthodes attributaires (getattr
,setattr
etdelattr
). Des choses commeint
accéder également aux méthodes magiques lors de la coercition (vous pouvez implémenter__int__
), mais font double emploi en tant que types.len(obj)
est en fait le seul cas dont je ne pense pas que ce soit jamais différentobj.__len__()
.la source
hasattr()
j'utiliseraistry:
/except AttributeError:
et au lieu deif obj.__len__(): return True else: return False
je dirais simplement,return obj.__len__() > 0
mais ce ne sont que des choses stylistiques.bool(x)
faisait référencex.__nonzero__()
), votre méthode ne fonctionnerait pas. Les instances booléennes ont une méthode__nonzero__()
et votre code continuerait de s'appeler une fois que obj était un booléen. Devraitbool(obj.__bool__())
peut-être être traité de la même manière que vous avez traité__len__
? (Ou ce code fonctionne-t-il réellement pour Python 3?)len(x)
etx.__len__()
est que le premier lèvera OverflowError pour les longueurs qui dépassentsys.maxsize
, tandis que le dernier ne le fera généralement pas pour les types implémentés en Python. C'est plus un bogue qu'une fonctionnalité, cependant (par exemple, l'objet range de Python 3.2 peut principalement gérer des plages arbitrairement grandes, mais leur utilisationlen
peut échouer. Leur__len__
échec également, car ils sont implémentés en C plutôt qu'en Python)Ce ne sont pas vraiment des "noms magiques". C'est juste l'interface qu'un objet doit implémenter pour fournir un service donné. En ce sens, ils ne sont pas plus magiques que n'importe quelle définition d'interface prédéfinie que vous devez réimplémenter.
la source
Bien que la raison soit principalement historique, il y a certaines particularités dans Python
len
qui rendent l'utilisation d'une fonction au lieu d'une méthode appropriée.Certaines opérations en Python sont implémentées en tant que méthodes, par exemple
list.index
etdict.append
, tandis que d'autres sont implémentées en tant que méthodes appelables et magiques, par exemplestr
etiter
etreversed
. Les deux groupes diffèrent suffisamment pour que l'approche différente soit justifiée:str
,int
et les amis sont des types. Il est plus logique d'appeler le constructeur.iter
peut appeler__getitem__
si__iter__
n'est pas disponible et prend en charge des arguments supplémentaires qui ne rentrent pas dans un appel de méthode. Pour la même raison,it.next()
a été changé ennext(it)
les versions récentes de Python - cela a plus de sens.__iter__
et__next__
- ça s'appelle lafor
boucle. Par souci de cohérence, une fonction est meilleure. Et cela le rend meilleur pour certaines optimisations.repr
agissent commestr
si. Avoirstr(x)
contrex.repr()
serait déroutant.isinstance
.getattr(x, 'a')
c'est une autre façon de fairex.a
etgetattr
partagent plusieurs des qualités susmentionnées.J'appelle personnellement le premier groupe de type méthode et le second groupe de type opérateur. Ce n'est pas une très bonne distinction, mais j'espère que cela aide d'une manière ou d'une autre.
Cela dit,
len
ne rentre pas exactement dans le deuxième groupe. C'est plus proche des opérations du premier, à la seule différence que c'est beaucoup plus courant que presque n'importe lequel d'entre eux. Mais la seule chose qu'il fait, c'est d'appeler__len__
, et c'est très procheL.index
. Cependant, il existe quelques différences. Par exemple,__len__
peut être appelé pour l'implémentation d'autres fonctionnalités, par exemplebool
, si la méthode a été appelée,len
vous pouvez romprebool(x)
avec unelen
méthode personnalisée qui fait des choses complètement différentes.En bref, vous avez un ensemble de fonctionnalités très courantes que les classes pourraient implémenter et qui pourraient être accessibles via un opérateur, via une fonction spéciale (qui fait généralement plus que l'implémentation, comme le ferait un opérateur), lors de la construction d'objet, et toutes partagent certains traits communs. Tout le reste est une méthode. Et
len
c'est en quelque sorte une exception à cette règle.la source
Il n'y a pas grand chose à ajouter aux deux articles ci-dessus, mais toutes les fonctions "magiques" ne sont pas vraiment magiques du tout. Ils font partie du module __ builtins__ qui est implicitement / automatiquement importé au démarrage de l'interpréteur. C'est à dire:
se produit chaque fois avant le démarrage de votre programme.
J'ai toujours pensé que ce serait plus correct si Python ne faisait cela que pour le shell interactif et exigeait des scripts pour importer les différentes parties à partir des composants intégrés dont ils avaient besoin. Une gestion __ main__ probablement différente serait également intéressante dans les shells vs interactifs. Quoi qu'il en soit, consultez toutes les fonctions et voyez à quoi cela ressemble sans elles:
la source