Jump to content

Python 3 Class Instantiation Problem

straight_stewie

I'm writing a class. When run, it gives me the error "obj = ES(equation) NameError: name 'ES' is not defined". Why is this happening?
It has to be something simple. When I got the error I looked up quite a few different class creation and instantiation tutorials. What am I missing?

Here is the code:
 

# This program will be a calculator that can accept infix notation. It will use the shunting yard algorithm to convert
# to Reverse Polish Notation and then the result will be calculated.
equation = "1 + 2,"
obj = ES(equation)
print(obj.get_postfix())


class ES:
    __infix_equation = ""
    __postfix_equation = ""
    __result = ""

    def __init__(self, infix_equation="ERROR: No Equation"):
        self.__infix_equation = infix_equation
        shunting_yard()
        solver()

    def is_num(self, num):
        try:
            int(num)

        except:
            try:
                float(num)

            except:
                return False

        return True

    def shunting_yard(self):

        equation = self.__infix_equation.strip().split() # All equations must be space delimited and end in a comma
        postfix_string = ""
        operator_stack = []

        for i in range(0, len(equation)):
            if is_num(equation[i]):
                postfix_string = "{old} {new}" .format(old=postfix_string, new=equation[i])

            elif (equation[i] != ",") and(equation[i] != ")"):
                operator_stack.append(equation[i])

            elif equation[i] == ")":
                token = operator_stack.pop()

                while token != "(":
                    postfix_string = "{old} {new}" .format(old=postfix_string, new=token)

                    if token != "(":

                        if len(operator_stack) == 0:
                            postfix_string = "ERROR: Mismatched Parenthesis"
                            break

                        token = operator_stack.pop()

            self.__postfix_equation = postfix_string


    def solver(self):
        x = 1 + 2 # Just as a placeholder

    def get_postfix(self):
        return self.__postfix_equation


PS, The Shunting-Yard is incomplete and not perfect.

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

4 minutes ago, straight_stewie said:

obj = ES(equation)

I'm no expert in python but in some languages you need to at least declare a function header without code first and then declare its code later. does that make sense?

             ☼

ψ ︿_____︿_ψ_   

Link to comment
Share on other sites

Link to post
Share on other sites

I think you need to define the class before you create an instance of it. Meaning, you need to move this (or at least the second and third lines):

equation = "1 + 2,"
obj = ES(equation)
print(obj.get_postfix())

down below the class.

Link to comment
Share on other sites

Link to post
Share on other sites

Unlike some languages where the class definition simply tells the compiler the class API and memory layout, Python class, method, and function definitions are themselves executable statements.  Just like when you import a module, the actual act of importing it is to execute it, hence you put any non-initialization code in a block starting with 'if __name__ == "__main__":'

 

Python itself is implemented with dictionaries.  the "class" statement creates a dictionary, then the "def" statements add method references to it.  That is how all scoping works in python is a hierarchy of dictionaries.  "def" just adds a function definition to whatever the parent scope is.  Instantiating a class also makes a dictionary unique to that instance.  Inheritance is then done through back linking these dictionaries.  Instance variable resolution (keeping in mind methods are just variables that store a reference to a function) are done by depth first search, starting with the instance, then the class, then the parent class, etc.  There are some optimizations made, especially for multiple inheritance, but that's the basic principle.  

 

This is likely the source of @Lacrimas problem.  There is no "shunting_yard" or "solver" defined in the current namespaces.  Being in a class method does not put you within the instance's or even the enclosing class's namespace.  This is [part of] the reason for the explicit "self" argument in the method definitions.  This implementation is also necessary, as it is how a distinction is made between the statements "var = 1", "self.var = 1", and "MyClass.var = 1": which are local, instance, and class-static variables, respectively.  

 

Therefore to call other methods from within a method, the proper syntax is to call "self.method_name".

 

 

Again, it's important to keep in mind that methods ARE variables.  They just hold references to functions.  In fact re-assigning "self.shunting_yard = 1" is perfectly valid, and does exactly what it appears to do.  "self.__infix_equation = self.shunting_yard" is also valid.  In fact the class-prototype model allows each instance's API to be mutable at run-time, as well as the class's API...but doing so is typically considered bad practice.

 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×