REF: share _simple_new
State: closed

@simonjayhawkins two questions: 1) is there a nice way to annotate the `values` arg (maybe a Protocol thing?) and 2) is the use of `cls.__annotations__["_data"]` an egregious antipattern?

Labels:
name: Refactor
description: Internal refactoring of code
name: Index
description: Related to the Index class or subclasses

Comments:
> 2) is the use of `cls.__annotations__["_data"]` an egregious antipattern?

to begin with a quote from Fluent Python, p8 

> "The first thing to know about special methods is that they are meant to be called by the Python interpreter, and not by you"

using type annotations in a runtime context is not something we've discussed.

I think in general it could be fragile. we can only use with certain types so cannot now change cls._data for the subclasses without potentially breaking the code. So we wouldn't, for example, be able to makes Indexes generic, e.g. Categorical[str] if we used this pattern in other Indexes.

```
>>> from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin
>>>
>>> DatetimeIndexOpsMixin.__annotations__
{'_data': typing.Union[pandas.core.arrays.datetimes.DatetimeArray, pandas.core.a
rrays.timedeltas.TimedeltaArray, pandas.core.arrays.period.PeriodArray], 'freq':
 typing.Union[pandas._libs.tslibs.offsets.BaseOffset, NoneType], 'freqstr': typi
ng.Union[str, NoneType], '_resolution_obj': <enum 'Resolution'>, '_bool_ops': ty
ping.List[str], '_field_ops': typing.List[str]}
>>>
>>> isinstance(42, DatetimeIndexOpsMixin.__annotations__["_data"])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\simon\anaconda3\envs\pandas-dev\lib\typing.py", line 769, in __
instancecheck__
    return self.__subclasscheck__(type(obj))
  File "C:\Users\simon\anaconda3\envs\pandas-dev\lib\typing.py", line 777, in __
subclasscheck__
    raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks
>>>
```

assuming that the assert has been added for runtime debugging and not to appease mypy:

can you not add something akin to construct_array_type from ExtensionDtype as a class attribute to Index to store the backing array type?


> 1) is there a nice way to annotate the `values` arg (maybe a Protocol thing?)

is the reason type annotations for values has not been added related to the above?
> can you not add something akin to construct_array_type from ExtensionDtype as a class attribute to Index to store the backing array type?

will do

>> is there a nice way to annotate the values arg (maybe a Protocol thing?)

> is the reason type annotations for values has not been added related to the above?

I can add an annotation with `Union[DatetimeArray, TimedeltaArray, PeriodArray]`, was wondering if there is a way to make it isolate just the one of those three (like the annotations in the current code with three separate simple_new implementations)
> > > is there a nice way to annotate the values arg (maybe a Protocol thing?)
> 
> > is the reason type annotations for values has not been added related to the above?
> 
> I can add an annotation with `Union[DatetimeArray, TimedeltaArray, PeriodArray]`, was wondering if there is a way to make it isolate just the one of those three (like the annotations in the current code with three separate simple_new implementations)

right. so we want to `tie` the type of values, a parameter of a class method, to the type of self._data, a class attribute (and maybe a construct_array_type like attribute)? I think this is where we would use Generic. the backing array type would be the type parameter.

so something like (completely untested!)

 (ArrayLikeT is the same as ArrayLike but we want to change that to a Union, and we want a typevar here)

```
ArrayLikeT = TypeVar("ArrayLikeT", "ExtensionArray", np.ndarray)

class Index(IndexOpsMixin, PandasObject, Generic(ArrayLikeT)):

    _data : ArrayLikeT
    _construct_array_type: Type[ArrayLikeT]

    def _simple_new(cls, values: ArrayLikeT, name: Label = None):
         ...
```




updated + green