Remember what decorators are:
@decorate
def foo(...):
...
is exactly equivalent to:
def foo(...):
...
foo = decorate(foo)
The decorator is called on the function, so calling the first parameter self makes no sense. Also, the decorator is called on the function when it is defined, and whatever it returns is used in place of the function. So even if your started
decorator didn't throw an AttributeError
by trying to access the _started
attribute of a function, it would then return None
, making all your methods set to None
, and thus not even be callable.
What you want is something like this:
import functools
def started(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if not self._started:
raise ...
else:
return func(self, *args, **kwargs)
return wrapper
Almost all decorators are of this form; they take a function, create a wrapper that does something "around" the received function, and then return the wrapper. The use of functools.wraps
here is a convenience if you ever end up working with this code in an interactive interpreter session; it automatically updates the wrapper
function with the name and docstring of the original function, which makes the decorated functions "look like" the original function a bit more.
It's irrelevant whether this is defined inside the class or not.