calls = 0
def tracer(func):                         # Stan w zakresie zawierającym oraz zmiennej globalnej
    def wrapper(*args, **kwargs):         # Zamiast atrybutów klas
        global calls                      # Zmienna calls jest globalna, a nie per funkcja
        calls += 1
        print(f'wywołanie {calls} to {func.__name__}')
        return func(*args, **kwargs)
    return wrapper

@tracer
def hack(a, b, c):           # To samo co: hack = tracer(hack)
    print(a + b + c)

@tracer
def code(x, y):              # To samo co: code = tracer(code)
    print(x ** y)

if __name__ == '__main__':
    hack(1, 2, 3)            # Tak naprawdę wywołuje wrapper, dowiązany do hack
    hack(a=4, b=5, c=6)      # wrapper wywołuje hack

    code(4, 2)               # Tak naprawdę wywołuje wrapper, dowiązany do code
    code(2, y=16)            # Zmienna globalna calls nie jest tutaj liczona per funkcja!

