Take a good look at this enormous answer/novel. It’s one of the best explanations I’ve come across.
The shortest explanation that I can give is that decorators wrap your function in another function that returns a function.
This code, for example:
@decorate def foo(a): print a
would be equivalent to this code if you remove the decorator syntax:
def bar(a): print a foo = decorate(bar)
Decorators sometimes take parameters, which are passed to the dynamically generated functions to alter their output.
Another term you should read up on is closure, as that is the concept that allows decorators to work.