Does the f-string change inserted value in it?

154 Views Asked by At

This code prints the values of a and b while both refer to the ID of the wrapper's ID,and both return same ID.

def my_decorator(func):

    def wrapper():
        pass

    return id(wrapper)

def some_func():
    pass

a = id(my_decorator(some_func))  
b = id(my_decorator(some_func))

print(a)
print(b)    # both return same ID

Than I'm inserting the values of a & b into f-string and code returns different IDs.

a = f"foo - { id(my_decorator(some_func)) }"
b = f"foo - { id(my_decorator(some_func))} "
print(a)
print(b)   # return diff IDs

Why?

2

There are 2 best solutions below

0
Mark Ransom On BEST ANSWER

There are a couple of implementation details of standard Python you need to be aware of. They are interacting to give the behavior you are seeing. These details might be different in a different Python implementation, or even a future version. Since they're implementation details they're not guaranteed to stay the same.

First the id() of an object is the object's memory address. Two different objects can have the same id if they coincidentally end up at the same address. Of course this is impossible if both objects exist at the same time.

Second, objects are garbage collected immediately when there are no more references to them. Once an object is garbage collected, its memory is available for a new object.

In both cases, your calls to my_decorator are creating a new object that is not being assigned to a reference so it will be garbage collected at the end of the statement. In the first case there's nothing else going on so nothing gets in the way of the second object being allocated at the same place as the first. In the second case you're creating an f-string which is more work, probably causing some other object to occupy the newly freed space.

0
wjandrea On

The f-strings have nothing to do with it. What you're seeting is undefined behaviour, per the docs for id:

Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

(added bold)

What defines an object's lifetime? See docs:

Objects are never explicitly destroyed; however, when they become unreachable they may be garbage-collected.

Specifically, the objects that may have non-overlapping lifetimes are both wrapper (which becomes unreachable when my_decorator returns) and its ID (which becomes unreachable after calling id on it).

Lastly, I should point out that the func parameter is unused so some_func is also irrelevant.