Comment éviter le «soi» explicite en Python?

131

J'ai appris Python en suivant quelques tutoriels pygame .

J'y ai trouvé une utilisation intensive du mot-clé self , et venant d'un fond principalement Java, je constate que j'oublie toujours de taper self . Par exemple, au lieu de self.rect.centerxje taperais rect.centerx, car, pour moi, rect est déjà une variable membre de la classe.

Le parallèle Java auquel je peux penser pour cette situation est de devoir préfixer toutes les références aux variables membres avec ceci .

Suis-je bloqué en préfixant toutes les variables membres avec self , ou y a-t-il un moyen de les déclarer qui me permettrait d'éviter d'avoir à le faire?

Même si ce que je suggère n'est pas pythonique , j'aimerais quand même savoir si c'est possible.

J'ai jeté un coup d'œil à ces questions SO connexes, mais elles ne répondent pas tout à fait à ce que je recherche:

bguiz
la source
5
Je viens d'un arrière-plan Java et je trouve cela naturel, mais j'ajoute explicitement "ceci" à chaque appel pour qu'il soit plus clair que je fais référence à une variable d'instance.
Uri
4
Connaissez-vous la convention d'un m_préfixe pour tous les noms de membres observés par certains programmeurs C ++ / Java? L'utilisation de self.facilite la lisibilité d'une manière similaire. Vous devriez également lire dirtsimple.org/2004/12/python-is-not-java.html .
Beni Cherniavsky-Paskin
2
Bien que généralement m_utilisé pour les membres de données non statiques non publics uniquement (au moins en C ++).
@Beni excellent article lié, et oui, en fait, je respecte la convention d'utilisation mVariableName, pour les variables membres, lors du codage en Java. Je pense que le commentaire de @ Anurag résume assez bien la situation, pour ce qu'un développeur Java devrait faire lorsqu'il apprend python.
bguiz
11
Alors, comment se fait-il que tout le monde dise à OP pourquoi utiliser soi-même est bon / nécessaire / etc. mais personne ne dit si cela peut ou non être évité? Même si par une sorte de sale tour?
einpoklum

Réponses:

99

Python nécessite de spécifier soi-même. Le résultat est qu'il n'y a jamais de confusion sur ce qui est un membre et ce qui ne l'est pas, même sans la définition complète de la classe visible. Cela conduit à des propriétés utiles, telles que: vous ne pouvez pas ajouter de membres qui occultent accidentellement des non-membres et interrompent ainsi le code.

Un exemple extrême: vous pouvez écrire une classe sans aucune connaissance des classes de base qu'elle pourrait avoir, et toujours savoir si vous accédez à un membre ou non:

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()

C'est le code complet ! (some_function renvoie le type utilisé comme base.)

Un autre, où les méthodes d'une classe sont composées dynamiquement:

class B(object):
  pass

print B()
# <__main__.B object at 0xb7e4082c>

def B_init(self):
  self.answer = 42
def B_str(self):
  return "<The answer is %s.>" % self.answer
# notice these functions require no knowledge of the actual class
# how hard are they to read and realize that "members" are used?

B.__init__ = B_init
B.__str__ = B_str

print B()
# <The answer is 42.>

N'oubliez pas que ces deux exemples sont extrêmes et que vous ne les verrez pas tous les jours, et je ne suggère pas non plus que vous devriez souvent écrire du code comme celui-ci, mais ils montrent clairement que les aspects de soi sont explicitement requis.


la source
4
Je vous remercie. Cette réponse frappe le point car elle explique également les avantages de l'utilisation self.
bguiz
2
@Roger Pate: Veuillez arrêter de modifier ma question pour en supprimer Python. Je pense que ça y appartient. (et merci pour la réponse!)
bguiz
3
@bguiz: C'est la convention SO de ne pas dupliquer les balises dans le titre. Cependant, lorsque j'ai édité il y a 2 jours, je ne vous ai pas vu revenir le titre il y a 7 mois.
1
Ce serait bien si le soi pouvait être réduit à un seul personnage.
dwjohnston le
5
C'est en fait une raison plutôt stupide. Python pourrait ne pas accepter l'observation et vous demander de le déclarer spécifiquement, c'est-à-dire de «bénir» votre champ d'observation avec une sorte de __shadow__ myFieldName. Cela empêcherait également l'observation accidentelle, n'est-ce pas?
einpoklum
39

Les réponses précédentes sont toutes fondamentalement des variantes de «vous ne pouvez pas» ou «vous ne devriez pas». Bien que je sois d'accord avec ce dernier sentiment, la question reste techniquement sans réponse.

En outre, il existe des raisons légitimes pour lesquelles quelqu'un pourrait vouloir faire quelque chose dans le sens de ce que la question pose. Une chose que je rencontre parfois est de longues équations mathématiques où l'utilisation de noms longs rend l'équation méconnaissable. Voici quelques façons de procéder dans un exemple en conserve:

import numpy as np
class MyFunkyGaussian() :
    def __init__(self, A, x0, w, s, y0) :
        self.A = float(A)
        self.x0 = x0
        self.w = w
        self.y0 = y0
        self.s = s

    # The correct way, but subjectively less readable to some (like me) 
    def calc1(self, x) :
        return (self.A/(self.w*np.sqrt(np.pi))/(1+self.s*self.w**2/2)
                * np.exp( -(x-self.x0)**2/self.w**2)
                * (1+self.s*(x-self.x0)**2) + self.y0 )

    # The correct way if you really don't want to use 'self' in the calculations
    def calc2(self, x) :
        # Explicity copy variables
        A, x0, w, y0, s = self.A, self.x0, self.w, self.y0, self.s
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

    # Probably a bad idea...
    def calc3(self, x) :
        # Automatically copy every class vairable
        for k in self.__dict__ : exec(k+'= self.'+k)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

g = MyFunkyGaussian(2.0, 1.5, 3.0, 5.0, 0.0)
print(g.calc1(0.5))
print(g.calc2(0.5))
print(g.calc3(0.5))

Le troisième exemple - c'est-à-dire l'utilisation for k in self.__dict__ : exec(k+'= self.'+k)est essentiellement ce que la question demande réellement, mais laissez-moi être clair que je ne pense pas que ce soit généralement une bonne idée.

Pour plus d'informations et pour parcourir les variables de classe, ou même les fonctions, consultez les réponses et la discussion à cette question . Pour une discussion sur d'autres façons de nommer dynamiquement des variables, et pourquoi ce n'est généralement pas une bonne idée, consultez cet article de blog .

MISE À JOUR: Il semble qu'il n'y ait aucun moyen de mettre à jour ou de modifier dynamiquement les paramètres locaux d'une fonction dans Python3, donc calc3 et les variantes similaires ne sont plus possibles. La seule solution compatible python3 à laquelle je puisse penser maintenant est d'utiliser globals:

def calc4(self, x) :
        # Automatically copy every class variable in globals
        globals().update(self.__dict__)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

Ce qui, encore une fois, serait une pratique terrible en général.

argentum2f
la source
4
Brillant! C'est la réponse la plus (seule?) Correcte. +1 Vous donnez également une raison pratique unique pour le faire. + 1i
David Lotts
Après avoir créé une classe et déplacé le code à l'intérieur de celle-ci: maintenant toutes les méthodes et variables ne sont plus reconnues (pas de self ...) Je me souviens encore d'une autre raison pour laquelle python et je ne m'entends pas. Merci pour cette idée. Il ne résout pas le problème / mal de tête / illisibilité dans l'ensemble, mais fournit une solution de contournement modeste.
javadba
Pourquoi ne pas simplement mettre à jour localsau lieu d'utiliser exec?
Nathan
J'ai essayé locals().update(self.__dict__)en python 2 et 3, mais cela n'a pas fonctionné. En python3, même l'astuce «exec» n'est plus une option. D'un autre côté, globals().update(self.__dict__)ça marche, mais ce serait une pratique terrible en général.
argentum2f
26

En fait, ce selfn'est pas un mot-clé, c'est juste le nom conventionnellement donné au premier paramètre des méthodes d'instance en Python. Et ce premier paramètre ne peut pas être ignoré, car c'est le seul mécanisme dont dispose une méthode pour savoir sur quelle instance de votre classe elle est appelée.

Michał Marczyk
la source
1
Cette réponse, en particulier la 2ème phrase, est beaucoup plus utile que la réponse acceptée pour moi car je peux savoir que le `` moi explicite '' n'est qu'une des limitations de Python et n'est pas évitable
QuestionDriven
21

Vous pouvez utiliser le nom de votre choix, par exemple

class test(object):
    def function(this, variable):
        this.variable = variable

ou même

class test(object):
    def function(s, variable):
        s.variable = variable

mais vous êtes coincé avec l'utilisation d'un nom pour la portée.

Je ne vous recommande pas d'utiliser quelque chose de différent de vous-même, sauf si vous avez une raison convaincante, car cela le rendrait étranger pour les pythonistes expérimentés.

Esteban Küber
la source
26
Vous pouvez le faire, mais ne le faites pas ! Il n'y a aucune raison de rendre votre code plus étrange qu'il ne devrait l'être. Oui, vous pouvez l'appeler n'importe quoi, mais la convention est de l'appeler self, et vous devez suivre la convention. Cela rendra votre code plus facile à comprendre pour tout programmeur Python expérimenté qui l'examinera. (Cela inclut vous, dans six mois, en essayant de comprendre ce que fait votre ancien programme!)
Steveha
3
Encore plus extraterrestre:def function(_, variable): _.variable = variable
Bob Stein
1
@ BobStein-VisiBone encore plus extraterrestre:def funcion(*args): args[0].variable = args[1]
Aemyl
4
@steveha il ne le recommande pas, cette information est très utile pour les gens comme moi qui ne savaient pas que vous pouvez utiliser des mots-clés différents de self et se demandent pourquoi un objet de la propre classe est passé.
Sven van den Boogaart
> "plus bizarre". Python est étrange - en particulier ses structures de classes et cette utilisation de soi est un canari pour soutenir la lisibilité. Je sais que de nombreux défenseurs viendront après cela, mais cela ne change pas la véracité.
javadba
9

oui, vous devez toujours spécifier self, car explicite vaut mieux qu'implicite, selon la philosophie de python.

Vous découvrirez également que la façon dont vous programmez en python est très différente de la façon dont vous programmez en java, d'où l'utilisation de a selftendance à diminuer car vous ne projetez pas tout à l'intérieur de l'objet. Au contraire, vous utilisez davantage la fonction au niveau du module, qui peut être mieux testée.

au fait. Je détestais ça au début, maintenant je déteste le contraire. idem pour le contrôle de flux indenté.

Stefano Borini
la source
2
«Vous utilisez plus largement la fonction au niveau du module, qui peut être mieux testée» est douteux, et je serais fortement en désaccord. Il est vrai que vous n'êtes pas obligé de faire de tout une méthode (statique ou non) d'une classe, que ce soit "logiquement au niveau du module", mais cela n'a rien à voir avec soi-même et n'a pas d'impact significatif sur tester d'une manière ou d'une autre (pour moi).
Cela découle de mon expérience. Je ne le suis pas comme un mantra à chaque fois, mais cela rend les choses plus faciles à tester si vous mettez tout ce qui ne nécessite pas d'accès aux variables membres comme une méthode séparée et indépendante. oui, vous séparez la logique des données, ce qui, oui, est contraire à la POO, mais elles sont ensemble, juste au niveau du module. Je ne donne pas à l'un ou à l'autre la «meilleure» note, c'est juste une question de goût. Parfois, je me retrouve à spécifier des méthodes de classe qui n'ont rien à voir avec la classe elle-même, car elles ne touchent selfen aucune façon. Alors, quel est l'intérêt de les avoir dans la classe?
Stefano Borini
Je ne suis pas d'accord avec les deux parties de celui-ci (il semble que je n'utilise pas plus souvent des "non-méthodes" que dans d'autres langues), mais "qui peut être mieux testé" implique qu'une manière est supérieure à l'autre (est-ce ainsi que je lire? ne semble pas probable), alors que je ne trouve pas de soutien pour cela dans mon expérience. Notez que je ne dis pas que vous devriez toujours utiliser l'un ou l'autre, je dis seulement que les méthodes et les non-méthodes peuvent également être testées.
5
oui, bien sûr, vous pouvez, mais l'approche est différente. Un objet a un état, pas une méthode de niveau module. Si vous découvrez qu'un test échoue lors du test d'une méthode au niveau de la classe, deux choses auraient pu être fausses: 1) l'état de l'objet au moment de l'appel 2) la méthode elle-même. Si vous avez une méthode au niveau du module sans état, seul le cas 2 peut se produire. Vous avez déplacé la configuration de l'objet (où il s'agit d'une boîte noire en ce qui concerne le test, car elle est régie par la logique éventuellement complexe à l'intérieur de l'objet) vers la suite de tests. Vous réduisez la complexité et gardez un contrôle plus strict de la configuration.
Stefano Borini
1
"Si vous avez une méthode au niveau du module sans état", qu'en est-il d'une méthode au niveau du module avec état? Tout ce que vous me dites, c'est que les fonctions sans état sont plus faciles à tester que les fonctions avec état, et je suis d'accord avec cela, mais cela n'a rien à voir avec les méthodes et les non-méthodes. Voir le paramètre self comme exactement cela: juste un autre paramètre de la fonction.
4

Le «self» est l'espace réservé conventionnel de l'instance d'objet courante d'une classe. Il est utilisé lorsque vous voulez faire référence à la propriété, au champ ou à la méthode de l'objet à l'intérieur d'une classe comme si vous faisiez référence à «lui-même». Mais pour raccourcir les choses, quelqu'un dans le domaine de la programmation Python a commencé à utiliser "self", d'autres royaumes utilisent "this" mais ils en font un mot-clé qui ne peut pas être remplacé. J'ai plutôt utilisé "son" pour augmenter la lisibilité du code. C'est l'une des bonnes choses de Python - vous avez la liberté de choisir votre propre espace réservé pour l'instance de l'objet autre que "self". Exemple pour soi:

class UserAccount():    
    def __init__(self, user_type, username, password):
        self.user_type = user_type
        self.username = username            
        self.password = encrypt(password)        

    def get_password(self):
        return decrypt(self.password)

    def set_password(self, password):
        self.password = encrypt(password)

Maintenant, nous remplaçons 'self' par 'its':

class UserAccount():    
    def __init__(its, user_type, username, password):
        its.user_type = user_type
        its.username = username            
        its.password = encrypt(password)        

    def get_password(its):
        return decrypt(its.password)

    def set_password(its, password):
        its.password = encrypt(password)

qui est plus lisible maintenant?

LEMUEL ADANE
la source
pourquoi pas juste s(ou une autre lettre unique) au lieu deits
javadba
«its» a un sens et «s» n'en a pas.
LEMUEL ADANE
sa la même signification: un alias de l'instance de classe. je devrais rechercher ce que itssignifie dans le contexte tout de même
javadba
Les deux ne sont pas lisibles pour moi. Il est encore illogique d'avoir «soi-même» comme paramètre externe, cela n'a aucun sens.
Cesar
3

self fait partie de la syntaxe python pour accéder aux membres des objets, donc j'ai peur que vous soyez coincé

Charles Ma
la source
2
self est un moyen de dire le modificateur d'accès sans vraiment en utiliser un. +1
Perpetualcoder
1

En fait, vous pouvez utiliser la recette "Auto implicite" de la présentation d'Armin Ronacher "5 ans de mauvaises idées" (google it).

C'est une recette très intelligente, comme presque tout d'Armin Ronacher, mais je ne pense pas que cette idée soit très attrayante. Je pense que je préférerais cela explicite en C # / Java.

Mettre à jour. Lien vers "recette de mauvaise idée": https://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58

Alex Yu
la source
Est- ce le lien auquel vous faites référence? Si oui, veuillez inclure dans votre réponse
reubenjohn
Non, la "mauvaise idée" d'Armin a l'air plus drôle à mon goût. J'ai inclus un lien.
Alex Yu
Avant de cliquer sur le lien de la recette, notez que cela le rend implicite dans la liste des paramètres def method(<del> self </del> ), mais self.variableest toujours nécessaire avec ce hack astucieux.
David Lotts
0

Ouais, le moi est fastidieux. Mais est-ce mieux?

class Test:

    def __init__(_):
        _.test = 'test'

    def run(_):
        print _.test

la source
10
_a une signification particulière dans le shell Python, où il contient la dernière valeur renvoyée. C'est sûr de l'utiliser comme ça, mais potentiellement déroutant; Je l'éviterais.
Cairnarvon
Non, ce n'est pas mieux, mais pourquoi pas une seule lettre à la place, par exemple sou m(pour imiter C ++)
javadba
0

De: Self Hell - Plus de fonctions avec état.

... une approche hybride fonctionne mieux. Toutes vos méthodes de classe qui effectuent réellement des calculs doivent être déplacées dans des fermetures, et les extensions pour nettoyer la syntaxe doivent être conservées dans des classes. Farcissez les fermetures dans des classes, en traitant la classe comme un espace de noms. Les fermetures sont essentiellement des fonctions statiques, et ne nécessitent donc pas de selfs *, même dans la classe ...

user5554473
la source
Les fermetures conviennent pour les petits cas d'utilisation, mais leur utilisation prolongée augmentera considérablement la charge mémoire d'un programme (car vous utilisez un OO basé sur un prototype plutôt qu'un OO basé sur une classe - donc chaque objet nécessite son propre ensemble de fonctions plutôt garder un ensemble commun de fonctions dans une classe). De plus, cela vous empêchera de pouvoir utiliser des méthodes magiques / dunder (par exemple __str__, etc.) car elles sont invoquées différemment des méthodes normales.
Dunes
0

Je pense que ce serait plus facile et plus lisible s'il y avait une instruction «membre» tout comme il y a «global» pour que vous puissiez dire à l'interpréteur quels sont les objets membres de la classe.

Pablo Villar
la source