views:

107

answers:

3

I know it is probably a stupid question, but I am new to OOP in Python and if I declare a function def myFunction( b) and pass an instance of an object to it, I get TypeError: expected string or buffer.

To be more specific, I have a following code that I use to parse a summary molecular formula and make an object out of it.

class SummaryFormula:
    def __init__( self, summaryFormula):
        self.atoms = {}
        for atom in re.finditer( "([A-Z][a-z]{0,2})(\d*)", summaryFormula):
          symbol = atom.group(1)
          count = atom.group(2)

    def extend( self, b):
        # these are the two dictionaries of both molecules
        originalFormula = self.atoms.copy()
        self.atoms.clear()
        addAtoms = SummaryFormula( b)

        # and here both dictionaries are merged
        for atom in addAtoms.atoms.keys():
          if atom in originalFormula.keys():
            self.atoms[ atom] = originalFormula[ atom]
            self.atoms[ atom] += addAtoms.atoms[ atom]
          else:
            pass
        for atom in originalFormula.keys():
          if atom not in self.atoms.keys():
            self.atoms[ atom] = originalFormula[ atom]

 #this is what works now
 test = SummaryFormula( "H2CFe2")
 test.extend("H5C5") #result is a molecule H7C6Fe2

 #this is what I want instead
 test = SummaryFormula( "H2CFe2")
 toExtend = SummaryFormula( "H5C5")
 test.extend( toExtend)

Thank you, Tomas

+1  A: 

Firstly, the program needs to include the re module.

Secondly, you have a typo at line 4:

for atom in re.finditer( "([A-Z][a-z]{0,2})(\d*)", SummaryFormula):

should read

for atom in re.finditer( "([A-Z][a-z]{0,2})(\d*)", summaryFormula):

i.e. lower-case s in summaryFormula.

SummaryFormula refers to the name of the class while summaryFormula refers to the second parameter (after self) of the __init__ method.

Third, the line addAtoms = SummaryFormula(b) is passing an instance of SummaryFormula as argument b (assigned in top-level part of script test.extend(toExtend).

The fixed program should look like:

import re

class SummaryFormula:
    def __init__(self, summaryFormula):
        self.atoms = {}
        for atom in re.finditer("([A-Z][a-z]{0,2})(\d*)", summaryFormula):
          symbol = atom.group(1)
          count = atom.group(2)

    def extend( self, b):
        # these are the two dictionaries of both molecules
        originalFormula = self.atoms.copy()
        self.atoms.clear()

        # PASS AN APPROPRIATE VALUE HERE!
        addAtoms = SummaryFormula("SOME STRING")

        # and here both dictionaries are merged
        for atom in addAtoms.atoms.keys():
          if atom in originalFormula.keys():
            self.atoms[ atom] = originalFormula[ atom]
            self.atoms[ atom] += addAtoms.atoms[ atom]
          else:
            pass
        for atom in originalFormula.keys():
              if atom not in self.atoms.keys():
            self.atoms[ atom] = originalFormula[ atom]

#this is what works now
test = SummaryFormula("H2CFe2")
test.extend("H5C5") #result is a molecule H7C6Fe2

#this is what I want instead
test = SummaryFormula("H2CFe2")
toExtend = SummaryFormula("H5C5")
test.extend(toExtend)

with "SOME STRING" replaced by the intended string literal or string variable reference. I don't know the exact intention of the program so I'll leave it up to somebody else to determine what this program should be passing to the constructor of SummaryFormula at this point.

Hope that helps!

Richard Cook
Thank you, I repaired that, but that still doesn't solve the problem.
Tomas Novotny
Extended answer with comment about "addAtoms = SummaryFormula( b)" line.
Richard Cook
+1  A: 

Richard Cook is correct. There is another problem, however: In extend, you say:

addAtoms = SummaryFormula( b)

Thus, a SummaryFormula instance is passed into the __init__ method of SummaryFormula. Here (modulo the typo mentioned before), this object is given to re.finditer:

for atom in re.finditer( "([A-Z][a-z]{0,2})(\d*)", summaryFormula)

The function re.finditer expects a string; it doesn't know what to do with a SummaryFormula instance.

There are a few ways to fix this. The immediately simplest is to check whether you already have a SummaryFormula instance before trying to create one:

if isinstance(b, SummaryFormula):
    addAtoms = b
else if isinstance(b, str):
    addAtoms = SummaryFormula(b)
else:
    raise TypeError("Expected a SummaryFormula or equivalent string.")
David M.
Perfect, thank you very much!
Tomas Novotny
You're welcome. Good luck.
David M.
+1  A: 

In short you can pass any object to a function, including instances of classes you created.

More generally, everything in Python is an object. Check out more information at: http://diveintopython.org/getting_to_know_python/everything_is_an_object.html

This is a key concept in Python so if you're new to the language spend some time getting familiar as it will help your write code which is more concise and pythonic.

The error you get isn't from passing a class instance object, but rather is generated somewhere (see other answers as they seem to be onto it) else in your code by another function or operation which is expecting a string, or string-like object to operate on. For example you can generate a similar error by doing:

>>> a = 2
>>> open(a, 'r')

TypeError: coercing to Unicode: need string or buffer, int found

Here the error occurs because the file opening function open is expecting a string rather than an integer.

dtlussier