Python 3中的真正私有变量

时间:2018-12-14 01:40:17

标签: python scope private

所以我知道在python这样的变量中使变量“私有”的方法:

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中制作真私有变量的任何方法都不应在生产代码中完成。我只想知道如何

5 个答案:

答案 0 :(得分:4)

  

我尝试覆盖 getattribute ,但是问题在于无法知道调用是否来自类内部(我知道)。

您可以使用inspect模块来查找调用函数的名称和模块,并将其与白名单进行比较。

但是inspect也有getattr_static,可以绕开任何__getattribute__


在Python中,没有什么是真正私有的。有多种方法可以使访问变得困难,但是在这些方法中总有一些方法。

当时唯一的解决方案是在当前的Python解释器之外。您可以将外部函数接口用于其他一些更安全的语言,也可以将远程过程调用(例如xmlrpc)用于相同或另一子进程中运行的Python解释器,甚至可以以具有不同权限的其他用户身份运行。私有变量和允许访问它的所有函数将驻留在当前解释器之外。然后就没有办法检查它了。

这种类型的privilege separation甚至是Pyro RPC库的stated use cases之一。

答案 1 :(得分:3)

Python没有私有属性的原因是我们无法确定它是在类的内部还是外部。它们共享相同的属性访问过程。 self.private就是obj.private。因此,如果我们阻止obj.private,也将阻止self.private。区别它们的唯一方法是使用obj.privateself._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 的概念,它可以通过添加一层属性代理来使 NEARLY 私有属性(如我所说,这将阻止访问在课程内部):

__getattribute__

使用双下划线或更改 //mediaPlayer.MediaOpened += media_MediaOpened; 都是不好的做法,尤其是后者,可能会造成灾难。

答案 2 :(得分:2)

看完inspect模块的this answer之后,我(有点)完成了!

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,但如果从外部调用则会产生错误。这可能是人们所能接近的。

答案 3 :(得分:2)

使用闭包而不是属性,无需花哨的检查就可以得到几乎相同的效果。

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也可以查看闭包。

答案 4 :(得分:2)

尽管不是完全100%私有,但我喜欢做的事情是在方法中使用闭包来将通常无法访问的属性作为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代码提供该级别的功能始终会使它们保持打开状态,因为它们始终可以访问和更改。

如果我对此有误,请发表评论:)