Python 3 中的真正私有变量
Truly Private Variables in Python 3
所以我知道在 python 中创建变量 "private" 的方法如下:
class Foo:
def __init__(self):
self.__private = 'bar'
这个"works"和没有,如下图:
foo = Foo()
'__private' in vars(foo) #False
'_Foo__private' in vars(foo) #True
现在,我明白这是在 python 中创建私有变量的方法,我喜欢 这种方法。它允许您修改名称,这样子 class 就不会意外地覆盖它(因为它以 class 的名称开头),并且没有人会意外地使用它。它还使您能够更改私有变量如果您知道自己在做什么。另外,这是最好的方法,因为真正的私有变量是不可能的。
我是这么想的。
最近,我在阅读 PEP 8 时看到了这一行:
We don't use the term "private" here, since no attribute is really private in Python (without a generally unnecessary amount of work).
此引用在 Designing for Inheritance section of PEP 8 中找到。
注意短语 "without a generally unnecessary amount of work"。我现在确定 必须 是一种在 python 中获取真正私有变量的方法。我该怎么做?
我已经尝试覆盖 __getattribute__
,但问题是无法判断呼叫是否来自 class 内部(我知道)。
此外,__dict__
属性在尝试执行此操作时很烦人,因为它包含对所有实例变量的引用。
我也想到了 metaclasses,但那些似乎和 __getattribute__
.
有同样的问题
想法?
Note:
I understand that any way to make truly private variables in python should never be done in productive code. I just want to know how it could be done.
之所以Python没有私有属性,是因为我们无法判断它是在class内部还是外部。它们共享相同的属性访问过程。 self.private
正好是 obj.private
。所以,如果我们阻止 obj.private
,self.private
也会被阻止。区别他们的唯一方法是给不同的名字,让 obj.private
成为 @property
或 data descriptor
对 self._private
的代理,并相信使用它的人都是成年人。
无论如何,我想分享一下 data descriptor
的概念,它可以通过添加一层属性代理来使 NEARLY 私有属性(正如我所说,这会阻止来自 'inside' 和 class) 的访问:
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__
都是不好的做法,尤其是后者,可能会酿成大祸。
在看了 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
在 class 内,但如果从外部调用会产生错误。这可能是最接近的了。
I have tried overriding getattribute, but the problem is that there is no way to tell if the call is coming from inside the class or not (that I am aware of).
您可以使用 inspect
模块来查找调用函数的名称和模块,您可以将其与白名单进行比较。
但是inspect
也有getattr_static
,可以绕过任何__getattribute__
.
Python 没有什么是真正私密的。有办法使访问变得困难,但总有办法解决这些问题。
唯一的解决方案是在当前 Python 解释器之外。您可以将外部函数接口用于其他一些更安全的语言或远程过程调用(例如 xmlrpc)到子进程中的相同或另一个 Python 解释器 运行,甚至是一个 运行 作为具有不同权限的不同用户。私有变量和所有允许访问它的函数将存在于当前解释器之外。那就没办法检查了。
这种类型的 privilege separation is even one of the stated use cases 用于 Pyro RPC 库。
通过使用闭包而不是属性,您可以获得几乎相同的效果而无需花哨的检查。
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
也可以看到闭包。
我喜欢做的事情是在方法中使用闭包 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。
但是成员描述符本身确实存在,并且绑定到 class.
但就像我说的,它不是 100% 私密的...
您可以通过闭包访问提供给 class 方法的描述符方法:
>>> 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 代码提供该级别的功能总是使它们保持打开状态,因为它们始终可以被访问和更改。
如果我在这方面有误,请发表评论:)
所以我知道在 python 中创建变量 "private" 的方法如下:
class Foo:
def __init__(self):
self.__private = 'bar'
这个"works"和没有,如下图:
foo = Foo()
'__private' in vars(foo) #False
'_Foo__private' in vars(foo) #True
现在,我明白这是在 python 中创建私有变量的方法,我喜欢 这种方法。它允许您修改名称,这样子 class 就不会意外地覆盖它(因为它以 class 的名称开头),并且没有人会意外地使用它。它还使您能够更改私有变量如果您知道自己在做什么。另外,这是最好的方法,因为真正的私有变量是不可能的。
我是这么想的。
最近,我在阅读 PEP 8 时看到了这一行:
We don't use the term "private" here, since no attribute is really private in Python (without a generally unnecessary amount of work).
此引用在 Designing for Inheritance section of PEP 8 中找到。
注意短语 "without a generally unnecessary amount of work"。我现在确定 必须 是一种在 python 中获取真正私有变量的方法。我该怎么做?
我已经尝试覆盖 __getattribute__
,但问题是无法判断呼叫是否来自 class 内部(我知道)。
此外,__dict__
属性在尝试执行此操作时很烦人,因为它包含对所有实例变量的引用。
我也想到了 metaclasses,但那些似乎和 __getattribute__
.
想法?
Note: I understand that any way to make truly private variables in python should never be done in productive code. I just want to know how it could be done.
之所以Python没有私有属性,是因为我们无法判断它是在class内部还是外部。它们共享相同的属性访问过程。 self.private
正好是 obj.private
。所以,如果我们阻止 obj.private
,self.private
也会被阻止。区别他们的唯一方法是给不同的名字,让 obj.private
成为 @property
或 data descriptor
对 self._private
的代理,并相信使用它的人都是成年人。
无论如何,我想分享一下 data descriptor
的概念,它可以通过添加一层属性代理来使 NEARLY 私有属性(正如我所说,这会阻止来自 'inside' 和 class) 的访问:
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__
都是不好的做法,尤其是后者,可能会酿成大祸。
在看了 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
在 class 内,但如果从外部调用会产生错误。这可能是最接近的了。
I have tried overriding getattribute, but the problem is that there is no way to tell if the call is coming from inside the class or not (that I am aware of).
您可以使用 inspect
模块来查找调用函数的名称和模块,您可以将其与白名单进行比较。
但是inspect
也有getattr_static
,可以绕过任何__getattribute__
.
Python 没有什么是真正私密的。有办法使访问变得困难,但总有办法解决这些问题。
唯一的解决方案是在当前 Python 解释器之外。您可以将外部函数接口用于其他一些更安全的语言或远程过程调用(例如 xmlrpc)到子进程中的相同或另一个 Python 解释器 运行,甚至是一个 运行 作为具有不同权限的不同用户。私有变量和所有允许访问它的函数将存在于当前解释器之外。那就没办法检查了。
这种类型的 privilege separation is even one of the stated use cases 用于 Pyro RPC 库。
通过使用闭包而不是属性,您可以获得几乎相同的效果而无需花哨的检查。
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
也可以看到闭包。
我喜欢做的事情是在方法中使用闭包 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。
但是成员描述符本身确实存在,并且绑定到 class.
但就像我说的,它不是 100% 私密的...
您可以通过闭包访问提供给 class 方法的描述符方法:
>>> 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 代码提供该级别的功能总是使它们保持打开状态,因为它们始终可以被访问和更改。
如果我在这方面有误,请发表评论:)