web-dev-qa-db-fra.com

Pourquoi avez-vous explicitement besoin de l'argument "self" dans une méthode Python?

Lorsque vous définissez une méthode sur une classe en Python, cela ressemble à ceci:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

Mais dans d'autres langages, tels que C #, vous avez une référence à l'objet auquel la méthode est liée avec le mot clé "this" sans le déclarer en tant qu'argument dans le prototype de la méthode. 

S'agissait-il d'une décision de conception de langage intentionnelle en Python ou existe-t-il des détails d'implémentation nécessitant le passage de "self" comme argument?

180
Readonly

J'aime citer le Zen of Python de Peters. "Explicite est meilleur qu'implicite."

En Java et en C++, 'this.' peut être déduit, sauf lorsque vous avez des noms de variables qui rendent la déduction impossible. Donc, vous en avez parfois besoin et parfois pas.

Python choisit de rendre ce genre de choses explicite plutôt que basé sur une règle. 

De plus, puisque rien n'est implicite ou supposé, des parties de la mise en œuvre sont exposées. self.__class__, self.__dict__ et d'autres structures "internes" sont disponibles de manière évidente.

88
S.Lott

C'est pour minimiser la différence entre les méthodes et les fonctions. Il vous permet de générer facilement des méthodes dans des métaclasses ou d'ajouter des méthodes lors de l'exécution à des classes préexistantes.

par exemple.

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

Cela facilite également (autant que je sache) la mise en œuvre du runtime Python.

55
Ryan

Je suggère de lire le blog de Guido van Rossum à ce sujet - Pourquoi le moi explicite doit rester .

Quand une définition de méthode est décorée, nous ne savons pas s'il faut lui attribuer automatiquement un paramètre 'self': le décorateur pourrait transformer la fonction en une méthode statique (qui n'a pas de 'self'), ou une méthode de classe (qui a un drôle de soi qui fait référence à une classe plutôt qu’à une instance) ou peut faire quelque chose de complètement différent (c’est trivial d’écrire un décorateur qui implémente '@classmethod' ou '@staticmethod' en pur Python). Il est impossible de savoir ce que fait le décorateur s’il faut doter la méthode en cours de définition d’un argument implicite 'self' ou non.

Je rejette les hacks comme "@classmethod" et "@staticmethod".

51
bhadra

Python ne vous oblige pas à utiliser "self". Vous pouvez lui donner le nom que vous voulez. N'oubliez pas que le premier argument d'un en-tête de définition de méthode est une référence à l'objet.

16
Victor Noagbodji

Permet également de faire ceci: (en bref, invoquer Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5) retournera 12, mais le fera de la façon la plus folle qui soit.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Bien entendu, cela est plus difficile à imaginer dans des langages tels que Java et C #. En rendant explicite l'auto-référence, vous êtes libre de faire référence à un objet par cette auto-référence. En outre, une telle manière de jouer avec les classes au moment de l'exécution est plus difficile à utiliser dans les langages plus statiques - ce n'est pas forcément bon ou mauvais. C'est juste que le moi explicite permet à toute cette folie d'exister.

De plus, imaginez ceci: nous aimerions personnaliser le comportement des méthodes (pour le profilage, ou de la magie noire folle). Cela peut nous amener à penser: et si nous avions une classe Method dont nous pourrions outrepasser ou contrôler le comportement?

Eh bien la voici:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

Et maintenant: InnocentClass().magic_method() agira comme prévu. La méthode sera liée avec le paramètre innocent_self à InnocentClass et avec le magic_self à l'instance MagicMethod. Bizarre hein? C'est comme avoir 2 mots-clés this1 et this2 dans des langages tels que Java et C #. Une telle magie permet aux frameworks de faire des choses qui seraient autrement beaucoup plus verbeuses.

Encore une fois, je ne veux pas commenter l'éthique de ce genre de choses. Je voulais simplement montrer des choses qu'il serait plus difficile de faire sans une référence explicite à soi-même.

6
vlad-ardelean

Je pense que la vraie raison en plus de "Le zen de Python" est que les fonctions sont des citoyens de première classe en Python. 

Ce qui en fait essentiellement un objet. Maintenant, le problème fondamental est que si vos fonctions sont également des objets, dans paradigme orienté objet, comment enverriez-vous des messages à des objets lorsque les messages eux-mêmes sont des objets? 

Ressemble à un problème d’œuf de poule. Pour atténuer ce paradoxe, le seul moyen possible est de passer un contexte d’exécution aux méthodes ou de le détecter. Mais comme python peut avoir des fonctions imbriquées, il serait impossible de le faire car le contexte d'exécution changerait pour les fonctions internes. 

Cela signifie que la seule solution possible est de passer explicitement 'self' (le contexte d'exécution).

Je pense donc que c'est un problème de mise en œuvre, le Zen est venu beaucoup plus tard.

2
pankajdoharey

Je pense que cela a à voir avec PEP 227:

Les noms dans la portée de la classe ne sont pas accessibles. Les noms sont résolus dans le portée de la fonction englobante la plus interne. Si une définition de classe apparaît dans un chaîne d'étendues imbriquées, le processus de résolution ignore la classe définitions. Cette règle empêche les interactions étranges entre la classe attributs et accès aux variables locales. Si une opération de rattachement de nom se produit dans une définition de classe, il crée un attribut sur le résultat objet de classe. Pour accéder à cette variable dans une méthode ou dans une fonction imbriquée dans une méthode, une référence d'attribut doit être utilisée, que ce soit via soi-même ou via le nom de la classe.

2
daole

Comme expliqué dans self en Python, Demystified

n'importe quoi comme obj.meth (args) devient Class.meth (obj, args). Le processus d’appel est automatique alors que le processus de réception ne l’est pas (son explicite). C'est la raison pour laquelle le premier paramètre d'une fonction dans la classe doit être l'objet lui-même. 

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from Origin"""
        return (self.x**2 + self.y**2) ** 0.5

Invocations:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () définit trois paramètres mais nous en avons passé deux (6 et 8). De même, distance () nécessite un, mais aucun argument n'a été transmis. 

Pourquoi Python ne se plaint-il pas de cette incompatibilité de numéro d'argument}?

Généralement, lorsque nous appelons une méthode avec des arguments, la fonction de classe correspondante est appelée en plaçant l'objet de la méthode avant le premier argument. Donc, tout ce qui ressemble à obj.meth (args) devient Class.meth (obj, args). _ {Le processus d'appel est automatique alors que le processus de réception ne l'est pas (explicitement).

C’est la raison pour laquelle le premier paramètre d’une fonction de la classe doit être l’objet même. L’écriture de ce paramètre en tant que self n’est qu’une convention. Ce n'est pas un mot clé et n'a aucune signification particulière en Python. Nous pourrions utiliser d'autres noms (comme celui-ci) mais je vous suggère fortement de ne pas le faire. L'utilisation de noms autres que self est mal vue par la plupart des développeurs et dégrade la lisibilité du code ("Readability count") . 
...
Dans l'exemple ci-dessus, self.x est un attribut d'instance, tandis que x est une variable locale. Ils ne sont pas identiques et se trouvent dans des espaces de noms différents.

Le moi est là pour rester

Beaucoup ont proposé de faire de self un mot-clé en Python, comme ceci en C++ et en Java. Cela éliminerait l'utilisation redondante de self explicite de la liste des paramètres formels dans les méthodes. Cette idée semble prometteuse, mais elle ne va pas se passer. Du moins pas dans un proche avenir. La raison principale est la compatibilité descendante. Voici un blog du créateur de Python lui-même expliquant pourquoi le moi explicite doit rester.

0
mon