So if I try something like this with built-in list before init:
list.hack = 'impossible'
I get a TypeError.
TypeError: can't set attributes of built-in/extension type 'list'
But if I make my class that extends built-in list like so:
class mylist(list):
def __setattr__(self, name, value):
raise NotImplementedError
I can oddly do this:
mylist.hack = 'haha'
And when I would init "mylist", I would have a "hack" attribute within it.
x = mylist()
x.hack
[Out]: 'haha'
Even though I can't set any new attributes after I init "mylist", I can do so in pre init state.
Is it possible to get same pre init behaviour with custom classes, as there is with built-ins?
First of all, the __setattr__
def is not necessary:
>>> class MyList(list):
... pass
>>> MyList.hack = 'haha'
>>> x = MyList()
>>> x.hack
'haha'
You are not adding an attribute to an instance (x
) but to the class (MyList
). (Some languages have a static
keyword for these
attributes (C++, Java, PHP, ...).)
It is roughly equivalent to:
>>> class MyList(list):
... hack = 'haha' # class attribute, ie. "static"
>>> x = MyList()
>>> x.hack
'haha'
Note that this has nothing to do with pre/post init:
>>> class MyList(list):
... hack = 'haha'
>>> x = MyList()
>>> MyList.hack2 = 'hehe' # post init
You have:
>>> x.hack
'haha'
But also:
>>> x.hack2
'hehe'
To summarize, in Python:
x
is an instance of C
and attr
an attribute of C
, then x.attr
is equivalent to C.attr
.For the record, you can prevent this flexible behavior using a metaclass:
>>> class MyListMeta(type):
... def __setattr__(self, name, value):
... raise AttributeError()
>>> class MyList(list, metaclass=MyListMeta):
... hack = 'haha'
As expected:
>>> x = MyList()
>>> x.hack
'haha'
But now:
>>> MyList.hack2 = 'hehe'
Traceback (most recent call last):
...
AttributeError
Note that you can't set existing attributes either:
>>> MyList.hack = 'hehe'
Traceback (most recent call last):
...
AttributeError
Remarks:
Summary of the remarks: do not do this.