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