At a high level, the decorated function maintains a counter of how many times it was called.
There is one major issue with the code. The wrapper does not actually call the wrapped function as it should. Instead of return func, which just returns the function object, it should read
return func(*args, **kwargs)
As @warvariuc points out, one possible reason is that the author did not have or did not know about nonlocal, which lets you access the enclosing namespace.
I think a more plausible reason is that you want to be able to access the counter. Functions are first-class objects with mutable dictionaries. You can assign and access arbitrary attributes on them. It could be convenient to check foo.count after a few calls, otherwise why maintain it in the first place?
The reason that wrapper.counter is initialized the way it is is simply that wrapper does not exist in the local namespace until the def statement runs to create it. A new function object is made by the inner def every time you run counter. def generally is an assignment that creates a function object every time you run it.
One more minor point about the code you show is that foo.__name__ will be wrapper instead of foo after the decoration. To mitigate this, and make it mimic the original function more faithfully, you can use functools.wraps, which is a decorator for the decorator wrappers. Your code would look like this:
from functools import wraps
def counter(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        wrapper.count += 1
        # Call the function being decorated and return the result
        return func(*args, **kwargs)
    wrapper.count = 0
    # Return the new decorated function
    return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
    print('calling foo()')
Now you can do
>>> foo.__name__
'foo'
>>> foo()
calling foo()
>>> foo()
calling foo()
>>> foo()
calling foo()
>>> foo.count
3