简体   繁体   中英

Why does it say index out of bounds when there are two elements in the list and the index is 1

This is my code:

from tkinter import Label, Button, Tk, Toplevel
import sqlite3

db = sqlite3.connect('people.db')
sql = db.cursor()


def person_viewer(person: str) -> None:
    person_viewing_window: Toplevel = Toplevel()
    person_viewing_window.title(person)
    Label(person_viewing_window, text=person).pack()


root: Tk = Tk()
root.title("People")
root.geometry('800x600')
count: int = 0
people: list = []
for person in sql.execute('SELECT first_name FROM people;'):
    people.append(person)
    Button(root, text=person, command=lambda: person_viewer(people[count])).pack()
    count += 1
root.mainloop()

I used the debug feature of VS Code to make sure the index really isn't out of bounds and found it wasn't. The index was 1 and the length of the list is 2. That means the second element in the list because of Python's zero-based index. The exact error is below:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\me\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "c:\Users\me\Real Documents\people.pyw", line 21, in <lambda>
    Button(root, text=person, command=lambda: person_viewer(people[count])).pack()
IndexError: list index out of range

As the error indicates, the error only triggers on the click of any of the buttons.

Interestingly, when run in PyCharm Community Edition 2020.2, it gives a quite different error:

C:\Users\me\AppData\Local\Programs\Python\Python38-32\python.exe "C:/Users/me/Real Documents/people.pyw"
Traceback (most recent call last):
  File "C:/Users/me/Real Documents/people.pyw", line 19, in <module>
    for person in sql.execute('SELECT first_name FROM people;'):
sqlite3.OperationalError: no such table: people

This doesn't occur when run from IDLE, Visual Studio Code, or py.exe.

I don't understand why index is out of bound, and moreover, why PyCharm gives a different, definitely wrong, error. For the record, here's my SQL:

BEGIN TRANSACTION;

CREATE TABLE people (id INTEGER PRIMARY KEY NOT NULL AUTOINCREMENT, first_name TEXT NOT NULL, last_name TEXT NOT NULL);

INSERT INTO people VALUES(1,'My','Name');
INSERT INTO people VALUES(2,"Sister's",'Name');

COMMIT;

If you want your lambda function to refer to the person currently under consideration in your loop, you can copy that value to a dedicated variable inside the lambda function.

A way to do that would be like this:

command=(lambda p=person: person_viewer(p))

That binds the current value of person to the default value of an argument p in the function you are defining, and then calls person_viewer on that argument.

In context:

for person in sql.execute('SELECT first_name FROM people;'):
    people.append(person)
    Button(root, text=person, command=(lambda p=person: person_viewer(p))).pack()
    count += 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM