【问题标题】:Python class which instances' atributes can't be edited无法编辑实例属性的 Python 类
【发布时间】:2013-01-14 18:22:31
【问题描述】:

创建一个实例的属性不能从类外部更改的类最方便的方法是什么(您仍然可以获取值),因此可以在类的方法内调用self.var = v,而不是在类外调用ClassObject().var = v

我尝试过使用__setattr__(),但如果我覆盖它,name 属性将无法在__init__() 方法中启动。唯一的方法是覆盖__setattr__() 并使用object.__setattr__(),我现在正在这样做:

class MyClass(object):
    def __init__(self, name):
        object.__setattr__(self, "name", name)
    def my_method(self):
        object.__setattr__(self, "name", self.name + "+")
    def __setattr__(self, attr, value):
        raise Exception("Usage restricted")

现在这个解决方案有效,已经足够了,但我想知道是否有更好的解决方案。这个问题是:我仍然可以从课堂外的任何地方拨打object.__setattr__(MyClass("foo"), "name", "foo_name")

有什么方法可以完全防止将变量设置为类外的任何内容?

编辑:愚蠢的我更不用说我不是在这里寻找property,你们中的一些人已经回答了,但这对我来说还不够,因为它会使self._name 可变。

【问题讨论】:

  • 不,没有。无论如何,不​​是在纯 Python 中。为什么需要这个?
  • 不可能,但值得一问的是,为什么你甚至想要做这样的事情?
  • 也许我需要一个只读属性。一个不错的例子是STEAM_ID,以防你们使用过 Steam。
  • 不,您不需要只读属性。从来没有(我的意思是)任何理由完全阻止设置属性。使用@property 将表明您不应该这样做,这就是您需要的所有“保护”。
  • 如果您从来不需要它,这并不意味着其他任何人都不需要它。我有,如果你不同意,你就知道不够。

标签: python class methods python-3.x instance


【解决方案1】:

不,你不能在纯 python 中做到这一点。

您可以使用属性将您的属性标记为只读;改用下划线前缀的“私有”属性:

class Foo(object):
    def __init__(self, value):
        self._spam = value

    @property
    def spam(self):
        return self._spam

上面的代码只为属性指定了一个getter; Python 现在不允许你为Foo().spam 设置值:

>>> class Foo(object):
...     def __init__(self, value):
...         self._spam = value
...     @property
...     def spam(self):
...         return self._spam
... 
>>> f = Foo('eggs')
>>> f.spam
'eggs'
>>> f.spam = 'ham'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

当然,您仍然可以从外部访问“私有”_spam 属性:

>>> f._spam
'eggs'
>>> f._spam = 'ham'
>>> f.spam
'ham'

您可以使用双下划线约定,在编译时重命名开头带有__ 的属性名称(但不是结尾!)。这并不是为了使属性无法从外部访问,而是为了保护属性不被子类覆盖。

class Foo(object):
    def __init__(self, value):
        self.__spam = value

    @property
    def spam(self):
        return self.__spam

您仍然可以访问这些属性:

>>> f = Foo('eggs')
>>> f.spam
'eggs'
>>> f.__spam
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__spam'
>>> f._Foo__spam
'eggs'
>>> f._Foo__spam = 'ham'
>>> f.spam
'ham'

【讨论】:

  • 不是在找property,但既然你也回答说不能做,可能有人在找property,我就接受你的回答。
【解决方案2】:

在 Python 上没有严格的封装方式。您可以做的最好的事情是在预期的私有属性前面加上 2 个下划线 __。这将导致它们被类名 (_ClassName_AttribName) 破坏,因此如果您尝试在继承的类上使用它们,则不会引用基成员。但是,如果您使用 getattrib()setattrib(),名称不会被破坏。

您也可以覆盖 __dir()__ 以隐藏它们。

【讨论】:

  • 也感谢您的回答,我也可以接受这个,并给您点赞!
【解决方案3】:

您可以使用属性来模拟这种行为,但就像 Martijn 所说,可以直接访问变量。 这样做是您不了解 Python 哲学的信号,请检查一下。

Why Python is not full object-oriented?

属性方式:

http://docs.python.org/2/library/functions.html#property

class C(object):
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x
    def setx(self, value):
        raise Exception("Usage restricted")
    x = property(getx, setx)

【讨论】:

  • 这不符合 OP 的要求。
  • 其实 OP 想要的在 python 中是不可能的,至少不遵循禅宗。
  • 是的,我知道。给出答案的方式使它看起来是可能的,这就是我投反对票的原因。如果您在回答中提出“不可能”的观点,我将删除我的反对票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-24
  • 2021-05-20
  • 1970-01-01
  • 1970-01-01
  • 2019-04-10
  • 2018-06-12
相关资源
最近更新 更多