Prenons un exemple de classe Foo en Python:
class Foo: bar = 'bar' def access_bar(self): return self.bar
Puis-je, par exemple afficher un avertissement, en accédant directement à Foo (). bar
, mais en même temps, ne pas imprimer cet avertissement lors de l'appel de Foo (). access_bar ()
, qui accède à cet attribut depuis la classe?
J'ai essayé d'implémenter __getattribute __ , mais je n'ai pas réussi à différencier ces cas.
Je sais que c'est une question assez étrange, mais ne me répondez pas comme "Vous ne devriez pas avoir besoin de ça".
4 Réponses :
vous pourriez faire de bar
une propriété qui permet de contrôler l'accès sans montrer l'appel de méthode à l'extérieur, et rendre votre attribut privé:
direct access warn bar OK bar
imprime:
class Foo: __bar = 'bar' @property def bar(self): print("direct access") return Foo.__bar def access_bar(self): return self.__bar f = Foo() print("warn",f.bar) print("OK",f.access_bar())
Je sais de cette façon. Mais j'accède beaucoup à la `__bar` de l'intérieur et je n'ai pas aimé les traits de soulignement (et les avertissements PyCharm sur leur utilisation). J'essaie donc d'implémenter une manière dont je n'ai pas à utiliser de traits de soulignement à l'intérieur de la classe, mais l'utilisateur est averti lorsqu'il essaie d'accéder à cet attribut de l'extérieur.
@JosefKvita gardez à l'esprit que il n'y a pas de variables de classe privée en python . Peu importe combien vous essayez de cacher la variable interne qui contient la valeur, quelqu'un peut trouver un moyen d'y accéder directement. S'il s'agit d'une tentative pour dissuader d'autres ingénieurs d'accéder à des champs sensibles, essayez peut-être d'avoir une conversation avec eux à la place :)
Je le sais. Je veux juste imprimer un avertissement, pas le rendre inaccessible. :)
@JosefKvita pourquoi accédez-vous beaucoup à __bar
de l'intérieur? Le seul accès direct ne devrait-il pas provenir de access_bar
? Sinon, ma réponse ne vous aidera pas, elle ne fait pas la différence entre l'intérieur et l'extérieur de la classe.
Je suggère de stocker la valeur dans un attribut protégé (un trait de soulignement) ou privé (deux traits de soulignement), et de faire de Un utilisateur peut toujours écrire bar
une propriété accessible en toute sécurité, l'équivalent de access_bar dans votre question. C'est ainsi que ce genre de chose est généralement fait en Python.
class Foo:
_bar = 'bar'
@property
def bar(self):
# do extra things here
return self._bar
foo._bar
ou foo._Foo__bar
( pour un attribut privé) pour obtenir l'attribut en externe sans aucun avertissement, mais s'ils sont conscients des conventions entourant les traits de soulignement de début, ils se sentiront probablement un peu mal à l'aise et seront conscients des risques.
Voici la "vraie" réponse à votre question, que vous ne devriez probablement pas faire:
import inspect class Foo: bar = 'bar' def access_bar(self): return self.bar def __getattribute__(self, item): if item == 'bar': code = inspect.currentframe().f_back.f_code if not (start_lineno <= code.co_firstlineno <= end_lineno and code.co_filename == __file__): print('Warning: accessing bar directly') return super().__getattribute__(item) lines, start_lineno = inspect.getsourcelines(Foo) end_lineno = start_lineno + len(lines) - 1 print(1, Foo().bar) print(2, Foo().access_bar())
Si vous faites cela, il est important qu'il n'y ait qu'une seule classe nommée Foo dans le fichier, sinon
inspect.getsourcelines (Foo)
peut ne pas donner le bon résultat.
print (Foo.bar)
n'imprime pas d'avertissement. Cela ne fonctionne que sur les instances de la classe Foo
. Vous aurez besoin d'une métaclasse si vous souhaitez que cela fonctionne également pour les attributs de classe.
Je pense que c'est exactement ce dont j'avais besoin, merci beaucoup! J'essaierai de jouer avec. Je sais qu'il devait y avoir quelque chose, car tout peut être fait en Python! :-)
bon appel, je n'ai même pas osé dire à l'OP d'utiliser le module inspect pour cela. "ce que vous ne devriez probablement pas faire" vous donne mon vote :)
Maintenant, il vérifie si l'attribut a été appelé depuis l'extérieur de la méthode access_bar (), que se passe-t-il si je voulais vérifier en dehors de la classe Foo (la barre accessible dans n'importe quelle méthode à l'intérieur de la classe n'afficherait pas l'avertissement)?
Pourquoi ne pas utiliser inspect
pour trouver l'origine de l'appel à __getattribute__
, le modifier légèrement pour qu'il apprenne à imprimer un avertissement? Je n'ai aucune idée à quel point c'est une bonne idée.
Voici une autre tentative pour améliorer la réponse d'Alex en ajoutant une métaclasse afin qu'elle fonctionne également pour les attributs de classe, et en supprimant avec le module inspect
, et ajoutez à la place un indicateur d'avertissement à la fonction __getattribute__
elle-même.
class FooType(type): def __getattribute__(self, item): if item == "bar": print("Warning: accessing bar directly from class") return item.__getattribute__(self, item) class Foo(object, metaclass=FooType): bar = 'bar' def access_bar(self): return self.__getattribute__('bar', warn=False) def __getattribute__(self, item, warn=True): if item == 'bar' and warn: print('Warning: accessing bar directly from instance') return super().__getattribute__(item) print(Foo.bar) #Warning: accessing bar directly from class #bar print(Foo().bar) #Warning: accessing bar directly from instance #bar print(Foo().access_bar()) #bar
La mauvaise chose à ce sujet est que je dois accéder à l'attribut partout dans la classe (comme sur 100 endroits), avec un self .__ getattribute __ ('bar', warn = False)
, pas seulement self.bar
, ou ai-je tort? C'est sûrement pire que self._bar
.
C'est vrai. Je pense que cette solution vous empêche d'utiliser instance.bar
partout, y compris à l'intérieur de la classe. Il vous encourage à remplacer self.bar
par self.access_bar ()
.
Foo (). access_bar ()
n'est pas la même chose queFoo.access_bar ()
Vous semblez avoir une motivation pour poser votre question. Cela pourrait être utile si vous expliquez pourquoi cette distinction est nécessaire (même si ce n'est que de la curiosité!)
Voulez-vous vraiment un attribut class par opposition à un attribut d'instance (
self.bar = 'bar'
)? Êtes-vous sûr de connaître la différence?