2

Consider the following small python snippet where I add '2' to the first column of a 3 x 3 matrix:

import numpy as np

def changeValue(kernel):
    kernel[0,0]=kernel[0,0]+ 2 
    kernel[1,0]=kernel[1,0]+ 2 
    kernel[2,0]=kernel[2,0]+ 2 
    return kernel

myKernel = np.array((
 [0, -1, 0],
 [-1, 5, -1],
 [0, -1, 0]), dtype="int")
CVkernel=myKernel

print(CVkernel)
a=changeValue(myKernel)
print(a)
print(CVkernel)

I get the following output

[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]

[[ 2 -1  0]
 [ 1  5 -1]
 [ 2 -1  0]]

[[ 2 -1  0]
 [ 1  5 -1]
 [ 2 -1  0]]

The value of myKernel clobbers CVkernel. I think there is an unintentional call-by-reference (pass-by-reference?) going on but I am not sure why.

If I define the function slightly differently

def changeValue2(kernel):
    kernel=kernel + 2 
    return kernel

Then CVkernel is left untouched

[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]

[[2 1 2]
 [1 7 1]
 [2 1 2]]

[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]

What is going on here? I tried printing out the address register of the variables with print(id(kernel)) and print(id(CVkernel)) and that does not shed any light.

EDIT Even when I use a 'safe' function call, kernel=kernel + 2 , the id of myKernel and CVkernel are the same.

id of myKernel  139994865303344
myKernel 
[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]
id of CVKernel  139994865303344
CVKernel 
[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]

**call made to changeValue2**

id of myKernel  139994865303344
myKernel 
[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]
id of CVKernel  139994865303344
CVKernel 
[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]
output a 
[[2 1 2]
 [1 7 1]
 [2 1 2]]

Shouldn't the id of each variable be different if they are different instances?

aquagremlin
  • 3,515
  • 2
  • 29
  • 51
  • This doesn't make a copy: `CVkernel=myKernel` it just points `CVkernel` to the same array as `myKernel`. The you pass a reference of `myKernel` to the function, it changes it, returns the reference, which you store in `a`. Now you have three references to the same data. You need to explicitly make a copy if that's what you want `CVkernel` to be. – Mark May 19 '19 at 02:58
  • thank you for your reply. That's what I thought but it did not clarify why kernel = kernel +2 is ok but explicit references to the elements of the kernel array are not. – aquagremlin May 19 '19 at 13:59

3 Answers3

1

Try it as below:

def changeValue2(kernel):
    kernel += 2 
    return kernel

It shows the result as below:

[[ 0 -1  0]
 [-1  5 -1]
 [ 0 -1  0]]
[[2 1 2]
 [1 7 1]
 [2 1 2]]
[[2 1 2]
 [1 7 1]
 [2 1 2]]

You know well that It's a call by reference, but, In case of kernel = kernel + 2, the left kernel becomes another instance. Simply, It is same as newKernel = kernel + 2.

So, I changed it to kernel += 2, and it modified original kernel instance.

yaho cho
  • 1,779
  • 1
  • 7
  • 19
  • Thank you. I marked yours as the answer but would appreciate some 'under the hood' explanation of why kernel = kernel +2 is ok but explicit references to the elements of the kernel array are not. In both cases I am referencing the kernel object and modifying it. To extend your explanation then, the statement , kernel[0,0]=kernel[0,0]+ 2 , does not create a new instance but kernel = kernel + 2 does. Altho Imanux gave a valuable reference link, he also did not clarify this point. – aquagremlin May 19 '19 at 14:10
  • @aquagremlin Actually, Instance is memory. Multiple variables can refer one memory. So, `kernel = kernel + 2` creates new memory and set memory id to `kernel` variable. But, `kernel[0][0]` is an element of kernel as `int` type. So, `kernel` does not need to be instatiated. And, `kernel[0][0]` could create new memory for `int` object, internally. – yaho cho May 20 '19 at 10:55
1

Reason

You should never directly modify the object kernel you passed to the function changeValue2.

please check this link How arguments passed in python to find out what really happened when you try to modify the param

Solution:

  1. just use changeValue

  2. use the return value: myKernel = changeValue2(myKernel)

  3. Just a copy of yaho cho's solusion, Thanks again :)

def changeValue2(kernel):
    kernel += 2 
    return kernel
imanux
  • 11
  • 2
  • I think OP knows to use the returned value by `a=changeValue(myKernel)`. – yaho cho May 19 '19 at 05:27
  • Thanks, if he need to modify `myKernel`, `kernel += 2` is the easiest way :) – imanux May 19 '19 at 07:29
  • thank imanux for that valuable link. However I think you missed my problem. I do NOT want to clobber CVkernel with myKernel. Hence the function, changeValue , as well as your solution, kernel+=2, are not good because they BOTH clobber CVkernel with myKernel. – aquagremlin May 19 '19 at 14:32
  • for me, the solution is to explicitly define CVkernel the same way I defined myKernel in the beginning. my goal is to dynamically change the convolution kernel during image processing. – aquagremlin May 19 '19 at 14:33
-1

I suggest :

    CVkernel=myKernel.copy()
Fan Jups
  • 14
  • 4