【问题标题】:Private attributes in Python and PEP8Python 和 PEP8 中的私有属性
【发布时间】:2020-10-22 13:53:34
【问题描述】:

我刚刚重新阅读PEP8 并偶然发现了这一行:

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

我只是想知道如果我想让一个方法真正私有化,这种“不必要的工作量”会带来什么?

【问题讨论】:

  • 这意味着您可以将某些内容设为私有但需要大量工作
  • @drum,是的,我明白这一点。但这项工作究竟包括什么?
  • 在 Python 中将方法或属性设为完全私有几乎是不可能的

标签: python private


【解决方案1】:

在纯 python 中,最接近“真正”封装的是通过闭包中的变量(因为一旦函数返回,您就无法直接访问/分配它们)。这是一个小草图:

def OneTruePrivate():
    """hax which uses a closure to make _truly_ private variables"""
    x = 1

    class OneTruePrivate:
        @property
        def val(self):
            return x

        def increment(self):
            nonlocal x
            x += 1

    return OneTruePrivate()


var = OneTruePrivate()
print('initial value')
print(var.val)
var.increment()
print('after incremnet')
print(var.val)
print('look ma, no object state!')
import pprint; pprint.pprint(vars(var))

输出:

initial value
1
after incremnet
2
look ma, no object state!
{}

也就是说,即使有一些骇客,你也可以改变底层的闭包值

cell = var.increment.__func__.__closure__[0]
import ctypes
ctypes.pythonapi.PyCell_Set.argtypes = (ctypes.py_object, ctypes.py_object)
ctypes.pythonapi.PyCell_Set(cell, 9001)
print(var.val)

输出:

9001

【讨论】:

  • 另外,在 Python 3.8 中,您可以直接设置单元格的 cell_contents
【解决方案2】:

对此有一个范围。更多的隐私需要更多的工作,但更难绕过。 “真正私密”没有明确的界限。

前导双下划线是最不私密的选项。在属性上添加前导双下划线很容易,但也很容易通过手动名称修改绕过。

代码和绕过:

class Private1:
    def __init__(self, data):
        self.__data = data
    def get_data(self):
        return self.__data

x = Private1(1)

# Direct access fails...
x.__data = 2

# but you can do the name mangling manually.
x._Private1__data = 2

接下来是闭包变量或隐藏槽之类的东西。您无法通过名称修改来访问它们,但您仍然可以手动访问闭包单元或找出插槽吸气剂的位置。

闭包变量示例,带旁路:

class Private2:
    def __init__(self, data):
        def get_data():
            return data
        self.get_data = get_data

x = Private2(1)

# It's not immediately obvious how you'd even try to access the data variable directly,
# but you can:
x.get_data.__closure__[0].cell_contents = 2

隐藏槽示例,带旁路:

class Private3:
    __slots__ = ('data',)
    def __init__(self, data):
        _hidden_slot.__set__(self, data)
    def get_data(self):
        return _hidden_slot.__get__(self, type(self))
_hidden_slot = Private3.data
del Private3.data

x = Private3(1)

# Direct access fails...
x.data = 2

# but you can directly access the slot getter the same way the actual class did:
_hidden_slot.__set__(x, 2)

之后的下一步将是 C 扩展。手动编写一个 C 扩展需要做很多工作,我不打算再举一个例子了(但这里有一个 tutorial linkCython 使它更容易),但在 C 中实现的类型不会默认情况下在 Python 级别公开其内部数据。如果该类型没有特别努力提供访问权限,那么访问数据的唯一方法是使用更多 C,或者使用 ctypesgc.get_referents 之类的东西(如果隐藏数据是 GC 公开的 Python 引用) . (正确使用gc.get_referents也可以绕过上述所有其他保护。)

之后的下一步是将数据保存在您自己的私有服务器上,并且只允许客户端通过 Internet API 访问它。这比任何private 关键字都更加私密,绕过它需要诸如漏洞利用、传票或身体暴力之类的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-19
    • 2021-11-22
    • 2017-06-27
    • 2012-02-10
    • 1970-01-01
    • 2017-11-12
    • 2010-09-29
    相关资源
    最近更新 更多