How does __setattr__ work with class attributes?
<p>I am trying to understand how exactly __setattr__ works with class attributes. This question came about when I had attempted to override __setattr__ to prevent attributes from being written to in a simple class.</p>

<p>My first attempt used instance level attributes as follows:</p>

<pre><code>class SampleClass(object):

    def __init__(self):
        self.PublicAttribute1 = "attribute"

    def __setattr__(self, key, value):
        raise Exception("Attribute is Read-Only")
</code></pre>

<p>I had originally thought that only external attempts to set the attribute would throw an error, but even the statement inside the __init__ caused the exception to be thrown.</p>

<p>Later I ended up finding another way to do this, while attempting to solve a different problem. What I ended up with was:</p>

<pre><code>class SampleClass(object):
    PublicAttribute1 = "attribute"

    def __setattr__(self, key, value):
        raise Exception("Attribute is Read-Only")
</code></pre>

<p>I expected to get the same result, but to my surpirse I was able to set the class attribute while preventing changes from being made after the initial declaration. </p>

<p>I don't understand why this works though. I know it has to do with class vs. instance variables. My theory is that using __setattr__ on a class variable will create an instance variable of the same name, since I believe I think I have seen this behavior before, but I am not sure.</p>

<p>Can anyone explain exactly what is happening here?</p>

<p><code>__setattr__()</code> is called whenever a value is assigned to any of the class's property. Even if the property is initialized in the <code>__init__()</code>, it will make call to <code>__setattr__()</code>. Below is the example to illustrate that:</p>

<pre><code>&gt;&gt;&gt; class X(object):
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
...     def __setattr__(self, k, v):
...         print 'Set Attr: {} -&gt; {}'.format(k, v)
...
&gt;&gt;&gt; x = X(1, 3)       # &lt;-- __setattr__() called by __init__() 
Set Attr: a -&gt; 1
Set Attr: b -&gt; 3
&gt;&gt;&gt; x.a = 9           # &lt;-- __setattr__() called during external assignment
Set Attr: a -&gt; 9
</code></pre>

python