Python类中的数据隐藏

Python类中的数据隐藏,python,Python,我知道,在类定义之外,用双下划线\uuu前缀声明的类属性可能可见,也可能不可见。因为我们仍然可以通过对象访问这些属性 class A: def __init__(self): self.a = 1 self.b = 2 ---- ---- self.z = 26 self.catch = 100 现在,为了保护除属性catch之外的所有属性,我必须用双下划线声明它们,这非常混乱。在我的类定义

我知道,在类定义之外,用双下划线
\uuu
前缀声明的类属性可能可见,也可能不可见。因为我们仍然可以通过
对象访问这些属性

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
        ----
        ----
        self.z = 26
        self.catch = 100
现在,为了保护除属性
catch
之外的所有属性,我必须用双下划线声明它们,这非常混乱。在我的类定义中,是否有一种方法可以说只有
self.catch
可以在类外访问


如果您在其他地方回答或之前讨论过此问题,请道歉。

不,因为它是名称的一部分,您不能这样做


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

是的,可以在闭包中隐藏私有数据——至少,如果有一种方法可以从外部
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编程的推荐方式


上面,
private
A
的所有实例共享。要基于每个实例使用私有数据,请将
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
为什么要保护属性

如果要保护属性不被意外覆盖,请使用双下划线,可能还可以使用仅准备好的属性,以使访问更容易。但大多数时候。只需帮自己一个忙,让属性保持打开状态,这将使调试更加容易


如果您想保护您的对象不被第三方篡改,您可以使用
\uuuu getattr\uuuuu
\uuuu setattr\uuuuu
,但您可能根本不应该将受保护的数据传递给不受信任的客户端对象,而是使用,并更不透明地隐藏您的贵重物品。您可能还需要重新考虑您的体系结构。

虽然
unutbu
接受的答案看起来是隐藏数据的好主意,但是仍然可以使用
\uuuu closure\uuu
访问
私有
词典(无法删除此属性):

或者按照注释中的
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

我认为创建
私有属性的唯一方法是编写某种
CPython
扩展。

不。但是,除非你真的有妄想症,否则有人可以使用
对象访问对象这一事实并不是什么大问题。您真的只是想防止人们在使用代码时意外访问他们不应该访问的变量。模糊处理实现了这个目标。可能是@BjörnPollex的复制品,这不是他想要的。他想要一个明确的“公共”操作符,而不是相反。你真的需要双下划线吗?为什么不使用更标准的单下划线
self.\u a
?为什么要保护属性?我可以向您指出关于封装的wiki条目,但我将给您一个实际示例。假设您有一个大型而复杂的python应用程序,其中有许多属性为“name”的类。发现一个错误,错误地覆盖了类管理员的名称。如果您按照您的建议将name属性保持打开状态,您将如何找到有问题的代码行?如果您隐藏了属性并使用了访问器,您可以在getter上设置一个断点,然后快速找到罪魁祸首。@dar512:如果您的类是公共接口的一部分,并且客户端可能会篡改属性,请务必使用访问器来保护它们!但是,在典型应用程序中,公共类的数量相当有限。而且,使用不可变数据有许多优点;我经常使用
namedtuple
的子类以防弹的方式表示数据。作为一名程序员,如果我选择用猎枪射击自己的腿,那就这样吧。你通过隐藏东西限制了我修复/修改代码的能力。“protected”和“private”关键字只是反模式。请不要认为你在对其他程序员“隐藏”东西是在帮任何人的忙。如果要执行类似操作,请使用
\u variable
并提供
variable
getter属性和no setter。如果我愿意,我仍然可以用这种方式手动修改
\u变量
。但是,不要使用语言黑客来混淆你的内部代码,不要让其他程序员混淆你的内部代码。这可能会引起兴趣:。它将Python中隐藏的函数数据与Java中的私有变量进行了比较。看起来不错,但这会导致内存泄漏,因为一旦创建了
A
的实例,dict
private
将始终引用它。在对
private
进行GC之前,Python无法对其进行GC。
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