Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何限制在构造函数之外设置属性?_Python_Variable Assignment_Setattr - Fatal编程技术网

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

Python 如何限制在构造函数之外设置属性?,python,variable-assignment,setattr,Python,Variable Assignment,Setattr,我想禁止在类初始化后对其某些属性进行进一步赋值。例如;在初始化Person实例“p”之后,任何人都不能显式地将任何值分配给“ssn”(社会保险号码)属性\u setattr在\u init方法中赋值时也被调用,因此这不是我想要的。我只想限制进一步的任务。我怎样才能做到这一点 class Person(object): def __init__(self, name, ssn): self.name = name self._ssn = ssn d

我想禁止在类初始化后对其某些属性进行进一步赋值。例如;在初始化Person实例“p”之后,任何人都不能显式地将任何值分配给“ssn”(社会保险号码)属性\u setattr\u 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.
通常的方法是使用以下划线开头的“private”属性和用于公共访问的只读属性:

import operator

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

请注意,这实际上并不妨碍任何人更改属性
\u ssn
,但前面的
\u
说明该属性是私有的

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


只是指出我们仍然可以修改
\u ssn

对象有一个特殊属性,
\uuu dict\uu
,它是一个字典,用于将对象的所有实例属性与其对应的值进行映射。我们可以通过修改对象的
\u dict\u
属性直接添加/更新/删除实例属性

我们仍然可以这样修改
\u snn

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

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

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

p.__dict__.get('_ssn') # returns '4321'
正如我们所看到的,我们仍然能够更改
\u ssn
的值。根据设计,没有一种直接的方法(如果有的话)可以在所有情况下绕过Python中的属性访问

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

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

希望这有帮助

Python不支持私有或受保护的属性。您需要实现描述符协议。标准库提供了decorator来简洁地完成这项工作

只需在init方法中声明前面有两个下划线的属性。它被称为名称混乱(name mangling),防止通过u ssn访问该属性,尽管在这种情况下u Person_ussn仍然可以访问和修改该属性。但是,如果没有显式定义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'

仅仅因为
\uuuu init\uuuu
中的赋值也会调用
\uuuu setattr\uuuuu
,我认为您可能必须尝试使用metaclassFYI,这并不是Python真正的工作方式。记录您的API以告诉其他开发人员不要更改它。@detly:Python的通常理念是采取措施防止意外违约,但不要费心防止故意违约。@SvenMarnach-true;由OP将规则应用于他或她的情况。+1。Python的理念是,不要强制使用语言“你不能碰它”,而是使用以
开头的属性来表示实现细节——这意味着客户机可以尝试处理它,但它们不再受到你对向后/向前兼容性或类不变量的任何保证的影响。所以,他们可以摆弄,他们可以打破东西,但这都是他们自己的错。
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'