1

When experiment with python decorators, I got a result that are beyond my understanding, which is related with inner function, closure, assignment.

I try below code,

def myfunc():
    print("myfunc")

def decorator1(func):
    def inner1(*args, **kwargs):
        func(*args, **kwargs)
    print("inner1 id = %d " % id(inner1))
    # print(inner1.cache)
    # return inner1

def decorator2(func):
    def inner2(*args, **kwargs):
        func(*args, **kwargs)
    inner2.cache = {}
    print("inner2 id = %d " % id(inner2))
    print("\t\t\t cache id = %d " % id(inner2.cache))
    return inner2

# Block1 all same
decorator1(myfunc)
decorator1(myfunc)
decorator2(myfunc)
decorator2(myfunc)
decorator1(myfunc)
decorator1(myfunc)
print()

# Block2 deferrent when d2 = ...
decorator1(myfunc)
decorator1(myfunc)
d1 = decorator2(myfunc)
d2 = decorator2(myfunc)
decorator1(myfunc)
decorator1(myfunc)
print()

# Block3 all same
decorator1(myfunc)
decorator1(myfunc)
decorator2(myfunc)
decorator2(myfunc)
decorator1(myfunc)
decorator1(myfunc)
# print()

and get below output

inner1 id = 7696544290000
inner1 id = 7696544290000
inner2 id = 7696544290000
                        cache id = 7696550302496
inner2 id = 7696544290000
                        cache id = 7696547474720
inner1 id = 7696544290000
inner1 id = 7696544290000

inner1 id = 7696544290000
inner1 id = 7696544290000
inner2 id = 7696544290000
                        cache id = 7696550302496
inner2 id = 7696544291152
                        cache id = 7696547501392
inner1 id = 7696544290144
inner1 id = 7696544290144

inner1 id = 7696544290144
inner1 id = 7696544290144
inner2 id = 7696544290144
                        cache id = 7696548415040
inner2 id = 7696544290144
                        cache id = 7696547350000
inner1 id = 7696544290144
inner1 id = 7696544290144

my question is

1 why in Block1, two decorator2 call print same id for inner2, but different id for inner2.cache?

2 why in Block2, inner2 id begin to change, where as in Block1 not? (Block2 assign the returned value, Block1 not.)

qeatzy
  • 1,363
  • 14
  • 21
  • Garbashe collector? Maybe? – developer_hatch Aug 18 '19 at 05:59
  • 1
    See https://stackoverflow.com/a/3877276/718349. Yes, it has to do with garbage collection, but also has to do with the fact that CPython, _by chance_, happens to allocate a new object in the same address. It does not need to in general, however. – Sorawee Porncharoenwase Aug 18 '19 at 06:07
  • @SoraweePorncharoenwase yessss indeed, I just find a post talking about that, very true a very interesting indeed I dind't know at all about it – developer_hatch Aug 18 '19 at 06:15

1 Answers1

0

Python, when you re assign a variable, changes its id sometimes because of CPython optimization, so, for example:

def f():
  return None 

$> f.a = {}
$> id(f.a)
$> 140511869957216
$> f.a = {}
$> id(f.a)
$> 140511869504400

You can check this question how to re-assign variable in python without changing id? maybe can help you to clarify

This post is really nice to, I will highlight some examples it gave.

x = 500
y = 500
id(x)
4338740848
id(y)
4338741040

What happened here? Even after assigning the same integer values to different variable names, we are getting two different ids here. These are actually the effects of CPython optimization we are observing here. CPython implementation keeps an array of integer objects for all integers between -5 and 256. So when we create an integer in that range, they simply back reference to the existing object. You may refer the following links for more information.

developer_hatch
  • 15,898
  • 3
  • 42
  • 75