make DateOffset immutable
State: closed

Returning to the long-standing goal of making `DateOffset`s immutable (recall: `DateOffset.__eq__` calls `DateOffset._params` which is _very_ slow.  `_params` can't be cached ATM because `DateOffset` is mutable`)

Earlier attempts in this direction tried to make the base class a `cdef class`, but that has run into `pickle` problems that I haven't been able to sort out so far.  This PR goes the patch-`__setattr__` route instead.

Note: this PR does _not_ implement the caching that is the underlying goal.

I'm likely to make some other PRs in this area, will try to keep them orthogonal.

Labels:
name: Internals
description: Related to non-user accessible pandas implementation
name: Timedelta
description: Timedelta data type
name: Frequency
description: DateOffsets

Comments:
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/21341?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@e24da6c`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff            @@
##             master   #21341   +/-   ##
=========================================
  Coverage          ?   91.91%           
=========================================
  Files             ?      153           
  Lines             ?    49550           
  Branches          ?        0           
=========================================
  Hits              ?    45546           
  Misses            ?     4004           
  Partials          ?        0
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.31% <100%> (?)` | |
| #single | `41.79% <100%> (?)` | |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/21341?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/tseries/offsets.py](https://codecov.io/gh/pandas-dev/pandas/pull/21341/diff?src=pr&el=tree#diff-cGFuZGFzL3RzZXJpZXMvb2Zmc2V0cy5weQ==) | `97.16% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/21341?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/21341?src=pr&el=footer). Last update [e24da6c...f931224](https://codecov.io/gh/pandas-dev/pandas/pull/21341?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

OK, its working again.

@jbrockmendel ok pls rebase, I think you have some residual comments about normalize (your PR was merged so maybe need changing)
This is better than nothing since it will allow us to move forward with some serious perf improvements to PeriodIndex ops, but is still pretty ugly.  If there's a decent chance of getting someone more competent than me to look at the pickle issue holding up 
#18824
#18224 that would be the best-case.
@jbrockmendel ok this is ok. merge before your set_state/get_state change? does it matter?
There will be a merge conflict I'll need to address.  Since I'm holding out hope WillAyd can help make #18224 viable (and this unnecessary), I'd rather the the getstate/setstate one get merged first.
ok dokie
> I think this needs a whatsnew as now technically DateOffsets are immutable yes? do we have some direct tests for this?

added
thanks @jbrockmendel