Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting a class __name__ declaratively

Why can't you override a class name declaratively, e.g. to use a class name which is not a valid identifier?

>>> class Potato:
...     __name__ = 'not Potato'
...     
>>> Potato.__name__  # doesn't stick
'Potato'
>>> Potato().__name__  # .. but it's in the dict
'not Potato'

I thought maybe it was simply a case that this was overwritten after the class definition block completes. But seems that's not true, because the name is writable yet apparently not set in the class dict:

>>> Potato.__name__ = 'no really, not Potato'
>>> Potato.__name__  # works
'no really, not Potato'
>>> Potato().__name__  # but instances resolve it somewhere else
'not Potato'
>>> Potato.__dict__
mappingproxy({'__module__': '__main__',
              '__name__': 'not Potato',  # <--- setattr didn't change that
              '__dict__': <attribute '__dict__' of 'no really, not Potato' objects>,
              '__weakref__': <attribute '__weakref__' of 'no really, not Potato' objects>,
              '__doc__': None})
>>> # the super proxy doesn't find it (unless it's intentionally hiding it..?)
>>> super(Potato).__name__
AttributeError: 'super' object has no attribute '__name__'

Questions:

  1. Where does Potato.__name__ resolve?
  2. How is Potato.__name__ = other handled (inside and outside of a class definition block)?
like image 346
wim Avatar asked Apr 11 '18 17:04

wim


People also ask

What does __ class __ mean in Python?

__class__ is an attribute on the object that refers to the class from which the object was created. a. __class__ # Output: <class 'int'> b. __class__ # Output: <class 'float'> After simple data types, let's now understand the type function and __class__ attribute with the help of a user-defined class, Human .

How do you give a class name in Python?

Using type() and __name__ attribute to Get the Class Name of an Instance. Also, we can also print the type of c (class car) which gives the object of c and __name__ provides the name of the class. __name__ is a built-in variable that evaluates the name of the current module.

How do you set the metaclass of class A to B?

In order to set metaclass of a class, we use the __metaclass__ attribute. Metaclasses are used at the time the class is defined, so setting it explicitly after the class definition has no effect.

What is the name for an instance of a class?

An instance of a class is an object. It is also known as a class object or class instance. As such, instantiation may be referred to as construction. Whenever values vary from one object to another, they are called instance variables.


1 Answers

Where does Potato.__name__ resolve?

Most documented dunder methods and attributes actually exist in the native code side of the object. In the case of CPython, they are set as pointers in a slot in a C Struct defined in the object model. (defined here - https://github.com/python/cpython/blob/04e82934659487ecae76bf4a2db7f92c8dbe0d25/Include/object.h#L346 , but with fields easier to visualize when one actually creates a new class in C, like here: https://github.com/python/cpython/blob/04e82934659487ecae76bf4a2db7f92c8dbe0d25/Objects/typeobject.c#L7778 , where the "super" type is defined)

Therefore, __name__ is set there by the code in type.__new__, to which it is the first parameter.

How is Potato.__name__ = other handled (inside and outside of a class definition block)?

A class's __dict__ parameter is not a plain dictionary - it is an special mapping proxy object, and the reason for that is exactly so that all attribute settings on the class itself don't go through the __dict__, and instead go through the __setattr__ method in type. In there, assignments to these slotted dunder methods are actually filled in the C object's C structure, and then reflected on the class.__dict__ attribute.

So, outside a class block, cls.__name__ is set in this way - as it takes place after the class has been created.

Inside a class block, all attributes and methods are collected into a plain dict (though that can be customized). This dict is passed to type.__new__ and other metaclass methods - but as said above, this method fills in the __name__ slot from the explicit passed name parameter (that is, the "name" argument passed in the call to type.__new__)- even though it just updates the class __dict__ proxy with all names in the dict used as namespace.

That is why cls.__dict__["__name__"] can start with a different content from what is in the cls.__name__ slot, but subsequent assignments put both in sync.

An interesting anecdote is that three days ago I came across some code trying to reuse the __dict__ name explicitly in the class body, which has similarly puzzling side-effects. I even wondered whether there should be a bug report on that, and queried the Python developers - and as I had thought of, the authoritative answer was:

...all __dunder__ names are reserved for the implementation and they should
only be used according to the documentation. So, indeed, it's not illegal,
but you are not guaranteed that anything works, either.

(G. van Rossum)

And it applies just the same to trying to define __name__ in the class body.

https://mail.python.org/pipermail/python-dev/2018-April/152689.html


And if one actually wants to override __name__ as an attribute in the classbody, a metaclass for that is a simple as a metaclass can be:

class M(type):
    def __new__(metacls, name, bases, namespace, **kw):
         name = namespace.get("__name__", name)
         return super().__new__(metacls, name, bases, namespace, **kw)
like image 97
jsbueno Avatar answered Sep 20 '22 18:09

jsbueno