ENH: Support ExtensionArray in Groupby
```python
In [1]: import pandas as pd

In [2]: from cyberpandas import IPArray

In [3]: df = pd.DataFrame({"A": IPArray([0, 0, 1, 2, 2]), "B": [1, 5, 1, 1, 3]})

In [4]: df
Out[4]:
         A  B
0  0.0.0.0  1
1  0.0.0.0  5
2  0.0.0.1  1
3  0.0.0.2  1
4  0.0.0.2  3

In [5]: df.groupby("A").B.mean()
Out[5]:
A
0.0.0.1    1
0.0.0.2    2
Name: B, dtype: int64
```

Note that right now `Out[5].index` just just an `Index` with object dtype. In the future, we could tie an Index type to an ExtensionArray type, and ensure that the extension type propagates through.
What I have so far is relatively straightforward (surprisingly). But I'm probably missing things. Are there edge cases or other operations we should test? # [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/20502?src=pr&el=h1) Report
> Merging [#20502](https://codecov.io/gh/pandas-dev/pandas/pull/20502?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/766a4800b4d9b7e847577b74b24128fcda8eeb55?src=pr&el=desc) will **increase** coverage by `0.02%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/20502/graphs/tree.svg?src=pr&token=eZ4WkYLtcO&width=650&height=150)](https://codecov.io/gh/pandas-dev/pandas/pull/20502?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #20502      +/-   ##
==========================================
+ Coverage   91.82%   91.84%   +0.02%     
==========================================
  Files         152      152              
  Lines       49249    49249              
==========================================
+ Hits        45225    45235      +10     
+ Misses       4024     4014      -10
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.23% <100%> (+0.02%)` | :arrow_up: |
| #single | `41.89% <100%> (ø)` | :arrow_up: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/20502?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/groupby.py](https://codecov.io/gh/pandas-dev/pandas/pull/20502/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZ3JvdXBieS5weQ==) | `92.55% <100%> (ø)` | :arrow_up: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/20502/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `84.52% <0%> (-0.21%)` | :arrow_down: |
| [pandas/plotting/\_converter.py](https://codecov.io/gh/pandas-dev/pandas/pull/20502/diff?src=pr&el=tree#diff-cGFuZGFzL3Bsb3R0aW5nL19jb252ZXJ0ZXIucHk=) | `66.81% <0%> (+1.73%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/20502?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/20502?src=pr&el=footer). Last update [766a480...98a3a85](https://codecov.io/gh/pandas-dev/pandas/pull/20502?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
 If you use `as_index=False`, can you in that way ensure it keeps the correct extension dtype? Not easily. By the time we're wrapping up the output, we've long since converted the input to an Index.

That said, once we have ExtensionIndexes, it should be a one-line change: 
https://github.com/pandas-dev/pandas/blob/cdfce2b0ad99f7faad57cc5247cf33aab5725bed/pandas/core/groupby.py#L3037 lgtm. and another reason to make EA a base class for ``Index``.