I'm very new to Python and I don't understand how functions themselves can seemingly have attributes. In the code below, there is a function called f, and later in the code, something by the name of f.count is referenced. How can a function, namely f, have a .count? I'm getting an error message of: 'NoneType' object has no attribute 'count' on that line, so it obviously doesn't have that attribute yet. How do I give it that attribute?
def fcount(n):
print n.__name__
@fcount
def f(n):
return n+2
for n in range(5):
print n
#print f(n)
print 'f count =',f.count #THE LINE CAUSING THE ERROR MENTIONED ABOVE
@fcount
def g(n):
return n*n
print 'g count =',g.count
print g(3)
print 'g count =',g.count
Edit: Added fcount(), which doesn't do anything much, and details about error.
Let’s start with the definition of f:
@fcount
def f(n):
return n+2
This defines a f as the return value of a call to the function fcount, which is used as a decorator (the leading @) here.
This code is roughly equivalent with
def f(n):
return n+2
f = fcount(f)
since the decorator – fcount – does not return anything, f is None and not a function at the call site.
In your case fcount should return some function and add a count attribute to that returned function. Something useful (?) might be
def fcount(fn):
def wrapper(n):
wrapper.count += 1
return fn(n)
wrapper.count = 0
return wrapper
EDIT
As @jonrsharpe pointed out, a generalized decorator can forward positional and keyword arguments by capturing them with *args and **kwargs in the signature and expanding them in the same way when calling another function. The names args and kwargs are used by convention.
Python also has a helper function (a decorator itself) that can transfer information (name, docstring and signature information) from one function to another: functools.wraps. A complete example looks like this:
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator
def f(a, b, c=None):
"The useful f function"
pass
print f.__name__ # `f` rather than `wrapper`
print help(f) # `f(*args, **kwargs) The useful f function` rather than `wrapper(*args, **kwargs)`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With