How can I use string representation to print the combined molecule?

What I don’t understand is how making the __add__ method return Molecule([self, other]) “returns a Molecule with the two Atom s” like the exercise tells us to do. I’ve tried uncommenting the comment in line 16 and printing out salt but all that’s printed out is “<main.Molecule object at 0x7f66084df6a0>”. How is “<main.Molecule object at 0x7f66084df6a0>” a molecule with the two atoms? ;-;

Do you have reason to expect any other output?
Perhaps you might write code which causes a different string representation of your object.

I was expecting the output to be “NaCl”, as it’s a molecules with 2 atoms (I think). This was my initial solution to the exercise:

class Atom:
def init(self, label):
self.label = label

def add(self, other):
return self.label + other.label

class Molecule:
def init(self, atoms):
if type(atoms) is list:
self.atoms = atoms

sodium = Atom(“Na”)
chlorine = Atom(“Cl”)
salt = Molecule([sodium, chlorine])
salt = sodium + chlorine
print(salt)

“NaCl” gets printed to the console. But this wasn’t the right answer. My question now is how is the answer presented by codecademy a molecule with 2 atoms?

In order to expect that output there would need to be code which writes those specific characters to stdout. There’s not. So no reason to expect it. The number of atoms wouldn’t affect this. You could print out an atom and get a similar result, again, no code that is doing string formatting for that.

What affects the string representation of an object?

Oh I get it now. repr() is the method responsible for the string representation of an object not add(), so my solution was definitely wrong. And by making add() return Molecule([self, other]) we are literally returning a molecule with 2 atoms.

Thanks a lot for taking your time to reply. I was afraid no one would answer at first :sweat_smile:

Try add the following code inside of the Molecule class as the dunder method. Then try to print your “:salt:”.

def __repr__(self):
  molecular_formula = ""
  for index in range(len(self.atoms)):
    molecular_formula += salt.atoms[index].label
  return molecular_formula
5 Likes

This should definitely be added as a second task for this exercise, thank you!

2 Likes

Added some string representation to help see results.

class Atom:
  def __init__(self, label):
    self.label = label
  def __repr__(self):
    return self.label
  def __add__(self, other):
    return Molecule([self, other])
    
class Molecule:
  def __init__(self, atoms):
    if type(atoms) is list:
	    self.atoms = atoms
  def __repr__(self):
    return str(self.atoms)
sodium = Atom("Na")
chlorine = Atom("Cl")
salt = Molecule([sodium, chlorine])
print("Original Salt: " + str(salt))
newsalt = sodium + chlorine
print("New Salt: " + str(newsalt))
1 Like

This confuses me even more. Shouldn’t the result be “NaCl” not the list?

I really, really just don’t understand this at all. The RGB example made sense but the exercise is totally throwing me for a loop. Grrrrr!!!

You are correct. I probably should have returned the string NaCl instead of the string [Na, Cl].
I was lazy and returned the string version of the list.

I could rewrite Molecule so it uses this return line instead.

return (f"{salt.atoms[0].label}{salt.atoms[1].label}")

Thanks to @mtf for that line.

1 Like

I just wish we could go back to that lesson and join all the pieces of this conversation. The link in the above page no longer works. Is anybody closely connected enough to put us back in touch?

1 Like

I believe this link in the original FAQ post above goes to the lesson in question.

1 Like

Another fun way to unpack this at the end using the asterisk and string formatting.

class Atom:
  def __init__(self, label):
    self.label = label
  def __repr__(self):
    return str(self.label)
  def __add__(self, other):
    return Molecule([self, other])
    
class Molecule:
  def __init__(self, atoms):
    if type(atoms) is list:
	    self.atoms = atoms
  def __repr__(self):
    return str(self.atoms)
sodium = Atom("Na")
chlorine = Atom("Cl")
salt = Molecule([sodium, chlorine])
print("Original Salt: " + str(salt))
newsalt = sodium + chlorine
print("New Salt: {}{}".format(*newsalt.atoms))

You can try this if you want to find the molecule printed with the atoms in the list side by side, ie: [‘Na’, ‘Cl’]

class Atom:
  def __init__(self, label):
    self.label = label
    
  def __add__(self, other):
    return Molecule([self.label, other.label])
    
class Molecule:
  def __init__(self, atoms):
    if type(atoms) is list:
	    self.atoms = atoms

  def __repr__(self):
    return "{}".format(self.atoms)
      
sodium = Atom("Na")
chlorine = Atom("Cl")
salt = Molecule([sodium, chlorine])
salt = sodium + chlorine
print(salt)

The __repr__ isn’t even necessary. We already have enough methods and instance variables to achieve this.

class Atom:
  def __init__(self, label):
    self.label = label
  
  def __add__(self, other):
    return Molecule([self, other])

class Molecule:
  def __init__(self, atoms):
    if type(atoms) is list:
	    self.atoms = atoms
      
sodium = Atom("Na")
chlorine = Atom("Cl")
#salt = Molecule([sodium, chlorine])
salt = sodium + chlorine

print("".join(atom.label for atom in salt.atoms))

That means having a line similar to this for every molecule our program produces. It makes so much more sense to just be able to print the instance…

print (salt)

That line need only be written once, in the class definition so it works for all molecules.

    def __repr__(self):
        return ''.join(x.label for x in self.atoms)

carbon_monoxide = Atom('C') + Atom('O')
print (carbon_monoxide)

#  CO
3 Likes

True, it would definitely be more convenient in the scope of a more general program, where this would be reused.

1 Like