1

I thought Python assignment statements were 'pass by value'. For example

b=0
a=b
b=1
print(a) #prints 0
print 
(b) #prints 1

However, I am confused by a different behavior when dealing with other kinds of data. From this tutorial on openCV I modified the code slightly to show two images. The code below takes this image: and adds it into this image enter image description here

and repeats the process, adding this image

onto the same base image.

import cv2
import numpy as np

# Load two images
img1 = cv2.imread('3D-Matplotlib.png')
#img1a = img1
img1a = cv2.imread('3D-Matplotlib.png')
img2 = cv2.imread('mainlogo.png')
img3 = cv2.imread('helloo.png')
# I want to put logo on top-left corner, So I create a ROI
rows,cols,channels = img2.shape
roi = img1[20:rows+20, 20:cols+20]

rows3,cols3,channels3 = img3.shape
roi3 = img1[50:rows3+50, 50:cols3+50 ]


# Now create a mask of logo 
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
# add a threshold
ret, mask = cv2.threshold(img2gray, 220, 255, cv2.THRESH_BINARY_INV)
#anything crossing over 220 is thelower limit
#binary threshold is 0 or 1
#anything> 220 goes to 255
#anything below 220 goes to 0-> black
#and create its inverse mask
mask_inv = cv2.bitwise_not(mask)
#do same for img3
img3gray = cv2.cvtColor(img3,cv2.COLOR_BGR2GRAY)
ret3, mask3 = cv2.threshold(img3gray, 140, 255, cv2.THRESH_BINARY_INV)
mask_inv3 = cv2.bitwise_not(mask3)


# take the ROI of the plot, and throw the mask over it 
img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

# Take only region of logo from logo image.
img2_fg = cv2.bitwise_and(img2,img2,mask = mask)

#do the same with the other mask
img3_bg = cv2.bitwise_and(roi3,roi3,mask = mask_inv3)
img3_fg = cv2.bitwise_and(img3,img3,mask = mask3)
#

dst = cv2.add(img1_bg,img2_fg)
dst3 = cv2.add(img3_bg,img3_fg)

img1[0:rows, 0:cols ] = dst
img1a[50:rows3+50, 50:cols3+50 ] = dst3

cv2.imshow('r1',img1)
cv2.imshow('r3',img1a)

cv2.waitKey(0)
cv2.destroyAllWindows()

In the above posted code, I get two different images

If I comment out line 7 and uncomment line 8, I would expect the same result if it was pass by value. But I get something else

.

Both images are the same. Obviously, the manipulations onto img1 are 'carried over' to img1a because img1a is set to be equal to img1. If the assignment statement was 'pass by value' (as I would expect from python), then img1 and img1a should be different. But since they are the same, I conclude that img1 is a ptr that was passed to img1a. Thus, if I try to print img1a, I get the same data as I would from printing img1.

So maybe images are passed by reference? What other data types in Python behave this way? Arrays? Dictionaries? Or am I totally wrong and confused.

aquagremlin
  • 3,515
  • 2
  • 29
  • 51

3 Answers3

2

When a variable is defined, you are telling Python that future occurrences of this name are referring to ... object. That holds even with definitions like a = b. b refers to an object and you are telling Python that a is now also referring to that object. Changing which object b refers to does not change a because a is not linked to b; it is linked to the object that b referred to at the moment that a was defined. If you have a list called b and say a = b, then changing a with something like a.append(4) will of course change b because they are the same object. Using a = 4, however, is just changing which object a is referring to; it does not change b.

zondo
  • 19,901
  • 8
  • 44
  • 83
  • please look at this code http://pastebin.com/jHQDgpJD There are two lists, a and b. b is initialized. a is set equal to b. Then an element is appended to list b. Then printing out lists a and b gives different results. List a contains the original list with the appended element. But the object to which b refers is empty! – aquagremlin Apr 14 '16 at 03:02
  • If i use b = b + [3] instead of b.append([3]), then lists a and b are different and the object, to which list a refers, has not changed. And list b contains the new element as expected. – aquagremlin Apr 14 '16 at 03:05
  • so it seems that using the function '+' appends an element to a list 'differently than using .append method. I am SO CONFUSED. – aquagremlin Apr 14 '16 at 03:07
  • this thread http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference and many other threads illustrate the difference using two kinds of subroutines - what we used to call procedures and functions. The idea was that you could only get information back out from the inside of a subroutine by using the keyword 'return'. This idea was tied to the idea of 'scope'. However in my examples above, there is no use of functions or returned parameters. – aquagremlin Apr 14 '16 at 04:20
  • `.append()` works on the list itself. It puts a new item at the end. It returns `None`, but does some work on the list itself. Therefore, the list is changed when you do `b.append([3])`, but `b` is then reassigned to what `.append()` returned: `None`. When you do `b = b + [3]`, `b + [3]` is a new list that has `b`'s items plus a three. You then assign `b` to that new list and `a` is not changed. – zondo Apr 14 '16 at 09:51
1

Like in Java, everything in Python is passed and assigned by value. All values (every expression and variable) in Python are references (pointers to objects), and assigning one variable to another make the second variable point to the same object as the first.

When you say you are making "manipulations onto img1", what you are doing is you are calling methods on the object pointed to by img1 (using the subscript or slice syntax, e.g. img1[...] = dst, is still implicitly calling methods on the object pointed to by img1) that are mutating that object. Those changes can be seen through any other object pointer that happens to be pointing to that object. That has nothing to do with passing or assigning. Calling methods is not assigning or passing. If all you did with img1 was simply assign various things to the variable img1 (i.e. img1 = something), you would indeed see that such statements never have an effect on what object img1a points to. That is what assigning by value means.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • thank you for your comment but I am still confused. In case #1 (when I set img1a = cv2.imread('3D-Matplotlib.png')), then the transformations to img1a appear as expected. But in case #2(when I set img1a = img1), then the manipulations to img1a seem to be ignored and I see two copies of img1. Is that because in case#2 the variable img1a is pointing to the SAME bits as img1? – aquagremlin Apr 14 '16 at 02:42
  • here is simple code http://pastebin.com/YZT73Qtt a and b are set to some numbers. a is then set to equal b. then i 'manipulate' b. but a does not change. this is different behavior. – aquagremlin Apr 14 '16 at 02:42
  • please look at my comments to zondo's remark below. – aquagremlin Apr 14 '16 at 03:07
-2

There are 2 types of objects: mutable and immutable as explained: https://en.wikibooks.org/wiki/Python_Programming/Data_Types#Mutable_vs_Immutable_Objects Following are immutable (the rest all are mutables like list, dict, etc, even user defined objects.): . int, float, long, complex . str . bytes . tuple . frozen set

If you assign a mutable object to a variable, its reference is copied. So any changes in one will always be reflected in other. Where as for immutable, this is not the case.

ballade4op52
  • 2,142
  • 5
  • 27
  • 42
Jimmy
  • 55
  • 3
  • 3
    No: Python passes all objects in exactly the same way, independent of the object's mutability. This can be confirmed by printing the `id` of the objects outside the call and inside the function. – DSM Apr 14 '16 at 00:11