0

I was experimenting with id to understand default values in python

def f(x=5000):
    print(id(x))
    x=40000
    print(id(x))
    print()
    

f()

f()

This gives the output

2187265500336
2187265501488

2187265500336
2187265501488

I understand why the first print statements are the same. This is because the default value x is evaluated when the function is defined.

However, that in both cases the second print statement gives the same id is confusing me and I couldn't find an answer online elsewhere. Consider

a=100000
print(id(a))

a=400000
print(id(a))

a=100000
print(id(a))

a=400000
print(id(a))

this gives the output

2187265558768
2187265558608
2187265559440
2187265558032

What is causing the difference in the two cases? Inside the function, when we reassign the variable in different function calls, it gets the same id, yet when we reassign a variable to the same value as previously outside a function, this doesn't occur

EDIT in response to @Barmar's comment and the question linked understanding python id() uniqueness. I somewhat understand, but not fully. My best guess now would be (1) the memory is left free for future use as said in the comment (2) in the example below, there seems to be some underlying reason why the same memory gets assigned to x each time even when other memory is assigned. Is this because of caching optimisations,

Caching optimizations mean that you are not always guaranteed to get a new object in cases where one might naiively think one should, but this does not in any way violate the uniqueness guarantee of IDs. Builtin types like int and str may have some caching optimizations, but they follow exactly the same rules: If they are live at the same time, and their IDs are the same, then they are the same object.

def f(x=5000):
    print(id(x))
    c=23000
    d=12444
    x=40000

    w=23444

    print(id(x))
    print()
    

f()

f()
2187265559440
2187265559376

2187265559440
2187265559376

Here we assign several new variables, but each time the same id gets assigned to the variable x upon reassignment.

Regarding @dont just talk code's comment

It probably will, if you just try it a few more times.

I then tried this with the following, but the same result held when repeated a million times

def f(x=5000):
    id_1 = id(x)
    c=23000
    d=12444
    x=40000

    w=23444

    id_2 = id(x)
    return (id_1, id_2)

[f() for n in range(1000000)] == [f() for n in range(1000000)]
True
riemann_lebesgue
  • 299
  • 5
  • 17
  • 1
    When you leave the function, the memory for `x` is made available for future use. The next time you call the function, it happens to get the same memory, so the ID is the same. – Barmar Sep 11 '21 at 21:22
  • @Barmar why don't you give some more explanations and making this as an answer? – Prophet Sep 11 '21 at 21:26
  • I'm sure there are existing duplicates, I just don't have a link to them. – Barmar Sep 11 '21 at 21:27
  • but why doesn't this occur with the outside of function usage? – riemann_lebesgue Sep 11 '21 at 21:37
  • @justanothermathmo It probably will, if you just try it a few more times. – no comment Sep 11 '21 at 21:42
  • @Barmar and don't talk just code, I experimented with this further. Each repeat the same memory was allocated, even when other memory was allocated also, for a million repeats – riemann_lebesgue Sep 11 '21 at 21:53
  • 1
    Whether the memory is reused is just an accident of patterns of allocation. – Barmar Sep 11 '21 at 21:54
  • ok thank you. So the memory is left available, and the fact the same memory is used is just an accident of the algorithms used to allocate it to variables. that makes sense:) – riemann_lebesgue Sep 11 '21 at 21:56
  • 1
    It is no accident that the memory is reused, @Barmar is wrong. Look at `f.__code__.co_consts`. The second entry is the integer `40000`. It get's stored as a constant during function compilation time and never get's deleted. – MegaIng Sep 12 '21 at 20:49
  • @MegaIng I was talking about when memory is reused for the variables outside the function. – Barmar Sep 12 '21 at 21:34
  • @Barmar To quite you : `When you leave the function, the memory for x is made available for future use. The next time you call the function, it happens to get the same memory, so the ID is the same. ` That is straight up false. – MegaIng Sep 13 '21 at 00:19
  • @MegaIng The question is asking why it's different outside the function. My point about unpredictability was referring to that. – Barmar Sep 13 '21 at 00:42
  • @Barmar But it isn't unpredictable, even outside the function: If you put the code they posted inside a module, it will also be the same id for all equal numbers. It behaves only like they describe when you execute it statement for statement inside a REPL. Your comment is just wrong. – MegaIng Sep 13 '21 at 00:52
  • @Barmar Could you reopen this question, since it isn't a duplicate? – MegaIng Sep 13 '21 at 00:54
  • @Barnar Thank you. I will add an answer tomorrow. – MegaIng Sep 13 '21 at 01:11

1 Answers1

5

Constants inside a function come from the function's code object's co_consts attribute.

>>> f.__code__.co_consts
(None, 40000)
>>> id(f.__code__.co_consts[1])
140572403241776
>>> f()
140572374132688
140572403241776

So the second int object with the value 40000 is also created at the function definition, not during the function call.

This is a CPython implementation detail, because all details about CPython bytecode and code objects are CPython implementation details.

kaya3
  • 47,440
  • 4
  • 68
  • 97