Special attributes added to classes on creation
Xiang Zhang
zhangyangyu0614 at gmail.com
Mon Jul 4 06:22:47 EDT 2016
On Monday, July 4, 2016 at 12:02:41 AM UTC+8, Steven D'Aprano wrote:
> I have some code which can preprocess (using a metaclass) or postprocess
> (using a decorator) a class:
>
> @process
> class K:
> pass
>
>
> class K(metaclass=process):
> pass
>
>
> Both should give the same result, but I have found that the results are
> slightly different because the dict passed to process as a metaclass is
> different from the the class dict seen by the decorator. I can demonstrate
> the difference in Python 3.6 by using this metaclass/decorator:
>
> class process(type):
> def __new__(meta, *args):
> if len(args) == 1:
> # decorator usage
> cls = args[0]
> d = cls.__dict__
> elif len(args) == 3:
> # metaclass usage
> name, bases, d = args
> cls = super().__new__(meta, *args)
> print(sorted(d.keys()))
> return cls
>
>
> If I then try it against two identical (apart from their names) classes, I
> get these results:
>
>
> py> @process
> ... class K:
> ... x = 1
> ...
> ['__dict__', '__doc__', '__module__', '__weakref__', 'x']
> py> class Q(metaclass=process):
> ... x = 1
> ...
> ['__module__', '__qualname__', 'x']
>
>
>
> Now if I check the newly created Q, I see the same keys K has:
>
> py> sorted(Q.__dict__.keys())
> ['__dict__', '__doc__', '__module__', '__weakref__', 'x']
>
>
> Is there any documentation for exactly what keys are added to classes when?
>
>
>
>
> --
> Steven
> “Cheer up,” they said, “things could be worse.” So I cheered up, and sure
> enough, things got worse.
eryk sun has a very good explanation. I want to add two points:
1. __qualname__ though passed in class creation, but it won't appear in the class.__dict__ since it is deliberately deleted from __dict__ in type_new.
2. __dict__ and __weakref__ are possible to be added even when there is not __slots__. __dict__ can be added when the base class's dictoffset == 0 and __weakref__ can be added when the base class's weakrefoffset == 0 && tp_itemsize == 0. An example is:
In [2]: class T(int):
...: pass
...:
In [3]: T.__dict__
Out[3]: mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'T' objects>, '__doc__': None})
I think this is a nice design in attribute searching.
BTW, I think this is a implementation detail. If you rely on it, it may easily break in the future.
More information about the Python-list
mailing list