Python: function takes 1 positional argument but 2

2019-01-26 16:45发布

I was creating a Sudoku Game in python with Tk.

I got a error about the function on a keypress for a button

from random import randint
from tkinter import *

class sudoku:
    global root,result,lb
    def __init__(self):
        self.aleatoriedade()
        for k in range(9):
            j=randint(0,80)
            x=j//9
            y=j-(x*9)
            lb[x][y]['text']=result[x][y]
        lb[0][0].bind('<KeyPress-2>',self.kk)
        #setted this for test
        root.mainloop()

    def kk(self):
        lb[0][0]['text']='2'


    def aleatoriedade(self):
        for i in range(9):
            var=0
            while var in result[0]:
                var=randint(1,9)
            result[0][i]=var

        for i in range(1,9):
            for j in range(9):
                result[i][j]=result[0][field[i][j]-1]

#MAIN()
n = 3
field = [[(i*n + i//n + j) % (n*n) + 1 for j in range(9)]for i in range(9)] 
result = [[None for i in range(9)]for i in range(9)]
lb=[[None for i in range(9)]for i in range(9)]
x=0
y=0
root=Tk()

for i in range(9):
    for j in range(9):
        lb[i][j]=Button(root,font=("Verdana",'13',"bold"),bd=1,height=3,width=6)
        if (i in (0,1,2,6,7,8) and j in (0,1,2,6,7,8))or(i in (3,4,5) and j in (3,4,5)):
            lb[i][j]['bg']='white'
        lb[i][j].grid(row=i,column=j)
janela=sudoku()

and this error/exception in lb[0][0].bind('<KeyPress-2>',self.kk)

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__init__.py", line 1489, in __call__
    return self.func(*args)
TypeError: kk() takes 1 positional argument but 2 were given

I don't mind where is the error. I have included the self on my function

4条回答
Summer. ? 凉城
2楼-- · 2019-01-26 16:57

Change kk definition to

def kk(self, event):
    ...

then when you pass self.kk as callback, tk will call it like func(event) (self.kk(event)) and everything will be fine.

Now when tk calls func(event), which is like self.kk(event), the number of arguments is wrong.

查看更多
一夜七次
3楼-- · 2019-01-26 17:07

I'm not a tkinter expert, but it seems (by what I've read so far) that the method

bind(some_string, some_function)

calls function passing the parameter string to it.

You have declared the method kk like

def kk(self):

and it means that it is only expecting one argument. You are also passing the method self.kk to bind(), which means that it will be called like

self.kk('<KeyPress-2>')

There is the problem! That call, in fact, is passing two arguments to the method kk. It's equivalent to

sudoku.kk(janela, '<KeyPress-2>')

Note that janela is the actual instance of the class sudoku. Coming back to the problem, you are passing two arguments!!!

How can you solve it?

As I said I'm not an expert on this topic, but my guess is to declare the method kk with two parameters:

def kk(self, another_parameter):
    # ...

Note: I would recommend you to follow Python naming conventions. In other words, class names should be like SomeClassName or Sudoku.

查看更多
Luminary・发光体
4楼-- · 2019-01-26 17:11

I see that this has been answered, but I have a way I really prefer and that you and others may appreciate.

Say that your method kk is used in multiple spots, and you don't want to have to send in some random variable to take up the spot of "another_parameter" shown below (working off of Christian's response),

def kk(self, another_parameter):

Personally, I think parameter lists should have ONLY what they need. So, as long as you have no need for the "another_parameter" variable that the bind() function sends, I suggest using Lambda by doing the following:

lb[0][0].bind('<KeyPress-2>',lambda e:self.kk())

I think you need the two parentheses after kk now because lambda is actually going to run that function with it's parameters (in your case, if you remove the one I said to, there would be none). What lambda is doing for us is catching the parameter being thrown to kk from the bind function (that is what the 'e' is after lambda, it represents the argument). Now, we don't need it in our parameter list, and we can resume our kk definition to be

def kk(self):

I started using the approach by Christian (which works!) but I didn't like the extra variable. Obviously both methods work, but I think this one helps, especially if the function being called in bind is used more than once and not necessarily used by a bind call.

查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-01-26 17:18

You need to define kk function as this:

def kk(self, event):
    lb[0][0]['text']='2'

Because you're binding kk to a key press event, and it is automatically passed the event object (which has some useful information about the event), so kk need to have another argument, event, other than self.

查看更多
登录 后发表回答