Python - 如果在 class 中使用了未声明的变量,则强制出错

Python - Force an error if an undeclared variable is used inside a class

考虑以下 Python 代码:

class a_class:
    defined_var = 1

    def a_function(self):
        self.defined_var = 2
        self.undefined_var = 3 

可以为我在 class 开始时未声明的变量赋值(然后隐式声明它)(就像我为 undefined_var 在前面的例子中)给我带来了几个问题,因为对于巨大的 classes 我忘记了我定义的和我没有定义的。

我知道这个问题听起来很傻。我已经使用 C/C++/Java 开发了很长一段时间,其中 class 中的变量定义是强制性的...

有没有办法避免这种情况?我的意思是,我想要像 C/C++/Java 这样的行为,如果我使用未定义的变量,我会收到错误消息。

您可以覆盖 object.__setattr__:

class a_class:
    defined_var = 1

    def a_function(self):
        self.defined_var = 2
        self.undefined_var = 3

    def __setattr__(self, key, value):
        if hasattr(self, key):
            object.__setattr__(self, key, value)
        else:
            raise AttributeError(f"\"{key}\" is not defined in \"{self.__class__.__name__}\"")

a = a_class()
print(a.defined_var)
a.a_function()  # <-- throws an error

P.S. defined_var 是所有实例共享的 class 变量。这意味着所有更改都会影响 class 的所有实例(也许这不是您所期望的)。更多信息 here.

默认情况下,python 个实例有 __dict__ - a dictionary that stores all of the object's attributes. This is what allows them to store arbitrary attributes. You can suppress the creation of the __dict__ by defining __slots__。这是一个 class 级变量,列出了 class 的所有属性实例的名称。如果 class 有 __slots__,尝试分配给未定义的属性将抛出异常。

示例:

class MyClass:
    __slots__ = ['defined_var']

    def a_function(self):
        self.defined_var = 2

obj = MyClass()
obj.a_function()
obj.undefined_var = 3  # throws AttributeError

只需在此处添加一些注释:

Python 的创建者 Guido van Rossum 表示,他实际上创建了 slots 以实现更快的属性访问。

from book fluent python__slots__ 用于更快的属性访问和减少内存使用,而不是阻止用户添加更多属性。

检查此代码:

class MyClass:
    __slots__ = ['defined_var', '__dict__']

obj = MyClass()
obj.undefined_var = 3  # works fine

使用__slots__时的问题,如果你创建这个class的子class,你也需要在其中重复__slot__属性。

class MyClass:
    __slots__ = ['defined_var']

class MySubClass(MyClass):
    # you need to redefine the __slots__ here too
    pass

obj = MyClass()
sub_obj = MySubClass()
sub_obj.undefined_var = 3  # works fine not excepting this
obj.undefined_var = 3  # throw exception

因此,如果您真的需要这个,请按照@Olvin Roght 的建议重写 setattr 方法。

class MyClass:
    x = None 
    def __setattr__(self, key, value):
        if hasattr(self, key):
            object.__setattr__(self, key, value)
        else:
            raise AttributeError(f"\"{key}\" is not defined in \"{self.__class__.__name__}\"")


class MySubClass(MyClass):
    pass


obj = MyClass()
sub_obj = MySubClass()
sub_obj.x = 3  # works fine
obj.x = 3  # works fine
sub_obj.undefined_var = 3  # error
obj.undefined_var = 3  # error  

如果有一天你想对你的对象进行猴子修补:

obj = MyClass()
obj.__dict__['z'] = 4
print(obj.z)  # print 4, we added a new attribute

所以不要担心阻止客户端添加新属性,这是一个坏习惯。你会阻止你自己控制你的对象并改变它的行为,消除 python 给你的灵活性,你为什么要那样。