【问题标题】:Data Hiding in Python ClassPython 类中的数据隐藏
【发布时间】:2012-01-26 20:41:38
【问题描述】:

我知道由双下划线__ 前缀声明的类的属性在类定义之外可能可见也可能不可见。因为我们仍然可以通过object._className__attrName 访问这些属性。

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
        ----
        ----
        self.z = 26
        self.catch = 100

现在为了保护除catch 属性之外的所有属性,我必须用双下划线声明它们,这很混乱。有没有办法在我的类定义中说只有self.catch 可以在类外访问?

如果这个问题在其他地方得到回答或之前讨论过,我们深表歉意。

【问题讨论】:

  • 没有。但是,除非您真的很偏执,否则某人可以使用object._className__attrName 访问对象这一事实并不是什么大问题。您真的只是想防止人们在使用您的代码时意外访问他们不应该访问的变量。混淆实现了这个目标。
  • @BjörnPollex,这不是他想要的。他希望需要一个明确的“公共”“运营商”,而不是相反。
  • 你真的需要双下划线吗?为什么不直接使用更标准的单下划线self._a

标签: python


【解决方案1】:

是的,可以在闭包中隐藏私有数据——至少,如果有办法从外部make_A 访问private,我还没有找到它:

def make_A():
    private = {
        'a' : 1,
        'b' : 2,
        'z' : 26,
        }
    class A:
        def __init__(self):
            self.catch = 100
            private['a'] = 2    # you can modify the private data
        def foo(self):
            print(private['a']) # you can access the private data 
    return A

A = make_A()

a=A()

a.foo()
# 2

注意private 不在dir(a)

print('private' in dir(a))
# False

虽然这是可能的,但我不认为这是在 Python 中编程的可推荐方式。


在上面,privateA 的所有实例共享。要在每个实例的基础上使用私有数据,请将 self 添加到 dict 键:

def make_A():
    private = {}
    class A:
        def __init__(self):
            self.catch = 100
            private[self,'a'] = 1    # you can modify the private data
            private[self,'b'] = 2    
            private[self,'z'] = 26    
        def foo(self):
            print(private[self,'a']) # you can access the private data 
    return A

【讨论】:

  • *closures 点赞。这可能很有趣:Secret Variables i Python。它将 Python 中隐藏的函数数据与 Java 中的私有变量进行比较。
  • 看起来不错,但是会导致内存泄漏,因为一旦创建了A的实例,就会一直被dictprivate引用。 Python 无法对它进行 GC,直到 private 被 GC'ed。
【解决方案2】:

虽然unutbu 接受的答案似乎是隐藏数据的好主意,但仍可使用__closure__ 访问private 字典(此属性无法删除):

def make_A():
    private = {}
    class A:
        def __init__(self):
            self.catch = 100
            private[self,'a'] = 1    # you can modify the private data
            private[self,'b'] = 2    
            private[self,'z'] = 26    
        def foo(self):
            print(private[self,'a']) # you can access the private data
    return A


A = make_A()
a = A()
a.foo()  # 1
a.foo.__closure__[0].cell_contents[(a, 'a')] = 42
a.foo()  # 42

或者点击cmets中Sumukh Barve提供的链接:

def createBankAccount():
    private = {'balance': 0.0}
    def incr(delta):
        private['balance'] += delta;
    account = {
        'deposit': lambda amount: incr(amount),
        'withdraw': lambda amount: incr(-amount),
        'transferTo': lambda otherAccount, amount: (
            account['withdraw'](amount),
            otherAccount['deposit'](amount)
        ),
        'transferFrom': lambda otherAccount, amount: (
            otherAccount['transferTo'](account, amount)
        ),
        'getBalance': lambda : private['balance']
    }
    return account;


account = createBankAccount()

print(account['getBalance']())
account['getBalance'].__closure__[0].cell_contents['balance'] = 1000**1000
print(account['getBalance']())  # I can buy a couple of nations

我讨厌创建 private 属性的唯一方法是编写某种CPython 扩展。

【讨论】:

    【解决方案3】:

    不,因为它是名称的一部分,所以你不能这样做。

    嗯,实际上你可以通过 getter/setter 进行黑客攻击,但通常不应该这样做。

    【讨论】:

      【解决方案4】:

      为什么您可能想要保护属性?

      如果您想保护属性不被无意覆盖,请使用双下划线,并且可能还使用现成的属性以使访问更容易。但大多数时候,you aint't gonna need it。帮自己一个忙,让属性保持打开状态,这样调试会容易得多。

      如果您想保护您的对象不被第三方篡改,您可以使用__getattr____setattr__,但可能您根本不应该将带有受保护数据的对象传递给不受信任的客户端,请使用facade pattern相反,更不透明地隐藏您的贵重物品。您可能也想重新考虑您的架构。

      【讨论】:

      • 您为什么要保护属性? 我可以将您指向关于封装的 wiki 条目,但我会给您一个实际示例。假设您有一个大型且复杂的 python 应用程序,其中有许多具有“名称”属性的类。发现一个错误,其中类管理员的名称被错误地覆盖。如果您按照您的建议将 name 属性保持打开状态,您将如何找到有问题的代码行?如果你隐藏了属性并使用了访问器,你可以在getter上下一个断点,快速找到罪魁祸首。
      • @dar512:如果您的类是公共接口的一部分,并且客户端可能会篡改属性,请务必通过访问器保护它们!但是,在典型应用程序中,公共类的数量相当有限。此外,使用不可变数据具有许多优点;我经常使用namedtuple 的子类以防弹的方式表示数据。
      • 作为一名程序员,如果我选择用霰弹枪射断自己的腿,那就这样吧。您通过隐藏东西来限制我修复/更改代码的能力。 'protected' 和 'private' 关键字只是反模式。请放弃通过“隐藏”其他程序员的事情来帮助任何人的想法。如果您想做这样的事情,请使用_variable 并提供variable getter 属性而不提供setter。如果我愿意,我仍然可以通过这种方式手动修改_variable。但不要使用语言技巧来混淆其他程序员的内部信息。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 2013-11-21
      • 1970-01-01
      • 1970-01-01
      • 2021-05-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多