我试图确切地理解__setattr__
是如何处理类属性的,这个问题是在我试图重写__setattr__
以防止属性被写入一个简单的类时出现的。
我第一次尝试使用示例级属性,如下所示:
class SampleClass(object):
def __init__(self):
self.PublicAttribute1 = "attribute"
def __setattr__(self, key, value):
raise Exception("Attribute is Read-Only")
我原本以为只有外部设置属性的尝试才会抛出错误,但即使是__init__
中的语句也会导致抛出异常。
后来我找到了另一种方法来解决这个问题,同时试图解决另一个问题。我最终得到的是:
class SampleClass(object):
PublicAttribute1 = "attribute"
def __setattr__(self, key, value):
raise Exception("Attribute is Read-Only")
我期望得到相同的结果,但令我惊讶的是,我能够设置class属性,同时防止在初始声明之后进行更改。
我不明白为什么会这样。我知道这与类和示例变量有关。我的理论是在类变量上使用__setattr__
会创建同名的示例变量,因为我相信我以前见过这种行为,但我不确定。
有人能解释一下到底发生了什么吗?
5条答案
按热度按时间dw1jzc5e1#
__setattr__()
只适用于类的示例。在第二个示例中,当您定义PublicAttribute1
时,您是在类上定义它;没有示例,所以__setattr__()
不会被调用.注意:在Python中,使用
.
表示法访问的对象被称为属性,而不是变量(在其他语言中,它们可能被称为“成员变量”或类似的名称)。如果在示例上设置同名属性,class属性将被隐藏,这是正确的。例如:
Python通过首先查看示例来解决属性访问问题,如果示例上没有该名称的属性,它会查看示例的类,然后是该类的父类,依此类推,直到到达
object
,Python类层次结构的根对象。所以在上面的例子中,我们在类上定义了
attr
。因此,当我们访问c.attr
时(示例属性),我们得到类上属性的值42,因为示例上没有这个属性,当我们设置示例的属性,然后再次打印c.attr
,我们得到刚才设置的值,因为在示例中有一个属性使用这个名字,但是值42
仍然作为类C.attr
的属性存在,正如我们看到的第三个print
。在
__init__()
方法中设置instance属性的语句由Python处理,就像任何设置对象属性的代码一样。Python不关心代码是在类的“内部”还是“外部”。所以,你可能会想,在初始化对象时,如何绕过__setattr__()
的“保护”?简单:你调用一个类的__setattr__()
方法,而这个类没有这个保护,通常是你的父类的方法,然后把你的示例传递给它.所以不要写:
你必须写:
由于属性存储在示例的属性字典中,名为
__dict__
,因此您也可以通过直接写入来绕过__setattr__
:这两种语法都很难看而且冗长,但是您可以相对容易地破坏您试图添加的保护(毕竟,如果你能做到这一点,那么其他人也能做到)可能会让你得出这样的结论:Python对受保护属性没有很好的支持。这是设计好的。“我们都是成年人。”你不应该用Python来考虑公共属性或私有属性。所有属性都是公共的。命名“私有”有一个约定。带有一个前导下划线的属性;这警告了使用你的对象的任何人,他们正在扰乱某种实现细节,但是如果他们需要并且愿意接受风险,他们仍然可以这样做。
ylamdve62#
类中定义的
__setattr__
方法只在类的 instances 上的属性赋值时调用,而不是在类变量上调用,因为它们没有在类的示例上用该方法赋值。当然,类也是示例,它们是
type
的示例(或者是一个定制的元类,通常是type
的子类),所以如果你想阻止类变量的创建,你需要创建一个带有__setattr__
方法的元类。但这并不是让类做你想做的事情所需要的,要得到一个只读属性,并且只能写一次(在
__init__
方法中),你可以使用一些更简单的逻辑,一种方法是在__init__
方法的末尾设置另一个属性,它告诉__setattr__
在设置后锁定赋值:另一种选择是使用
property
描述符来描述只读属性,并将真实的值存储在可以正常赋值的“私有”变量中。gev0vcfq3#
__setattr__
只在类的 instances 上被调用,而不是实际的类本身,但是类仍然是一个对象,所以在设置类的属性时调用类的类的__setattr__
。要改变类的属性设置方式,你需要研究元类。元类对于几乎所有的应用程序来说都是多余的,而且很容易混淆。试图使用户定义的类/对象只读通常是一个坏主意。也就是说,这里有一个简单的例子。
sd2nnvve4#
以下内容可在python文档中找到
属性赋值和删除只更新示例的字典,而不是类的字典。如果类有__setattr__()或__delattr__()方法,则调用该方法,而不是直接更新示例字典。
正如你可以清楚地看到的,setattr() 在被调用时改变了示例字典,所以每当你试图分配一个self.instance时,它就会引发异常Exception(“Attribute is Read-Only”),而设置一个类的属性就不会引发异常。
68de4m5k5#
只要给类的属性赋值,就调用
__setattr__()
,即使在__init__()
中初始化了属性,也会调用__setattr__()
,下面举例说明: