Question: I have a code given below. when I run the code, all x1,x2 and x3 having same values.As per logic x1 should have random numbers, x2 also random numbers , x3 should have 0s and 1s. 

```
import numpy as np
def reluDerivative(x):
    yy=x;
    yy[yy<=0] = 0
    yy[yy>0] = 1
    return yy

x1=np.random.uniform(-1, 1,10)
x2=x1
x3=reluDerivative(x1)

```

Answer: 
You should think of python variables as **labels**, hence when you do `x2=x1` you are just creating a new label for the same data in memory, this differs in behaviour for mutable and immutable objects, for example:

```
x1 = []
x2 = x1
x2.append(10)
print(x2)
print(x1)

```

will result on printing:

```
[10]
[10]

```

Because both `x2` and `x1` are labeling the same list. This is the behaviour for mutable objects, for immutable objects:

```
x1 = 10
x2 = x1
x2 += 5
print(x2)
print(x1)

```

will print:

```
15
10

```

Because in this case `x2` is re-assigned to label the new created `15` due to the `+` operation.

The solution is just to copy the data from the original object (for your numpy object):

```
x2 = np.copy(x1)

```
You just give the same list `x1` another name: `x2` that bot point to the same data. modifying one is modifying the data that the other one points to as well.

You need to (shallow) copy:

```
import numpy as np
def reluDerivative(yy):
    return [0 if x <=0 else 1 for x in yy ]

x1 = np.random.uniform(-1, 1,10)
x2 = x1.copy()    # copy of x1, no longer same data
x1[0] = 2         # proves decoupling of x1 and x2
x3 = reluDerivative(x2)

print(x1, x2, x3)

```

Output:

```
    [2.           0.84077008 -0.30286357 -0.26964574  0.3979045  -0.8478326
 -0.4056378   0.16776436  0.55790904  0.3550891 ]
    [-0.00736343  0.84077008 -0.30286357 -0.26964574  0.3979045  -0.8478326
 -0.4056378   0.16776436  0.55790904  0.3550891 ]
    [0, 1, 0, 0, 1, 0, 0, 1, 1, 1]

```