1

I'm relatively new in programming with Python. This code was working perfectly, until I tried turning it into a class. I'm making a class for my sudoku solver code, to practice classes and dabbling my toes in object oriented programming.

So I have read a whole bunch of questions from users who have a similar problem, most answers were : -instantiate class first before calling a function from said class but none of them seem to work for my specific example.

Here is my class:

#assume sudoku is imported as a np.array, white spaces replaced by zeros


class Sudoku():

    solution_number = 1

    def __init__ (self, sud_arr):
        self.sudo = sud_arr

    #print(self.sudo)

    def possible (self, y, x, num):
        for i in range(9):
            if self.sudo[y][i] == num:
                return False
            if self.sudo[i][x] == num:
                return False
            yy = (y//3)*3
            xx = (x//3)*3
            for i in range(3):
                for j in range(3):
                    if self.sudo[yy+i][xx+j] == num:
                        return False
        return True


    def solve(self):
        for i in range(9):
            for j in range(9):
                if self.sudo[i][j] == 0:
                    for nr in range(1,10):
                         if Sudoku.possible(i,j,nr): #line 34
                            self.sudo[i][j] = nr
                            Sudoku.solve()
                            self.sudo[i][j] = 0
                    return
        if Sudoku.solution_number > 1:  #if there is more than one solution, include solution number
            print("Solution Number {}".format(Sudoku.solution_number))
        else: print("Solution Number 1")
        print(self.sudo)                                  
        Sudoku.add_sol_num()

    @classmethod
    def add_sol_num(cls):           
        cls.solution_number += 1

After running:

s = Sudoku(su) #where su is a numpy array sudoku
s.solve() #line 52

I get the error:

  File "/Users/georgesvanheerden/Python/Projects/Sudoku/SudokuSolver.py", line 52, in <module>
    s.solve()
  File "/Users/georgesvanheerden/Python/Projects/Sudoku/SudokuSolver.py", line 34, in solve
    if Sudoku.possible(i,j,nr):
TypeError: possible() missing 1 required positional argument: 'num'
[Finished in 1.9s with exit code 1]

Sorry if this is too much code, I didn't know which parts to cut out.

1
  • Call an instance method on self using self.possible, not Sudoku.possible. You are doing similar things multiple times.
    – khelwood
    Commented Jun 19, 2020 at 10:31

4 Answers 4

1

use self.possible when using a method, Sudoku.possible gets you a reference to that method that cant find the instance that you are calling it from.

That also applies to if Sudoku.solution_number > 1, generally the pythonic way is to use the self variable, or the first argument to the method (although you can also pass self to the function: Solution.possible(self, i, j , nr) )

So your code would look like:

    def solve(self):
        for i in range(9):
            for j in range(9):
                if self.sudo[i][j] == 0:
                    for nr in range(1,10):
                         if self.possible(i,j,nr): #line 34
                            self.sudo[i][j] = nr
                            self.solve()
                            self.sudo[i][j] = 0
                    return
        if self.solution_number > 1:  #if there is more than one solution, include solution number
            print("Solution Number {}".format(self.solution_number))
        else: print("Solution Number 1")
        print(self.sudo)                                  
        Sudoku.add_sol_num() # add_sol_num is a @classmethod
3
  • 1
    Thank you so much, this makes so much sense. So always use self.function() when calling another function from inside the same class, not Classname.function(), otherwise you are creating a new instance. Commented Jun 19, 2020 at 10:38
  • 1
    instance creation only happens when you call the class like a function, this is more like you've entered a room from the wrong door so you cant find what you were looking for, looking for it incorrectly out of habit.
    – Dave Ankin
    Commented Jun 19, 2020 at 10:40
  • @GeorgeSebastiaanvanHeerden It's not creating a new instance; it's calling the method without giving it any instance.
    – khelwood
    Commented Jun 19, 2020 at 10:52
0

you can add self as the first argument:

if Sudoku.possible(self, i, j, nr):  #line 34
0

Let us first understand the error that is being given :

TypeError: possible() missing 1 required positional argument: 'num'

This says that there is no value for num argument while calling possible() method.

But you are passing 3 arguments here :

if Sudoku.possible(i,j,nr)

So what went wrong here ???

If you see the definition of your method:

def possible (self, y, x, num):

This says that you will be passing 4 arguments and one of which would be the instance/object of the class (self argument).

1. If we invoke this method using a class object, `self` argument is passed by default. So in this case we can just send 3 arguments apart from `self`.

2. If you want to invoke this method like you have done above, you will have to provide a value for  `self` argument explicitally.

So, here is how you can do it (pythonic way and good approach) : While invoking the method possible, use self keyword.

if self.possible(i,j,nr):
0

In lines 34 and 36 you call two methods as if they are static methods since you call the methods on the class not on some instance. That also is the reason, why self is not recognized and hence asked for another parameter in the method call. You want to call the methods of the current instance of Sudoku. Therefore

if self.possible(i,j,nr):

in line 34 and

self.solve()

in line 36 should do the trick.

Not the answer you're looking for? Browse other questions tagged or ask your own question.