In the code below, why does g.__int
equal 0
instead of 1
at the end, when both g.__dictionary
and g.__list
retain the values they had inside the decorators?
Why can I add a list/dict as an attribute to a function inside a decorator then access it outside the decorator, but I can't do the same with an integer?
Here's some code to illustrate:
import functools
def dictify(func):
func.__dictionary = { 0 : 0 }
@functools.wraps(func)
def _func(*args, **kwds):
func.__dictionary[0] += 1
print(' Incremented __dictionary, now __dictionary = {0}'.format(str(func.__dictionary)))
return func(*args, **kwds)
return _func
def listify(func):
func.__list = [1, 2, 3]
@functools.wraps(func)
def _func(*args, **kwds):
func.__list.append(0)
print(' Appended 0 to __list, now __list = {0}'.format(str(func.__list)))
return func(*args, **kwds)
return _func
def intify(func):
func.__int = 0
@functools.wraps(func)
def _func(*args, **kwds):
func.__int += 1
print(' Incremented __int, now __int = {0}'.format(func.__int))
return func(*args, **kwds)
return _func
def g():
return 'pennyroyal tea'
print('*** UNMODIFIED ***')
print('g() returns \'{0}\''.format(g()))
print('id(g) = {0}'.format(id(g)))
g = dictify(g)
print('*** DICTIFIED ***')
print('g() returns \'{0}\''.format(g()))
print('g.__dictionary = {0}'.format(str(g.__dictionary)))
print('id(g) = {0}'.format(id(g)))
g = listify(g)
print('*** LISTIFIED ***')
print('g() returns \'{0}\''.format(g()))
print('g.__dictionary = {0}'.format(str(g.__dictionary)))
print('g.__list = {0}'.format(str(g.__list)))
print('id(g) = {0}'.format(id(g)))
g = intify(g)
print('*** INTIFIED ***')
print('g() returns \'{0}\''.format(g()))
print('g.__dictionary = {0}'.format(str(g.__dictionary)))
print('g.__list = {0}'.format(str(g.__list)))
print('g.__int = {0}'.format(str(g.__int)))
print('id(g) = {0}'.format(id(g)))
This gives the following output:
*** UNMODIFIED ***
g() returns 'pennyroyal tea'
id(g) = 139861398390976
*** DICTIFIED ***
Incremented __dictionary, now __dictionary = {0: 1}
g() returns 'pennyroyal tea'
g.__dictionary = {0: 1}
id(g) = 139861398391096
*** LISTIFIED ***
Appended 0 to __list, now __list = [1, 2, 3, 0]
Incremented __dictionary, now __dictionary = {0: 2}
g() returns 'pennyroyal tea'
g.__dictionary = {0: 2}
g.__list = [1, 2, 3, 0]
id(g) = 139861398391216
*** INTIFIED ***
Incremented __int, now __int = 1
Appended 0 to __list, now __list = [1, 2, 3, 0, 0]
Incremented __dictionary, now __dictionary = {0: 3}
g() returns 'pennyroyal tea'
g.__dictionary = {0: 3}
g.__list = [1, 2, 3, 0, 0]
g.__int = 0
id(g) = 139861398391336
You can see that inside the decorator, the value of func.__int
is printed as 1
, but outside the decorator, g.__int
is the default 0
, yet g.__dictionary
and g.__list
retain their values when referenced both inside and outside their decorators.
Note: The id(g)
calls show that decorating with dictify()
, listify()
, and intify()
all return new objects, illustrating that functions are immutable. (See my detailed explanation here)
This question is based on a previous one of mine here. My practical need was satisfied by its answers, but my 'why' instinct won't leave this alone. :)