Question regarding __init__ and __repr__

Let’s assume I have a Class with an init constructor, which takes some arguments (self, a, b, c) but does not store them inside any variables.

Can I access a, b or c at all after an object was created?

For instance, I would like to define a repr method to return a string with a, b and c.
Do I have to store them in a Class-/Object-Variable beforehand to access them in my repr method?

2 Likes

Do you think you need to?

Given that your repr method is likely going to look like this:

def __repr__(self):
  # could put some code here...
  return some representation of the object

Where do you think that __repr__ is going to get these variables a,b and c from to include them in the representation? :slight_smile:

**Edit:** Had a bit of a think.

As a convoluted exercise, which you likely ought to ignore, there’s something like this…

class spam:
    x,y,z = None,None,None
    
    def __init__(self, a, b, c):
        self.x = a * b
        self.y = self.x + c
        self.z = b ** 4

    def __repr__(self):
        c = self.y - self.x
        b = self.z ** 0.25
        a = self.x // b
        return "(%s %s %s)" % (int(a),int(b),int(c))

eggs = spam(2,4,8)

print(eggs)

Where, if we stretch the meaning a (considerable) bit we’re not really storing a,b, or c directly but are calculating values from them… and if we wanted the values back, we need to reverse the calculations… but I think I’m solving some weird imaginary problem here when my original answer is likely better and at this point __repr__ isn’t really representing the object, but the initialization…

(Is this nonsense?? :thinking: )

5 Likes

Thanks for your reply!

After reading your post I think arguments are sent straight into the void if I don’t make sure to store them. Is that correct?

I was wondering, if the arguments passed are actually some kind of object themselves inside the memory.

And about repr:
Can I pass more arguments apart from “self” to it?
From my understanding, repr gets its input from the print() function. Or are there other possibilities to trigger repr?

2 Likes

I would make the point that if you’re not storing the arguments from __init__ somewhere inside the object/class as a member why are you passing them in the first place? (There is, of course, my previous questionable example where we used the initialisation arguments in calculations and kept the results in class attributes…)

In any case, as far as I am aware (and I’m happy to be corrected) yes - anything you pass into an object/class at initialisation which isn’t kept in a variable inside the object/class will be discarded.

If the arguments you pass at initialisation are variables in their own right, then yes they’ll exist elsewhere in the program memory space - but they won’t become class/object attributes or necessarily be accessible by methods inside that class/object…

On the arguments front, again I don’t think so. The point of __repr__ is to return some meaningful string representation of the class/object, so really all it ought to need knowledge of is itself (self). That gives you access to all the class/object methods and attributes, so I don’t see why you’d need to pass anything else in?

There’s more about __repr__ in the documentation here.

2 Likes

The parameters of a method are local variables. After execution of that method has terminated, those variables are gone. Therefore, if you wish to recover, at a later time, the information that was passed to the method, you must use the information to cause some effect that enables the information to be recovered or recreated later.

@thepitycoder has demonstrated that you do not need to store the information from the parameters exactly as given in order to recover it later. The example given performs some calculations and stores the results in instance variables in a manner that enables the original information to be recalculated later.

Other possibilities would be to:

  • store the information in an external file
  • store the information in an entirely different object
  • store the information as a series of instructions that, when followed, would reveal the original values of the parameters

With some imagination, you could probably come up with other ideas. The guiding principle is that the parameters of the __init__ method disappear when execution of the method terminates, so you must do something consequential with the information in order for it to be recoverable later.

4 Likes

Amazing, thank you both very much for your answers.

I am always interested in what happens “under the hood”, now that I’m starting to grasp the concept of classes/objects and their attributes.

Two more questions, if I may :smiley:

  1. Can I define class variables inside a method definition?
  2. Can I define object variables inside a method definition and use them in another method?

I know that 2 is possible with variables declared inside init, but I’m not sure if that’s an exception to the rule or not.

1 Like
  1. Can I define class variables inside a method definition?

You can do the following:

class spam:
    def __init__(self, a, b, c):
        self.x = a * b
        self.y = self.x + c
        self.z = b ** 4

    def add_something(self, thing):
        self.j = int(thing)
        return None

    def __repr__(self):
        return str({k:v for k,v in self.__dict__.items()})

eggs = spam(2,4,8)

print(eggs) # prints {'x': 8, 'y': 16, 'z': 256}

eggs.add_something(1024)

print(eggs) # prints {'x': 8, 'y': 16, 'z': 256, 'j': 1024}
  1. Can I define object variables inside a method definition and use them in another method?

You could, but it may be unwise… For example:

class spam:
    def __init__(self, a, b, c):
        self.x = a * b
        self.y = self.x + c
        self.z = b ** 4

    def do_something_with_j(self):
        print("The value of j is %s" % self.j)
        return None

    def add_something(self, thing):
        self.j = int(thing)
        return None

    def __repr__(self):
        return str({k:v for k,v in self.__dict__.items()})

eggs = spam(2,4,8)

print(eggs) # prints {'x': 8, 'y': 16, 'z': 256}

eggs.do_something_with_j()

eggs.add_something(1024)

print(eggs)

This results in an AttributeError, because eggs.j doesn’t exist when I try and print it.

On the other hand:

class spam:
    def __init__(self, a, b, c):
        self.x = a * b
        self.y = self.x + c
        self.z = b ** 4

    def do_something_with_j(self):
        print("The value of j is %s" % self.j)
        return None

    def add_something(self, thing):
        self.j = int(thing)
        return None

    def __repr__(self):
        return str({k:v for k,v in self.__dict__.items()})

eggs = spam(2,4,8)

print(eggs) # prints {'x': 8, 'y': 16, 'z': 256}

eggs.add_something(1024)

print(eggs) # prints {'x': 8, 'y': 16, 'z': 256, 'j': 1024}

eggs.do_something_with_j() # prints The value of j is 1024

You’d need to be absolutely sure that nothing is going to try and reference that attribute before your code has got around to defining it…

3 Likes

Try this:

class Insect:
  number_of_legs = 6
  population = 0
  def __init__(self, name, number_of_wings):
    Insect.population += 1
    self.name = name
    self.number_of_wings = number_of_wings
    self.serial_number = Insect.population
  def make_new_class_variable(self, val):
    Insect.new_class_variable = val
  def make_new_instance_variable(self, val):
    self.new_instance_variable = val
  def __repr__(self):
    rep = "Insect # {}: {}".format(self.serial_number, self.name)
    rep += "\n Wings = {}".format(self.number_of_wings)
    try:
      rep += "\n new_class_variable = {}".format(Insect.new_class_variable)
    except:
      pass
    try:
      rep += "\n new_instance_variable = {}".format(self.new_instance_variable)
    except:
      pass
    return rep

i = Insect("Buggy the Bug", 4)
print(i)
i.make_new_class_variable("Bugs are colorful!")
print(i)
i.make_new_instance_variable("I eat tomatoes!")
print(i)

Output:

Insect # 1: Buggy the Bug
 Wings = 4
Insect # 1: Buggy the Bug
 Wings = 4
 new_class_variable = Bugs are colorful!
Insect # 1: Buggy the Bug
 Wings = 4
 new_class_variable = Bugs are colorful!
 new_instance_variable = I eat tomatoes!
3 Likes

I think that’s a better example than mine. :slight_smile:

1 Like

All your examples are quite good.

When we are altering class and instance definitions dynamically, the hasattr function may come in handy. See hasattr(object, name).

Following is a version of the __repr__ method for Insect that uses hasattrr instead of try blocks:

  def __repr__(self):
    rep = "Insect # {}: {}".format(self.serial_number, self.name)
    rep += "\n Wings = {}".format(self.number_of_wings)
    if hasattr(Insect, "new_class_variable"):
      rep += "\n new_class_variable = {}".format(Insect.new_class_variable)
    if hasattr(self, "new_instance_variable"):
      rep += "\n new_instance_variable = {}".format(self.new_instance_variable)
    return rep
2 Likes