dispatch frame methods to series versions instead of re-implementing masking etc
This moves some of DataFrame's dispatching logic into `ops`, mostly in ways that don't change any logic.

The place that logic _is_ changed is in `DataFrame._combine_frame` where instead of defining `arith_op` to mask `func` and wrapping `{col: arith_op(this[col].values, other[col].values) for col in this.columns}`, we dispatch directly to the Series methods and wrap `{col: func(this[col], other[col]) for col in this.columns}`
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/19611?src=pr&el=h1) Report
> Merging [#19611](https://codecov.io/gh/pandas-dev/pandas/pull/19611?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/2fdf1e256e5e0b7f1fe909629e2f0b7893c8c7c3?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `94.23%`.

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

```diff
@@            Coverage Diff             @@
##           master   #19611      +/-   ##
==========================================
- Coverage   91.58%   91.57%   -0.01%     
==========================================
  Files         150      150              
  Lines       48867    48856      -11     
==========================================
- Hits        44755    44741      -14     
- Misses       4112     4115       +3
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.95% <94.23%> (-0.01%)` | :arrow_down: |
| #single | `41.75% <40.38%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/19611?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/19611/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.22% <100%> (+0.06%)` | :arrow_up: |
| [pandas/core/indexes/base.py](https://codecov.io/gh/pandas-dev/pandas/pull/19611/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9iYXNlLnB5) | `96.45% <93.33%> (-0.02%)` | :arrow_down: |
| [pandas/core/ops.py](https://codecov.io/gh/pandas-dev/pandas/pull/19611/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvb3BzLnB5) | `96.44% <93.33%> (-0.39%)` | :arrow_down: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/19611/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `83.64% <0%> (-0.21%)` | :arrow_down: |
| [pandas/core/series.py](https://codecov.io/gh/pandas-dev/pandas/pull/19611/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvc2VyaWVzLnB5) | `94.56% <0%> (-0.01%)` | :arrow_down: |
| [pandas/core/indexes/api.py](https://codecov.io/gh/pandas-dev/pandas/pull/19611/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9hcGkucHk=) | `98.78% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/19611?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/19611?src=pr&el=footer). Last update [2fdf1e2...118cd5d](https://codecov.io/gh/pandas-dev/pandas/pull/19611?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
 > The place that logic is changed is in DataFrame._combine_frame where instead of defining arith_op to mask func and wrapping {col: arith_op(this[col].values, other[col].values) for col in this.columns}, we dispatch directly to the Series methods and wrap {col: func(this[col], other[col]) for col in this.columns}

does this change anything from a user pov? > does this change anything from a user pov?

No, but I may have made a mistake here.  Pls put a pin in this until I double-check. I did in fact make a mistake, but there are no existing tests that catch it.  Next push will fix both of these. Will hold off on this until #19613 is closed. rebase did you have another PR which touched this code and we wanted to merge first? or is this one ok? if so rebase There was, and it was merged a few days ago.  #19613.  This should be good to go (after rebase) #19649 and #19582 touch _nearby_ code, but distinct enough that it shouldn't cause a problem. thanks