Функция exec не видит переменную python

День добрый, необходимо создавать множество кнопок, проблема в том, что без возведения в функцию всё работает отлично, но как только я возвожу в функцию:

NameError: name 'id_button_01' is not defined
M = {1: '01', 2: 'M', 4: 0}


def func(E):
    exec(f"id_button_{E} = tk.Button(window, image=M_img, command=pressed(M[4], M[2], id_button_{E}))")
    exec(f"id_button_{E}.place(x=0, y=0)")


func(M[1])

Помогите, в чём проблема? Или как можно написать данную функцию без exec? Сильно не пинайте, совсем ещё мал и неопытен. Заранее благодарю.


Ответы (3 шт):

Автор решения: Ян Щербаков

Дело в том, что exec не может влиять на глобальные переменные. Попробуйте

M = {1: '01', 2: 'M', 4: 0}


def func(E):
    exec(f"id_button_{E}") = tk.Button(window, image=M_img, command=pressed(M[4], M[2], id_button_{E}))
    exec(f"id_button_{E}").place(x=0, y=0)


func(M[1])

https://www.codepile.net/pile/LVk5ryNQ

→ Ссылка
Автор решения: Daniil Loban

Ошибка возникала не из-за exec а по причине того что в конструктор кнопки передавалась еще не инициализированная переменная id_button_N, при разбитии на 2 комманды все работает. Так же я использовал лямбду для инициализации command.

import tkinter as tk
from tkinter import messagebox as mb

M = {1: '01', 2: 'M', 4: 0}
window = tk.Tk()
M_img=None

def pressed(a,b,c):
  mb.showinfo(title="'"+str(a)+"'", message=b)

def func(E):
  exec(f"""id_button_{E} = tk.Button(window, image=M_img)
id_button_{E}['command'] = lambda:pressed(M[4], M[2], id_button_{E})""",
  globals())
  
  exec(f"id_button_{E}.place(x=0, y=0)", globals())


func(M[1])

window.mainloop()

Вот небольшой пример как передавать внутрь exec переменные:

N = 100;
ns_globals = {'N':N}
ns_locals = {'K': 20}
exec('N += K\nprint("№1", N)', ns_globals, ns_locals)
print('№2', N)
exec('N += N', globals())
print("№3", N)

выведет:

№1 120
№2 100
№3 200

exec(obj[, globals[, locals]])-> None

obj : Строка кода, либо объект кода. Поддерживается ввод данных из файлов (считываются до EOF). Если передана строка, она разбирается в набор инструкций, который потом исполняется (если отсутствуют синтаксические ошибки). Если передан объект кода, то он исполняется.

globals : Ожидается dict. Словарь глобального пространства, относительно которого следует исполнить код. Если locals не передан, будет использован globals. Если словарь не имеет значения по ключу __builtins__, на это место вставляется ссылка на значение аналогичного атрибута глобального пространства. Модифицировав __builtins__ словаря передаваемого в функцию, вы можете контролировать, какие какие из встроенных инструментов будут доступны коду.

locals : Ожидается объект-отображение (например, dict). Локальное пространство, в котором следует исполнить код.

Функциям exec() и eval() можно передавать результаты функций globals() и locals().

→ Ссылка
Автор решения: gil9red

Или как можно написать данную функцию без exec?

Используйте словарь.

Например:

M = {1: '01', 2: 'M', 4: 0}
id_button = dict()

def func(E):
    id_button[E] = tk.Button(window, image=M_img, command=pressed(M[4], M[2], id_button[E]))
    id_button[E].place(x=0, y=0)

func(M[1])

Тогда все кнопки, добавленные через func, можно будет получить из словаря id_button

→ Ссылка