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

时间:2012-06-07 09:19:51

标签: python variable-assignment setattr

我希望在初始化后禁止对类的某些属性进行进一步的赋值。例如;没有人可以明确地将任何价值分配给' ssn' Person实例后的(社会安全号码)属性' p'已初始化。在 _ init _ 方法中分配值时,也会调用 _ setattr _ ,因此它不是什么我想要。我想仅限制进一步的作业。我怎样才能做到这一点?

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.

4 个答案:

答案 0 :(得分:9)

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

import operator

class Person(object):
    def __init__(self, name, ssn):
        self.name = name
        self._ssn = ssn
    ssn = property(operator.attrgetter("_ssn"))

请注意,这并不会真正阻碍任何人更改属性_ssn,但该属性为私有的前导_文档。

答案 1 :(得分:3)

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

只需使用 init 方法声明该属性,并在其前加两个下划线即可。这称为名称修改,它防止通过__ssn访问该属性,尽管在这种情况下仍可以由_Person__ssn访问和修改该属性。但是,如果未明确定义设置器,则会引发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'

答案 2 :(得分:2)

您可以通过直接指定构造函数中的实例字典来绕过 setattr 。当然,这个技巧总是可以用来欺骗你用来使_ssn只读的任何其他箍(在初始写入之后)

 class Person(object): 
    def __init__(self, name, ssn):    
        self.name = name
        self.__dict__['_ssn'] = ssn 

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

答案 3 :(得分:0)

仅指出我们仍可以修改_ssn

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

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

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

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

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

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

我们可以看到,我们仍然能够更改_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

希望这会有所帮助!