如何限制在构造函数之外设置属性?

ozg*_*gur 4 python variable-assignment setattr

我想在初始化之后禁止对类的某些属性进行进一步的赋值.例如; 在初始化Person实例'p'之后,没有人能够明确地将任何值分配给'ssn'(社会安全号码)属性._ SETATTR _而分配中的数值也被称为_ INIT _方法,因此它是不是我想要的.我想仅限制进一步的作业.我怎样才能做到这一点?

class Person(object):
    def __init__(self, name, ssn):
        self.name = name
        self._ssn = ssn

    def __setattr__(self, name, value):
        if name == '_ssn':
            raise AttributeError('Denied.')
        else:
            object.__setattr__(self, name, value)

>> p = Person('Ozgur', '1234')
>> AttributeError: Denied.
Run Code Online (Sandbox Code Playgroud)

Sve*_*ach 10

通常的方法是使用以下划线开头的"private"属性和用于公共访问的只读属性:

import operator

class Person(object):
    def __init__(self, name, ssn):
        self.name = name
        self._ssn = ssn
    ssn = property(operator.attrgetter("_ssn"))
Run Code Online (Sandbox Code Playgroud)

请注意,这并不会真正阻碍任何人更改属性_ssn,而是_属性为私有的主要文档.

  • +1.Python的理念是,不要使用语言强制执行"你不能触及这个",而是使用以`_`开头的属性来表示实现细节 - 这意味着客户*可以*尝试摆弄它,但它们不再被覆盖通过任何保证你做的关于向后/向前兼容性或类不变量.所以,他们可以摆弄,他们可以破坏东西,但这是他们自己该死的错. (4认同)

小智 6

Python 不支持私有或受保护的属性。您需要改为实现描述符协议。标准库提供了装饰器来简洁地做到这一点。

只需在init方法中用两个下划线前面的属性声明。它被称为名称修改并阻止通过 __ssn 访问该属性,尽管在这种情况下它仍然可以被 _Person__ssn 访问和修改。但是,如果您没有为它明确定义 setter,则会引发 AttributeError。

当然,如果有人有意滥用 API,那么如果他非常有意的话,他可以这样做。但它不会偶然发生。

import re

class Person:
    """Encapsulates the private data of a person."""

    _PATTERN = re.COMPILE(r'abcd-efgh-ssn')

    def __init__(self, name, ssn):
       """Initializes Person class with input name of person and
       his social security number (ssn).
       """
       # you can add some type and value checking here for defensive programming
       # or validation for the ssn using regex for example that raises an error
       if not self._PATTERN.match(ssn):
           raise ValueError('ssn is not valid')
       self.__name = name
       self.__ssn = snn

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, value):
        self.__name = value

    @property
    def ssn(self):
        return self.__ssn

>>> p = Person('aname', 'abcd-efgh-ssn')
>>> p.ssn
'abcd-efgh-ssn'
>>> p.ssn = 'mistake'
AttributeError: 'can't set attribute'
Run Code Online (Sandbox Code Playgroud)


sha*_*han 5

只是指出我们仍然可以修改_ssn.

对象具有特殊的属性,__dict__即一个字典,将对象的所有实例属性与其对应的值进行映射。我们可以通过修改__dict__一个对象的属性来直接添加/更新/删除实例属性。

我们仍然可以_snn这样修改:

p = Person('Ozgur', '1234')

p.__dict__.get('_ssn') # returns '1234'

p.__dict__['_ssn'] = '4321'

p.__dict__.get('_ssn') # returns '4321'
Run Code Online (Sandbox Code Playgroud)

如我们所见,我们仍然能够更改 的值_ssn。按照设计,没有一种直接的方法(如果有的话)在所有情况下都绕过 Python 中的属性访问。

我将展示一种使用property()作为装饰器来限制属性访问的更常见方法:

class Person(object):
    def __init__(self, name, ssn):
        self.name = name
        self._ssn = ssn

    @property
    def ssn(self):
        return self._ssn

    @ssn.setter
    def ssn(self, value):
        raise AttributeError('Denied')


>> p = Person('Ozgur', '1234')
>> p.ssn
>> '1234'
>> p.ssn = '4321'
>> AttributeError: Denied
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!