Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Value by reference issue
#1
I've run across an issue with trying to copy an int argument for later use in the program. I realized the int that was copied is changing the value of the original argument; using id(val) and the copy id(valCopy), I can see that they have the same address. This isn't what I was expecting, as this is a simple int, not a list or similar. So, I did import copy, and used both copy.copy and copy.deepcopy, and all have the same result. I found that by modifying the copy, using +=1, then I get a new location, and no longer change the underlying.

I don't see any reference to having to do it this way, so I'm confused.
I'm using python version Python 3.8.10 (default, Jun 2 2021, 10:49:15) , but have also tried it on 3.10.x with same results.

import copy

def a(val):
    newVal1 = val
    print("values:    val: ", val,     " newVal:", newVal1)
    print("addresses: val: ", id(val), " newVal:", id(newVal1))

    newVal2 = copy.copy(val)
    print("values:    val: ", val,     " newVal:", newVal2)
    print("addresses: val: ", id(val), " newVal:", id(newVal2))

    newVal3 = copy.deepcopy(val)
    print("values:    val: ", val,     " newVal:", newVal3)
    print("addresses: val: ", id(val), " newVal:", id(newVal3))


    newVal1 += 1

    print("values:    val: ", val,     " newVal:", newVal1)
    print("addresses: val: ", id(val), " newVal:", id(newVal1))

def main():
    a(1)

if(__name__ == '__main__'):
    main()
Output:
values: val: 1 newVal: 1 addresses: val: 9476448 newVal: 9476448 values: val: 1 newVal: 1 addresses: val: 9476448 newVal: 9476448 values: val: 1 newVal: 1 addresses: val: 9476448 newVal: 9476448 values: val: 1 newVal: 2 addresses: val: 9476448 newVal: 9476480
Reply
#2
I'm not sure your code demonstrates anything changing. Can you give more information on how this behavior was causing a problem? All I see above is that you've made NewVal1, NewVal2, and NewVal3 point to the same object. That shouldn't cause issues.

ints are immutable, so there is usually no benefit to trying to make some sort of independent copy of them. An int can never change. I wonder if your original problem wasn't that the ints were the same object but something else happening.

Can you show the part where the original value changed?
Reply
#3
(Jul-30-2021, 11:32 PM)bowlofred Wrote: I'm not sure your code demonstrates anything changing. Can you give more information on how this behavior was causing a problem? All I see above is that you've made NewVal1, NewVal2, and NewVal3 point to the same object. That shouldn't cause issues.

ints are immutable, so there is usually no benefit to trying to make some sort of independent copy of them. An int can never change. I wonder if your original problem wasn't that the ints were the same object but something else happening.

Can you show the part where the original value changed?

The code you saw shows that the reference location is assigned to all variables, regardless of whether a simple assignment (=), copy.copy(adcnum), or copy.deepcopy(adcnum): all reference the same memory location for the val1, val2, val3; they all point to the same memory location. So regardless of the method of assignment, I still end up with a reference. So none of these does anything except pass the reference to the original memory location. So modifying one, modifies them all, since they are all pointing to the same memory location that was passed in via the function call.

'adcnum' is passed by reference in the function call. It's my understanding this shouldn't be the case, except for things such as lists, arrays, objects; that simple things such as int, are passed by value, not by reference.

Thus, I can't keep the original value brought in by the function call for use elsewhere in the program.

The snipped and simplified version of the original program, is shown below. Thus when it is assigned to channel, channel points to the memory location. Changing adcnum changes the value in memory, which is shared by 'commandWord'. Thus 'commandWord', which needs to hold the original value passed in to the function, isn't working correctly, because 'adcnum' has been modified.


def readadc(adcnum):
   channel = adcnum
   commandWord = adcnum << 4 | 143
Thanks for your assistance!
Reply
#4
Quote: So modifying one, modifies them all

Not true. These are ints and they are fundamentally not modifiable. So none of them can ever change. This is different than something like a list.

You have demonstrated that they are the same right now. You have not demonstrated that changing one changes another one. In fact, lines 17-20 show that (attempting) to change one of the variables leaves the other variable value unmodified.


Quote: The snipped and simplified version of the original program, is shown below. Thus when it is assigned to channel, channel points to the memory location.

Not quite. That's how C would do it. In python, the variable points to an object. The object is the main thing, not the address.

Quote:Changing adcnum changes the value in memory,
.

Assigning adcnum (using "=") should not modify the object it pointed to before. If it points to a mutable object, you can change that object through some methods, but assignment doesn't do that. You can't change an int anyway.

Your second program doesn't seem to be changing adcnum at all. You assign a value to channel, then you assign a value to commandWord. I don't see the problem in this example yet.

Why should commandWord be expected to hold the original value when you assign it a modified value on line 3?
Reply
#5
Numbers and strings are immutable objects in Python. Since, by definition, an immutable object cannot change, it doesn't make any sense to "copy" an immutable object. The object's "value" will always be the same and any copy is just wasted memory.

Variables in Python are entries in a dictionary. They are not "memory" that can be filled with different values. The only way to change a variable is through assignment (val = 5).

What is it you are trying to do? I don't understand what you mean by this:
Quote:I've run across an issue with trying to copy an int argument for later use in the program. I realized the int that was copied is changing the value of the original argument

I think you should post your code and describe the problem you are having. The example you post is not helping me understand your real problem.
Reply
#6
(Jul-30-2021, 11:32 PM)bowlofred Wrote: I'm not sure your code demonstrates anything changing. Can you give more information on how this behavior was causing a problem? All I see above is that you've made NewVal1, NewVal2, and NewVal3 point to the same object. That shouldn't cause issues.

ints are immutable, so there is usually no benefit to trying to make some sort of independent copy of them. An int can never change. I wonder if your original problem wasn't that the ints were the same object but something else happening.

Can you show the part where the original value changed?

From post: "...Variables in Python are entries in a dictionary. They are not "memory"

from https://docs.python.org/3/library/functions.html#id
"id(object)
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.

CPython implementation detail: This is the address of the object in memory.
Reply
#7
I don't know what you are trying to say with your last post. Objects in Python have a unique ID (unique at any given moment anyway). As an "implementation detail" this id CAN BE the memory location of the object. This is true while at the same time being of no importance at all. For two reasons:

1. Knowing an object's address provides no more features or freedoms than treating the object ID as a unique number. The only thing you can do with the ID is use it as an ID. You can't treat it like "memory". No peek or poke.

2. Variables and objects have no relationship other than the variable name is a key in a dictionary and the value associated with that key is an object. The dictionary can be changed to map the variable to a different object, or the variable name can be removed from the dictionary altogether.
Reply
#8
Thanks everyone for your help! Your push-back caused me to cut the code (not the one shown here, but the bit-banger code) to a minimum.

The problem was a coding error associated with the following statement (not sure what the problem is here, but I can research that tomorrow):

print("adcnum: {0:d}, commandWord: {0:b}, channel: {0:d}".format(adcnum, commandWord, channel))

For some reason, the channel was being printed with the commandWord value.

Thanks again!
Reply
#9
a, b, c = 1, 2, 3
print('{0:d} {0:d} {0:d}'.format(a, b, c))
Output:
1 1 1
The reason "1 1 1" is printed is because the 0 in {0:d} tells Python to use the first argument in format(). So the print statement prints the "a" value all three times. You could do this:
a, b, c = 1, 2, 3
print('{0:d} {1:d} {2:d}'.format(a, b, c))
Or let the index be implicit:
a, b, c = 1, 2, 3
print('{:d} {:d} {:d}'.format(a, b, c))
I find f' strings much easier to use and understand.
a, b, c = 1, 2, 3
print(f'{a:d} {b:d} {c:d}')
Output:
1 2 3
Reply
#10
(Jul-31-2021, 04:59 AM)deanhystad Wrote:
a, b, c = 1, 2, 3
print('{0:d} {0:d} {0:d}'.format(a, b, c))
Output:
1 1 1
The reason "1 1 1" is printed is because the 0 in {0:d} tells Python to use the first argument in format(). So the print statement prints the "a" value all three times. You could do this:
a, b, c = 1, 2, 3
print('{0:d} {1:d} {2:d}'.format(a, b, c))
Or let the index be implicit:
a, b, c = 1, 2, 3
print('{:d} {:d} {:d}'.format(a, b, c))
I find f' strings much easier to use and understand.
a, b, c = 1, 2, 3
print(f'{a:d} {b:d} {c:d}')
Output:
1 2 3

Thanks! Somehow, I missed that in reading. I thought it was related to keeping a leading zero.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Pass by object reference when does it behave like pass by value or reference? mczarnek 2 2,513 Sep-07-2020, 08:02 AM
Last Post: perfringo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020