所以我知道在python中创建变量“private”的方法如下:
class Foo:
def __init__(self):
self.__private = 'bar'
这“有效”,但没有,如下所示:
foo = Foo()
'__private' in vars(foo) #False
'_Foo__private' in vars(foo) #True
现在,我明白这是在python中创建私有变量的方法,我喜欢这种方式。它允许你修改名称,以便没有子类意外地覆盖它(因为它以类的名称开头),并且没有人会意外地使用它。如果您知道自己在做什么,它还可以让您更改私有变量。此外,这是最好的方法,因为真正的私有变量是不可能的。
或者我想。
最近,我正在阅读PEP 8,我看到了这一行:
我们在这里不使用术语“私有”,因为在Python中没有属性是真正私有的(没有通常不必要的工作量)。
这句话可以在Designing for Inheritance section of PEP 8找到。
注意短语“没有通常不必要的工作量”。我现在确定必须有一种方法可以在python中获得真正的私有变量。我该怎么办?
我试过重写__getattribute__
,但问题是没有办法判断调用是否来自类内部(我知道)。
此外,__dict__
属性在尝试执行此操作时很烦人,因为它包含对所有实例变量的引用。
我也想过元类,但那些似乎和__getattribute__
有同样的问题。
思考?
注意:我知道在python中创建真正的私有变量的任何方法都不应该在高效的代码中完成。我只是想知道如何做到这一点。
我试过覆盖getattribute,但问题是没有办法判断调用是否来自类内部(我知道)。
您可以使用inspect
模块查找调用函数的名称和模块,您可以将其与白名单进行比较。
但inspect
也有getattr_static
,可以绕过任何__getattribute__
。
Python中没有什么是真正私有的。有一些方法可以使访问变得困难,但总有办法绕过这些方式。
那时唯一的解决方案是在当前的Python解释器之外。您可以将外部函数接口用于某些其他更安全的语言或远程过程调用(例如xmlrpc)到相同或另一个在子进程中运行的Python解释器,甚至可以使用具有不同权限的不同用户运行。私有变量和允许访问它的所有函数将驻留在当前解释器之外。然后没有办法检查它。
这种类型的privilege separation甚至是Pyro RPC库的stated use cases之一。
Python没有私有属性的原因是我们无法判断它是在类的内部还是外部。它们共享相同的属性访问过程。 self.private
正是obj.private
。因此,如果我们阻止obj.private
,self.private
也被阻止。区别对待的唯一方法是给出不同的名称,并使obj.private
成为self._private
或@property
的data descriptor
的代理,并相信使用它的人都是成年人。
无论如何,我想分享data descriptor
的概念,它可以通过添加一层属性代理来创建几乎属于私有属性(正如我所说,这将阻止从类'内部'访问):
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
使用双下划线或更改__getattribute__
are两种不良做法,尤其是后者,可能会导致灾难。
通过使用闭包而不是属性,您可以在没有花哨检查的情况下获得几乎相同的效果。
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
当然,inspect
也可以看到封闭。
我喜欢做的事情,虽然它不完全是100%私有的,但是在方法中使用闭包来将R / W通常不可访问的属性作为member_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
请注意,inst.private名称不存在,如果引用则会引发AttributeError。 但成员描述符本身确实存在,并且绑定到该类。
但就像我说的那样,它并非100%私密...... 您可以通过它们的闭包访问提供给类方法的描述符方法:
>>> inst.showprivate.__closure__[0].cell_contents
<method-wrapper '__get__' of member_descriptor object at 0x00E588A0>
这是第一个后门,如果说方法包含__set__
的封闭。
但如果没有,第二个后门只是有点复杂:
>>> inst.showprivate.__closure__[0].cell_contents.__self__.__set__( inst, 30 )
>>> inst.showprivate()
30
虽然在使用多个闭包时,闭包单元格的顺序依赖于当前运行(如字典键)。
可悲的是,我似乎无法找出比这更安全的东西......
问题如前面的答案中所述: 属性无法分辨它们被访问的位置,并且通过python代码提供该级别的功能总是使它们保持打开状态,因为它们总是可以被访问和更改。
如果我错了,请评论:)
在看了this answer关于inspect
模块后,我(有点)做到了!
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
好吧,差不多。 inspect
也可以用来找到价值。但这非常接近。它允许类中的object.attr
,但如果从外部调用则会产生错误。这可能就像人们所能得到的一样接近。