On 12/28/20 9:31 PM, Guido van Rossum wrote:
Let me see if I can unpack this.
I observe that `type.__new__() ` is really the C function `type_new()` in typeobject.c, and hence I will refer to it by
that name.
I understand that `type_new()` is the only way to create type objects, and it
includes a call to `__init_subclass__()`.
In the source code of `type_new()`, calling `__init_subclass__()` is the last thing it does before returning the newly
created class object, so at this point the class object is complete, *except* that any updates made by the caller of
`type_new()` after `type_new()` returns have not been made, of course. (For example, `new_class.some_attr = 9` from
Ethan's post, or `__abstractmethods__`, which is set by update_abstractmethods() in abc.py.)
This is really the heart of the issue. A major reason to write a custom metaclass is to be able to modify the returned
class before giving it back to the user -- so even though `type_new` is complete the new class could easily not be
complete, and calling `__init_subclass__` and `__set_name__` from `type_new` is premature.
Now here's something that Ethan said that I don't follow:
For Enum, this means that `__init_subclass__` doesn't have access to the new
Enum's members (they haven't been added yet)
I would presume that in an example like the following, the members *are* set by the time `type_new()` is called. What am
I missing?
```
class Color(enum.Enum):
RED = 1
GREEN = 2
BLUE = 4
```
Maybe the problem is that the members are still set to their "naive" initial values (1, 2, 4) rather than to the
corresponding enum values (e.g. `<Color.Red: 1>`)?
Before `type_new()` is called all the (future) members are removed from `namespace`, so they are not present when
`__init_subclass__` is called. Even if they were left in as `{'RED': 1, `GREEN`: 2, `BLUE`: 4}` it would not be
possible for an `__init_subclass__` to customize the members further, or record them in custom data structures, or run
validation code on them, or etc.
Without a more elaborate use case I can't give that more than a shrug. This is how the `__init_subclass__()` protocol is
designed.
The `__init_subclass__` and `__set_name__` protocols are intended to be run before a new type is finished, but creating
a new type has three major steps:
- `__prepare__` to get the namespace
- `__new__` to get the memory and data structures
- `__init__` for any final polishing
We can easily move the calls from `type_new()` to `type_init`.
Note that for ABC, if you add or change the abstraction status of some class attributes, you can just call
`update_abstractmethods()` and it will update `__abstractmethods__` based on the new contents of the class. This also
sounds like no biggie to me.
The issue tracker sample code that fails (from #35815):
```
import abc
class Base(abc.ABC):
#
def __init_subclass__(cls, **kwargs):
instance = cls()
print(f"Created instance of {cls} easily: {instance}")
#
@abc.abstractmethod
def do_something(self):
pass
class Derived(Base):
pass
```
And the output:
`Created instance of <class '__main__.Derived'> easily: <__main__.Derived object
at 0x10a6dd6a0>`
If `Base` had been completed before the call to `__init_subclass__`, then `Derived` would have raised an error -- and it
does raise an error with the patch I have submitted on Github.
--
~Ethan~
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at
https://mail.python.org/archives/list/python-dev@python.org/message/HG5SHTY76LKYQS7OY5CXH6TYMUGK7O5L/
Code of Conduct: http://python.org/psf/codeofconduct/