代码之家  ›  专栏  ›  技术社区  ›  Eb946207 mourshad

Python3中的真正私有变量

  •  7
  • Eb946207 mourshad  · 技术社区  · 7 年前

    我知道在python中如何使变量“private”如下:

    class Foo:
        def __init__(self):
            self.__private = 'bar'
    

    foo = Foo()
    '__private' in vars(foo) #False
    '_Foo__private' in vars(foo) #True
    

    我喜欢 如果你知道你在做什么 . 而且,这是最好的方法,因为真正的私有变量是不可能的。

    最近,我在看书 PEP 8 我看到这句话:

    这里我们不使用术语“private”,因为Python中没有真正私有的属性(通常不需要做大量的工作)。

    Designing for Inheritance section of PEP 8 .

    必须 这是在python中获取真正私有变量的一种方法。我该怎么做?

    我已经试过了 __getattribute__ ,但问题是无法判断调用是否来自类内部(我知道)。

    __dict__ 属性在尝试执行此操作时很烦人,因为它包含对所有实例变量的引用。

    __getattribute__

    思想?


    从未 可以在生产代码中完成。我只是想知道它是怎么做到的 能够 完成。

    5 回复  |  直到 7 年前
        1
  •  4
  •   gilch    7 年前

    我已经试过了 getattribute ,但问题是无法判断调用是否来自类内部(我知道)。

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

    但是 检查 getattr_static ,可以绕过任何 __getattribute__ .


    Python中没有真正私有的东西。有一些方法使访问变得困难,但总有一些方法可以绕过这些方法。

    这种 privilege separation 甚至是其中之一 stated use cases 对于Pyro RPC库。

        2
  •  5
  •   gilch    7 年前

    通过使用闭包而不是属性,您可以获得几乎相同的效果,而无需进行花哨的检查。

    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

        3
  •  3
  •   Hou Lu    7 年前

    Python没有私有属性的原因是我们无法判断它是在类内部还是外部。它们共享相同的属性访问过程。 self.private 正是 obj.private . 所以,如果我们阻止 二等兵 , 这也被阻止了。区别它们的唯一方法是给它们起不同的名字,并使它们的名称不同 二等兵 self._private 通过 @property 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__

        4
  •  2
  •   Tcll    7 年前

    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代码提供这种级别的功能总是让它们保持打开状态,因为它们总是可以访问和更改的。

        5
  •  2
  •   Eb946207 mourshad    7 年前

    看了之后 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
    

    检查 也可以用于查找值。不过,这非常接近。它允许 object.attr

    推荐文章