Je connais donc le moyen de créer une variable "privée" en python comme ceci:
class Foo:
def __init__(self):
self.__private = 'bar'
Ceci "fonctionne" et ne fonctionne pas, comme indiqué ci-dessous:
foo = Foo()
'__private' in vars(foo) #False
'_Foo__private' in vars(foo) #True
Maintenant, je comprends que c'est le moyen de créer des variables privées en python et j'aime de cette façon. Il vous permet de modifier les noms de manière à ce qu'aucune sous-classe ne l'ignore accidentellement (car elle commence par le nom de la classe) et que personne ne l'utilisera accidentellement. Cela vous donne également le pouvoir de changer les variables privées si vous savez ce que vous faites . En outre, c'est la meilleure façon de le faire, car les variables réellement privées sont impossibles.
Ou alors j'ai pensé.
Récemment, je lisais PEP 8 et j’ai vu cette ligne:
Nous n'utilisons pas le terme "privé" ici, car aucun attribut n'est vraiment privé en Python (sans un travail généralement inutile).
Cette citation se trouve dans la section Conception pour l'héritage du PEP 8 .
Notez la phrase "sans travail généralement inutile". Je suis maintenant sûr qu'il y a must un moyen d'obtenir des variables vraiment privées en python. Comment je ferais ça?
J'ai essayé de surcharger __getattribute__
, mais le problème est qu'il n'y a aucun moyen de savoir si l'appel provient de l'intérieur de la classe ou non (à ma connaissance).
De plus, l'attribut __dict__
est pénible lorsque vous essayez de le faire car il contient des références à toutes les variables d'instance.
J'ai aussi pensé aux métaclasses, mais celles-ci semblent avoir les mêmes problèmes que __getattribute__
.
Pensées?
Remarque: Je comprends que toute méthode permettant de créer des variables réellement privées en python devrait être (jamais} _ en code productif. Je veux juste savoir comment cela se fait {pourrait}.
J'ai essayé de remplacer getattribute , mais le problème est qu’il n’ya aucun moyen de savoir si l’appel vient de la classe ou non (à ma connaissance).
Vous pouvez utiliser le module inspect
pour rechercher le nom et le module de la fonction appelante, que vous pouvez comparer à une liste blanche.
Mais inspect
a aussi getattr_static
, ce qui peut éviter tout __getattribute__
.
Rien n'est vraiment privé en Python. Il existe des moyens de rendre l'accès difficile, mais il existe toujours des moyens de contourner ces moyens. La seule solution est alors en dehors de l'interpréteur Python actuel. Vous pouvez utiliser une interface de fonction étrangère avec un autre langage, voire avec un autre interpréteur Python exécuté dans un sous-processus. La variable privée et toutes les fonctions autorisées à y accéder vivront en dehors de l'interpréteur actuel. Ensuite, il n'y a aucun moyen de l'inspecter.
La raison pour laquelle Python n'a pas d'attribut privé est que nous ne pouvons pas dire si c'est à l'intérieur ou à l'extérieur d'une classe. Ils partagent le même processus d'accès aux attributs. self.private
est exactement le obj.private
. Donc, si nous empêchons de obj.private
, self.private
est également empêché. La seule façon de les différencier est de donner un nom différent et de transformer le obj.private
en proxy de self._private
par @property
ou data descriptor
et de croire que ceux qui l'utilisent sont tous des adultes.
Quoi qu'il en soit, j'aimerais partager le concept de data descriptor
qui pourrait rendre PRESQUE attributs privés en ajoutant une couche d'attribut proxy (comme je l'ai dit, cela empêcherait l'accès depuis «l'intérieur» de la classe):
class Private:
def __init__(self, attribute):
self.attribute = attribute
def __get__(self, obj, type=None):
raise AttributeError("'{}' object has no attribute '{}'".format(obj, self.attribute))
def __set__(self, obj, value):
obj.__dict__[self.attribute] = value
class YourClass:
private = Private('private')
def __init__(self):
self.private = 10
print(self.private) # Raise AttributeError
Utilisez des soulignements doubles ou changez __getattribute__
les deux mauvaises pratiques, surtout la dernière, peuvent provoquer des catastrophes.
ce que j'aime faire, même si ce n'est pas à 100% privé, consiste à utiliser des méthodes de fermeture dans les méthodes pour les attributs normalement inaccessibles R/W en tant qu'objets membre_descriptor:
def privateNS():
class MyObject(object):
__slots__ = ['private'] # name doesn't matter
def __new__(cls, value): # only sets inst.private on new instance creation
inst = object.__new__(cls)
setprivate(inst, value)
return inst
# __init__ is not needed, and can't be used here to set inst.private
def showprivate(inst):
return getprivate(inst)
dsc = MyObject.private # get descriptor
getprivate = dsc.__get__
setprivate = dsc.__set__
del MyObject.private # revoke normal access
return MyObject
MyObject = privateNS()
del privateNS
inst = MyObject( 20 )
print( inst.showprivate() ) # 20
notez que le nom inst.private n'existe pas et générera une AttributeError si elle est référencée.
mais le descripteur de membre lui-même existe et est lié à la classe.
mais comme je l'ai dit, ce n'est pas 100% privé ...
vous pouvez accéder aux méthodes de description fournies aux méthodes de classe par leurs fermetures:
>>> inst.showprivate.__closure__[0].cell_contents
<method-wrapper '__get__' of member_descriptor object at 0x00E588A0>
c'est la première porte dérobée, si ladite méthode contient __set__
dans ses fermetures.
mais sinon, la 2e porte dérobée n’est qu’un peu plus compliquée:
>>> inst.showprivate.__closure__[0].cell_contents.__self__.__set__( inst, 30 )
>>> inst.showprivate()
30
quelque chose d’aide quand on utilise plusieurs fermetures, l’ordre des cellules de fermeture dépend de l’exécution en cours (comme les clés de dictionnaire).
malheureusement, je n'arrive pas à comprendre quoi que ce soit de plus sûr.
le problème est comme indiqué dans une réponse précédente:
Les attributs ne peuvent pas dire où ils sont accédés, et fournir ce niveau de fonctionnalité via du code python les laisse toujours ouverts, car ils peuvent toujours être consultés et modifiés.
si je me trompe, commentez s'il vous plaît :)
Vous pouvez obtenir presque le même effet sans l'inspection sophistiquée en utilisant des fermetures au lieu d'attributs.
class Foo:
def __init__(self):
private = 'bar'
def print_private():
print(private)
self.print_private = print_private
foo = Foo()
foo.print_private() # works
foo.private # kaboom
Bien entendu, inspect
peut également voir les fermetures.
Bien après avoir regardé cette réponse à propos du module inspect
, je l’ai (en quelque sorte) fait!
class Foo:
def __init__(self, private):
self.private = private
def __getattribute__(self, attr):
import inspect
frame = inspect.currentframe()
try:
back_self = frame.f_back.__self__
if not back_self == self: #is it inside the class?
ban = ('private', '__dict__') #all private vars, ban __dict__ for no loopholes
if attr in ban:
msg = 'Foo object has no attribute {!r}'
raise AttributeError(msg.format(attr))
finally:
del frame
return super().__getattribute__(attr)
def print_private(self):
print(self.private) #access in the class!
foo = Foo('hi')
foo.print_private() #output: hi
foo.private #makes an error
Enfin presque. inspect
peut également être utilisé pour trouver la valeur. Ceci est très proche, cependant. Il autorise object.attr
à l'intérieur de la classe mais crée une erreur s'il est appelé de l'extérieur. C'est probablement aussi proche que possible.