-2

Here's my code :

hp1 = 100
health1 = 'you have', hp1

hp1 = hp1 - 50
health1

print hp1
print health1

This is what it prints :

50
('you have', 100)

Why doesn't the hp1 change inside the health?

  • 1
    Why do you expect it to change? – Sayse Dec 18 '17 at 15:45
  • because i subtracted 50 from hp1. I'm new to python so I don't reallly know what i'm doing haha – Hussein Amr Dec 18 '17 at 15:47
  • 4
    Recommended reading: https://nedbatchelder.com/text/names.html – VPfB Dec 18 '17 at 15:58
  • 1
    You evaluated `hp1` at the point where you assigned it to `health1`. After that they had no connection. You're not thinking this through. Suppose you had instead used `5 + hp1**2/(1 - hp1)`. Would you still expect the value to track? Did you think you were creating a closure, and that the expression was reevaluated each time `health1` was referenced? Clearly not. – Tom Karzes Dec 18 '17 at 16:02
  • @TomKarzes Not true, at that point there still is a connection: `health1[1] is hp1`. – Stefan Pochmann Dec 18 '17 at 16:25
  • @StefanPochmann No. `health[1]` is `100`, not a reference to `hp1` as you suggest. They have the same value at that point, but that is *not* a connection. A connection implies a persistent, bound relationship. There is none. – Tom Karzes Dec 18 '17 at 16:36
  • @TomKarzes I didn't say it's a reference to the `hp1` variable, if that's what you mean. But it **is** a reference to the same object that `hp1` references. If `hp1` for example were a list and you appended to it, then that change **would** affect `health1`. – Stefan Pochmann Dec 18 '17 at 16:39
  • 1
    @StefanPochmann Yes, it's true that `health[1] is hp1` is `True` at that point. But that's not what I meant. I meant that changing either has no effect on the other. – Tom Karzes Dec 18 '17 at 16:44

5 Answers5

2

The following line:

health1 = 'you have', hp1

Is creating a tuple with two values: "you have" and 100 (Note that the value of hp1 is copied, and not referenced). It's then assigning this tuple to a new variable named health1.

health1 has nothing to do with hp1. If hp1 get overriden, deleted, thrown away, or anything happens to it, health1 doesn't care.


If you are so eager to pass this variable a reference, you can create a wrapper class around the int type:

class IntWrapper(object):
     def __init__(self, value):
          self.value = value
     def __add__(self, value):
          return IntWrapper(self.value + value)
     def __iadd__(self, value):
          self.value += value
          return self
     def __sub__(self, value):
          return IntWrapper(self.value - value)
     def __isub__(self, value):
          self.value -= value
          return self
     def __str__(self):
          return str(self.value)
     def __repr__(self):
          return str(self)

hp1 = IntWrapper(100)
health1 = 'you have', hp1

hp1 -= 50

print hp1          # 50
print health1      # ('you have', 50)
Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • If "the value is copied, not referenced", then why is `health1[1] is hp1` true? – Stefan Pochmann Dec 18 '17 at 16:44
  • Terminology nitpick: there are no primitive types in Python. `int` objects are full objects, just like any other. – juanpa.arrivillaga Dec 18 '17 at 16:56
  • @juanpa.arrivillaga Check out http://pythonvisually.com/ebook/primitive-data-types.html and https://en.wikipedia.org/wiki/Primitive_data_type – Stefan Pochmann Dec 18 '17 at 17:04
  • @StefanPochmann I agree. However, if it don't change the internal value then it will not accomplish what OP wants, because `hp1` would point to a new reference, and `health1` would be pointing to a stale one. – Matias Cicero Dec 18 '17 at 17:24
  • 1
    @MatiasCicero Yes, but you'd better not do that with `__sub__`. Do that with `__isub__` instead. Or maybe better with `decrease` or so, calling it as `hp1.decrease(50)` without assignment notation. – Stefan Pochmann Dec 18 '17 at 17:32
  • @StefanPochmann Good point. Wasn't aware of that distinction. I updated the code – Matias Cicero Dec 18 '17 at 17:34
  • Code looks alright now. You're still saying "value copied, not referenced", though. – Stefan Pochmann Dec 18 '17 at 17:37
  • @StefanPochmann That's because `int` values are immutable and never allocated dynamically (unless it's a big value, I suppose), i.e. `hp1 is 100` will yield true. If you were to have another variable `hp2 = 100`, then `hp1 is hp2` would also yield `true`. Maybe saying *"values are copied"* is more than confusing enough. I'll try to come up with something better. – Matias Cicero Dec 18 '17 at 17:44
  • What do you mean with "never allocated dynamically"? That sounds wrong. And based on your understanding, what do you think this prints? `hp1 = 100 + 200; hp2 = 300; print(hp1 is hp2)` – Stefan Pochmann Dec 18 '17 at 17:50
  • @MatiasCicero that is simply wrong. `int` objects are like any other object in Python. There is a CPython *implementation detail* that small-ints are cached, but that only works for int values `-5` to `256`, but for any other `int` in Cpython, `x = 400; y =400; print(y is x)` will print `False`. Regardless, you should never rely on the behavior that `some_int is some_other_int` just because `some_int == some_other_int`. This is the point I was getting at that there are no primitive types in Python, and `int` objects are full-fledged objects. note `import sys; print(sys.getsizeof(some_int))` – juanpa.arrivillaga Dec 18 '17 at 18:32
  • @juanpa.arrivillaga Where did you run `x = 400; y =400; print(y is x)` and get `False`? It gives me `True` everywhere. – Stefan Pochmann Dec 18 '17 at 19:00
  • @StefanPochmann I was running it in an 3.6 interactive interpreter session. If you put both statements on one line, the compiler will be smart enough to make both objects the same, but if you do two separate lines, `>>> x = 400` then `>>> y = 400` then try `x is y` it is showing false on my machine. – juanpa.arrivillaga Dec 18 '17 at 19:03
  • @juanpa.arrivillaga Yeah, that's why I used `hp1 = 100 + 200` in my above example. – Stefan Pochmann Dec 18 '17 at 19:06
  • @StefanPochmann yes, that probably demonstrates it better than trying to fool the compiler. – juanpa.arrivillaga Dec 18 '17 at 19:08
2

To automatically change the output with any mutations of hp1, you can use a class:

class Health:
   def __init__(self, health):
       self.health = health
   def __add__(self, val):
       return Health(self.health + val)
   def __sub__(self, val):
       return Health(self.health - val)
   def __repr__(self):
       return "you have {}".format(self.health)

hp1 = Health(100)
hp1 -= 50
print(hp1)

Output:

you have 50
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • This is the first answer that addresses the *intent* of the question, rather than just explaining the observed results. – chepner Dec 18 '17 at 15:57
  • @chepner Well, to be fair, the OP explicitly asked *"Why doesn't the hp1 change inside the health?*", and not *"How to...?"* – Matias Cicero Dec 18 '17 at 15:59
  • To be fair, in your example, you could have done `a = 0 ; a += 50 ; print a`. I believe you should have written what I answered to be in the OP's scope. – IMCoins Dec 18 '17 at 16:05
  • Uh... what is this? That does **not** do the desired thing. – Stefan Pochmann Dec 18 '17 at 16:37
0

Because you defined health1 - a (string, int) tuple - as hp1 was still 100 and didn't change it since then. This is not a pointer in C/C++ sense, just a copy by value.

Pavel
  • 7,436
  • 2
  • 29
  • 42
0

In your code you have done like this,

hp1 = 100 # setting hp1 as 100
health1 = 'you have', hp1 # making a tuple 

hp1 = hp1 - 50 # subracting 50 from hp1 -> gives 50 as result
health1 # simply calling health1

print hp1 # displaying hp1
print health1 # displaying health1

In this code,

You defined hp1 as 100, let it be stored in a location 1000

You made a tuple names health1 as 'you have', hp1. It will be stored in a location say 2000

You subtracted 50 from hp1 making hp1 50, this will make no change to health1 variable because it is stored in different location. But it will change the value of hp1

Hope this helps.!!

Sreeram TP
  • 11,346
  • 7
  • 54
  • 108
0

To do what you wish to do, you must use a class. This is the closest form of a pointer you will encounter in python.

Here is an example :

class Health():
    def __init__(self, value):
        self.hp = value

    def __repr__(self):
        return 'You have {}.'.format(self.hp)

health = Health(100)
hp_clone = health
health.hp -= 50

print hp_clone
# Program outputs : You have 50.

Your question is also a possible duplicate of Pointers in Python? .

What is happening here in your program has been explained by the others.

IMCoins
  • 3,149
  • 1
  • 10
  • 25