簡體   English   中英

Python 3中的真正私有變量

[英]Truly Private Variables in Python 3

所以我知道在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中沒有屬性是真正私有的(沒有通常不必要的工作量)。

該引用可在PEP 8Designing for Inheritance部分中找到。

注意短語“沒有通常不必要的工作量”。 我現在確定必須有一種方法可以在python中獲得真正的私有變量。 我該怎么辦?

我已經嘗試重寫__getattribute__ ,但問題是沒有辦法判斷調用是否來自類內部(我知道)。

此外, __dict__屬性在嘗試執行此操作時很煩人,因為它包含對所有實例變量的引用。

我也想過元類,但那些似乎與__getattribute__有同樣的問題。

思考?


注意:我知道在python中創建真正的私有變量的任何方法都不應該在高效的代碼中完成。 我只是想知道如何可以做到。

我試過覆蓋getattribute ,但問題是沒有辦法判斷調用是否來自類內部(我知道)。

您可以使用inspect模塊查找調用函數的名稱和模塊,您可以將其與白名單進行比較。

但是inspect也有getattr_static ,它可以繞過任何__getattribute__


Python中沒有什么是真正私有的。 有一些方法可以使訪問變得困難,但總有辦法繞過這些方式。

那時唯一的解決方案是在當前的Python解釋器之外。 您可以將外部函數接口用於某些其他更安全的語言或遠程過程調用(例如xmlrpc)到相同或另一個在子進程中運行的Python解釋器,甚至可以使用具有不同權限的不同用戶運行。 私有變量和允許訪問它的所有函數將駐留在當前解釋器之外。 然后沒有辦法檢查它。

這種類型的權限分離甚至是Pyro RPC庫的既定用例之一。

Python沒有私有屬性的原因是我們無法判斷它是在類的內部還是外部。 它們共享相同的屬性訪問過程。 self.private正是obj.private 因此,如果我們阻止obj.private ,也會阻止self.private 以不同它們的唯一方法就是給不同的名稱,使obj.private是代理self._private通過@propertydata 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__都是不良做法,尤其是后者,可能會造成災難。

看完這個關於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 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代碼提供該級別的功能總是使它們保持打開狀態,因為它們總是可以被訪問和更改。

如果我錯了,請評論:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM