0

Coming from a C++ background, I'm a little bemused by Python's variable usage and, in particular, the creation of class objects. Consider this code below:

class Cat(object):
    """My pet cat"""

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return "{0}, {1}".format(self.name, self.age)


class Age(object):

    def __init__(self, a):
        self.age = a

    def __repr__(self):
        return "{0}".format(self.age)

With the output:

>>> name='milo'
>>> age =4
>>> m = Cat(name, age)
>>> print(m)
milo, 4
>>> age +=1
>>> print(m)
milo, 4

>>> name='rex'
>>> age =Age(4)
>>> rex = Cat(name, age)
>>> print(rex)
rex, 4
>>> age.age += 55
>>> print(rex)
rex, 59

For some reason rex gets older, and milo stays 4 years old. So if I make another cat to replace the old one:

>>> age.age -=55
>>> spike = Cat('spike', age )
>>> print(spike)
spike, 4
>>> print(rex)
rex, 4

Both spike and rex are now the same age.

How should I code the Cat and Age classes so that I don't accidentally rejuvenate the old cats?

And why is it that 'Milo' does not age by one year when doing age +=1?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
CodingFrog
  • 1,225
  • 2
  • 9
  • 17
  • 7
    Milo doesn't age because integers are *immutable*, and you're assigning to `age` not `m.age`. If you want to prevent the age attribute being decreased, look into *properties* to control getting and setting attribute values. – jonrsharpe Oct 14 '18 at 21:13
  • 3
    Python doesn't pass pointers around. `age` is not the same reference as `m.age`. – Martijn Pieters Oct 14 '18 at 21:14
  • I thought everything was an object in Python, and that doing an in-place addition to 'age' would increament Milo's age. – CodingFrog Oct 14 '18 at 21:17
  • @DogBreath integers have no in place addition, `x += y` defaults to `x = x + y` in the case of integers. Try your code with lists and you will see `+=` having mutating behavior. I recently wrote an answer on [+=](https://stackoverflow.com/questions/52747784/is-i-i-n-truly-the-same-as-i-n/52747886#52747886). – timgeb Oct 14 '18 at 21:18
  • So accepting that variables referring to integers are immutable, and therefore the Milo cat gets its own independent 'self.age' variable, how should the Cat and Age classes be coded to prevent unwanted side-effects? – CodingFrog Oct 14 '18 at 21:20
  • 3
    @DogBreath: `reference += integer`, where `reference` references an existing integer object, will *have* to just re-bind `reference` as integer objects are immutable; operations produce a new integer object. Also check out https://nedbatchelder.com/text/names.html. – Martijn Pieters Oct 14 '18 at 21:21
  • 1
    @DogBreath: it is not the variable that is immutable. Variables are just references. The *object* that the variables *references* is immutable. – Martijn Pieters Oct 14 '18 at 21:21
  • Also, to confuse you further, if we're being pedantic, an object could be mutable but have an `__iadd__` method (what's called when you use `+=`) that does not mutate the object but rather builds a new one. – timgeb Oct 14 '18 at 21:23
  • It seems as if I should never pass objects to other class constructors without perhaps doing a shallow-copy or deep-copy. But that seems unusual , is that the pythonic way? Sorry if you've all seen this kind of question before. – CodingFrog Oct 14 '18 at 21:23
  • 1
    @DogBreath It's quite common to make a (shallow) copy of mutable arguments to `__init__` that you want to bind to the initialized instance. – timgeb Oct 14 '18 at 21:24
  • 1
    Thanks guys for all your feedback. Thanks Martijn for the link... – CodingFrog Oct 14 '18 at 21:32

0 Answers0