Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Functions Can Have Attributes?

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.

like image 485
user3402743 Avatar asked Aug 08 '26 02:08

user3402743


1 Answers

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)`
like image 177
David Aurelio Avatar answered Aug 10 '26 16:08

David Aurelio



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!