This is dangerous. `args` will be `tuple` always, that's how `*` args are always passed. If somewhere in the "tree" of the `args` tuple a unhashable object appears, it won't be able to hash it (you cannot change contents of a tuple, but if there is a list or dict in the tuple, you can change it. And only immutable objects can be hashed).

So I would rather prefer `try:` `except TypeError:` approach.

Try this in python:
```python
{((1,2, (3, {})), 1): 1} # it won't run. Even though everything is in tuples, the dict breaks everything.
```

So when combined with the code from my comment below:
```python
try:
    if args not in self.cache:  # Raises TypeError as it hashes to check the key
        self.cache[args] = self.func(*args)  # This won't get executed then
except TypeError as e:
    if "unhashable" in str(e):
        return self.func(*args)     # Unhashable
    else:
        raise  # smtg else, so raise. I would not put the check here but I am not sure whether this is the sole TypeError we can get here.
else:
    return self.cache[args]     # Hashable
```
A bit bigger now but that should be reliable for all cases.