当前位置: 首页 > 知识库问答 >
问题:

使 Python 类在创建新属性时引发错误

黄宏毅
2023-03-14

假设这是我的类:

class A:
    def __init__(self):
        self.good_attr = None
        self.really_good_attr = None
        self.another_good_attr = None

然后调用者可以设置这些变量的值:

a = A()
a.good_attr = 'a value'
a.really_good_attr = 'better value'
a.another_good_attr = 'a good value'

但它们也可以添加新属性:

a.goood_value = 'evil'

这对于我的用例来说是不可取的。我的对象用于将许多值传递到一组方法中。(因此,从本质上讲,此对象替换了几个方法上的一长串共享参数,以避免重复并清楚地区分共享和不同内容。如果调用方键入了属性名称,则该属性将被忽略,从而导致意外和混淆,并且可能难以弄清楚的行为。最好快速失败,通知调用方他们使用的属性名称将被忽略。因此,类似于以下内容的内容是当他们使用对象上尚不存在的属性名称时我想要的行为:

>>> a.goood_value = 'evil'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'goood_value'

我如何才能做到这一点?

我还想指出,我完全知道调用者可以创建一个新类,做他们想做的任何事情,完全绕过它。不过,这将是不受支持的行为。制作我提供的对象只是创建了一个快速失败的愚蠢检查,以节省那些利用我提供的对象(包括我自己)的人防止错别字的时间,而不是让他们挠头,想知道为什么事情会以意想不到的方式发生。

共有3个答案

呼延智明
2023-03-14

通过向参数添加两个下划线(例如self.__good_attr)使参数私有,这样就有人无法在类之外设置该参数。然后创建一个函数来设置__good_attr变量,并让该函数在错误时引发异常。

东方河
2023-03-14

一个更惯用的选项是使用命名元组。

在Python 3.6及更高版本中,可以使用键入。NamedTuple轻松实现这一点:

from typing import NamedTuple, Any

class A(NamedTuple):
    good_attr: Any = None
    really_good_attr: Any = None
    another_good_attr: Any = None

如果需要,可以使用更具体的类型约束,但是必须包含注释,以便< code>NamedTuple能够获取属性。

这不仅会阻止添加新属性,还会阻止设置现有属性:

>>> a = A()
>>> a.goood_value = 'evil'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'goood_value'
>>> a.good_attr = 'a value'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

这将强制您在构造时指定所有值:

a = A(
    good_attr='a value',
    really_good_attr='better value',
    another_good_attr='a good value',
)

这样做通常不是问题,如果是,可以明智地使用局部变量来解决。

这些版本的Python要么没有键入模块,要么没有键入。命名图普尔不能像上面使用的那样工作。在这些版本中,可以使用集合.

定义该类很简单:

from collections import namedtuple

A = namedtuple('A', ['good_attr', 'really_good_attr', 'another_good_attr'])

然后施工作业如上:

a = A(
    good_attr='a value',
    really_good_attr='better value',
    another_good_attr='a good value',
)

但是,这不允许在调用构造函数时省略一些值。在构造对象时,可以显式包含< code>None值:

a = A(
    good_attr='a value',
    really_good_attr=None,
    another_good_attr='a good value',
)

或者,可以使用以下几种技术之一为参数提供默认值:

A.__new__.func_defaults = (None,) * 3
a = A(
    good_attr='a value',
    another_good_attr='a good value',
)
雷浩思
2023-03-14

您可以使用__setattr__方法连接到属性设置。此方法用于所有属性设置,因此也会为“正确”属性调用:

class A(object):
    good_attr = None
    really_good_attr = None
    another_good_attr = None

    def __setattr__(self, name, value):
        if not hasattr(self, name):
            raise AttributeError(
                '{} instance has no attribute {!r}'.format(
                    type(self).__name__, name))
        super(A, self).__setattr__(name, value)

因为good_attr等是在类上定义的,所以hasattr()call会为这些属性返回 ,并且不会引发异常。您也可以在 __init__中设置这些相同的属性,但必须在类上定义这些属性, hasattr()才能工作。

另一种方法是创建一个可以测试的白名单。

演示:

>>> a = A()
>>> a.good_attr = 'foo'
>>> a.bad_attr = 'foo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 10, in __setattr__
AttributeError: A instance has no attribute 'bad_attr'

当然,坚定的开发人员仍然可以通过向a.__dict__实例字典添加键来向您的实例添加属性。

另一种选择是使用< code>__slots__的副作用;slots用于节省内存,因为字典比直接将值放入Python为每个实例创建的C结构中需要更多的空间(那时不需要键和动态表)。副作用是在这样的类实例上没有更多属性的位置:

class A(object):
    __slots__ = ('good_attr', 'really_good_attr', 'another_good_attr')

    def __init__(self):
        self.good_attr = None
        self.really_good_attr = None
        self.another_good_attr = None

然后,错误消息如下所示:

>>> a = A()
>>> a.good_attr = 'foo'
>>> a.bad_attr = 'foo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'bad_attr'

但请阅读文档中列出的使用__slots__的注意事项。

由于在使用__slots__时没有__dict__实例属性,因此此选项实际上关闭了在实例上设置任意属性的大门。

 类似资料:
  • 问题 你想创建一个新的拥有一些额外功能的实例属性类型,比如类型检查。 解决方案 如果你想创建一个全新的实例属性,可以通过一个描述器类的形式来定义它的功能。下面是一个例子: # Descriptor attribute for an integer type-checked attribute class Integer: def __init__(self, name):

  • 我是弹性搜索的新手,我正在尝试使用下面的映射创建索引,我在网上找到了这些映射,并使用kibana作为我的客户机,它抛出错误。 “类型”:“映射程序解析异常”,“原因”:“根映射定义有不受支持的参数:[local_test:{u all={enabled=false},properties={amount={type=long},user_id={type=keyword},recurtive={t

  • 问题内容: 在以下代码中,我创建了一个基本抽象类。我希望所有继承自其的类都提供该属性,因此我将该属性设置为。 然后,我创建了一个名为的子类,该子类旨在提供一些功能,但仍保持抽象。中没有属性,但是python实例化了该类的对象而没有错误。一个人如何创建抽象属性? 问题答案: 从Python 3.3 开始,修复了一个错误,这意味着装饰器现在应用于抽象方法时,可以正确地标识为抽象。 注:订单的问题,你必

  • 我用Python编写了一个类,这样就可以从中继承。它的逻辑按预期工作,但我可以从名为的派生类中存在的状态访问属性。 生成以下错误: state_machine.py main.py

  • 问题内容: 我希望能够创建一个用Python初始化的类(在Python中),该类不接受新属性,但接受对现有属性的修改。我可以通过几种方法来做到这一点,例如,使用诸如 然后直接在内部进行编辑,但是我想知道是否存在“正确”的方法? 问题答案: 我不会直接使用,但是您可以添加一个函数来显式“冻结”实例:

  • 使用WicketTester时,访问会话的属性会引发NullPointerException错误。 wicket 9之前也有类似的问题:单元测试mockito httpSession 讨论建议使用 测试员。getRequest()。getSession()。setAttribute(“用户名”、“测试用户1”); 而不是 测试员。getHttpSession()。setAttribute(“用户名