1

I am not sure exactly what this means or how it is used, but I came across this functionality when it comes to a function. I am able to dynamically add attributes to a function.

What is the use of this and how does it happen? I mean it's a function name after all, how can we add attributes to it dynamically?

def impossible():
    print "Adding a bool"

impossible.enabled = True 
# I was able to just do this. And this is a function, how does this add up?

print(impossible.enabled) # No attribute error. Output: True
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Abhishek
  • 155
  • 3
  • 14
  • It’s not a function *name*; it’s a function, and it’s like any other value. Any type can permit or forbid new attributes. – Ry- Apr 15 '18 at 07:07
  • Does the same apply for a class as well? – Abhishek Apr 15 '18 at 07:09
  • The same which? =) Classes are also like any other values, and also are a type that permit new attributes, yes. Classes aren’t functions, though (try `isinstance(SomeClass, type(some_function))`). – Ry- Apr 15 '18 at 07:11
  • Is there a way to print the definition of the function, in case i do that would i be able to see the new attribute appended to it? – Abhishek Apr 15 '18 at 07:12
  • [There is](https://stackoverflow.com/questions/1562759/can-python-print-a-function-definition), but an attribute on a function isn’t part of a function’s definition anyway. You can use `dir(impossible)` to see all the names of attributes on it. – Ry- Apr 15 '18 at 07:12
  • Yea makes sense. – Abhishek Apr 15 '18 at 07:22
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/169013/discussion-between-abhishek-and-ry). – Abhishek Apr 15 '18 at 07:59

3 Answers3

3

in python functions are objects, they have all sorts of inner attributes and they can also have the __dict__ attribute, and like any other object anything in __dict__ is a variable of that object.you can see it if you print it before and after impossible.enabled

def impossible():
    print "Adding a bool"

print(impossible.__dict__) #  {}
impossible.enabled = True 
print(impossible.__dict__) #  {'enabled': True}

this doesn't change the function in any way since most of the time __dict__ is empty and holds no special value in a function. in fact its a bit useful since you could use this in a function to store static variables without cluttering your global names

AntiMatterDynamite
  • 1,495
  • 7
  • 17
0

The name impossible points to an object that represents the function. You're setting a property on this object (which python allows you to do on all objects by default):

>>> def foo():
...   print("test")
...
>>> dir(foo)
['__call__', ..., 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
>>> foo.a = 'test'
>>> dir(foo)
['__call__', ..., 'a', ...]

Since this object implements __call__, it can be called as a function - you can implement your own class that defines __call__ and it'll also be allowed to be called as a function:

>>> class B:
...   def __call__(self, **kw):
...     print(kw)
...
>>> b = B()
>>> b()    
{}
>>> b(foo='bar')
{'foo': 'bar'}

If you create a new function, you can see that you haven't done anything to the definition of a function (its class), just the object that represents the original function:

>>> def bar():
...   print("ost")
...
>>> dir(bar)
['__call__', ..., 'func_closure', ...] 

This new object does not have a property named a, since you haven't set one on the object bar points to.

MatsLindh
  • 49,529
  • 4
  • 53
  • 84
0

You can, for example, use a function attribute to implement a simple counter, like so:

def count():
    try:
        count.counter += 1
    except AttributeError:
        count.counter = 1
    return count.counter

Repeatedly calling "count()" yields: 1, 2, 3, ...