Skip to content

typing/Generic __orig_class__ availability during __init__ in 3.6 vs 3.7 #658

Closed
@baltus-atomicrules

Description

@baltus-atomicrules

I observe that when executing __init__ in a Generic class, the value of __orig_class__ is available in version 3.6 but not in 3.7

(I am comparing 3.6.7 with 3.7.0)

I've attached a simple example that defines a Generic class and then accesses __orig_class__ in the associated __init__ method.

Simple.py.txt

N = TypeVar('N')
class ZZ(Generic[N]):
    def __init__(self):
        print("__orig_class__ is: ", self.__orig_class__)

In 3.6, this code runs fine:

In [42]: import Simple

In [51]: a = Simple.ZZ[int]

In [56]: b = a()
__orig_class__ is:  Simple.ZZ[int]

In 3.7 however, the value of __orig_class__ is not available:

In [4]: import Simple

In [5]: a = Simple.ZZ[int]

In [6]: b = a()

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-52a6c422c17e> in <module>
----> 1 b = a()
/tool/pandora64/.package/python-3.7.0/lib/python3.7/typing.py in __call__(self, *args, **kwargs)
    668             raise TypeError(f"Type {self._name} cannot be instantiated; "
    669                             f"use {self._name.lower()}() instead")
--> 670         result = self.__origin__(*args, **kwargs)
    671         try:
    672             result.__orig_class__ = self
/proj/emu_scratch4/users/dbaltus/python/experiment/Simple.py in __init__(self)
      4 class ZZ(Generic[N]):
      5     def __init__(self):
----> 6         print("__orig_class__ is: ", self.__orig_class__)
AttributeError: 'ZZ' object has no attribute '__orig_class__'

> /proj/emu_scratch4/users/dbaltus/python/experiment/Simple.py(6)__init__()
      2
      3 N = TypeVar('N')
      4 class ZZ(Generic[N]):
      5     def __init__(self):
----> 6         print("__orig_class__ is: ", self.__orig_class__)
ipdb> q

This occurs because in 3.7, the __call__ method of _GenericAlias does not set the value of __orig_class__ until after __init__ is called. (It is done differently in 3.6).

    def __call__(self, *args, **kwargs):
        if not self._inst:
            raise TypeError(f"Type {self._name} cannot be instantiated; "
                            f"use {self._name.lower()}() instead")
        result = self.__origin__(*args, **kwargs)
        try:
            result.__orig_class__ = self
        except AttributeError:
            pass
        return result

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions