BUG: raise on non-hashable in __contains__
By checking before we get to the `Engine.__contains__` call, we can avoid a redundant call to `hash`
mypy didnt like my attempt at annotating, so reverted > mypy didnt like my attempt at annotating, so reverted

is this because \_\_contains__ is a special method and this code is unsafe. Is this not also a breaking change? restored annotations.  turns out the trouble was that `RangeIndex.__contains__` had a non-matching annotation > restored annotations. turns out the trouble was that `RangeIndex.__contains__` had a non-matching annotation

I think mypy should only complain if the class is a subclass of collections.abc.Container
even so, in typeshed, classes that want to restrict the type of key add a type:ignore.

so rather than Any, can Hashable be used? > so rather than Any, can Hashable be used?

When I use `Optional[Hashable]` mypy gives:

```
pandas/core/indexes/numeric.py:236: error: No overload variant of "int" matches argument type "Optional[Hashable]"
pandas/core/indexes/numeric.py:236: note: Possible overload variant:
pandas/core/indexes/numeric.py:236: note:     def int(self, x: Union[str, bytes, SupportsInt] = ...) -> int
pandas/core/indexes/numeric.py:236: note:     <1 more non-matching overload not shown>
pandas/core/indexes/numeric.py:488: error: Argument 1 to "len" has incompatible type "Optional[Hashable]"; expected "Sized"
pandas/core/indexes/numeric.py:488: error: Item "Hashable" of "Optional[Hashable]" has no attribute "item"
pandas/core/indexes/numeric.py:488: error: Item "None" of "Optional[Hashable]" has no attribute "item"
pandas/core/indexes/numeric.py:490: error: Argument 1 to "len" has incompatible type "Optional[Hashable]"; expected "Sized"
Found 5 errors in 1 file (checked 846 source files)
``` Yea I guess might be tough to get that to work with mypy since we are catching the `TypeError` at runtime, though passing a non-hashable item to that would currently raise an error at the `hash()` call that is uncaught, so I think mypy is highlighting an actual bug in a roundabout way > looks fine. can you add assert hash(key) in the cython code?

A big part of the motivation here is to avoid duplicated hash calls > > looks fine. can you add assert hash(key) in the cython code?
> 
> A big part of the motivation here is to avoid duplicated hash calls

do asserts not get stripped? isn't that the point? (its not a big deal as long as you have a comment, but the asserts are to provide developer guarantees)

also needs a rebase thanks @jbrockmendel 