Как лучше всего получить доступ к объекту подкласса в другом подклассе?

Прилагается минимальный рабочий пример проблемы, с которой я столкнулся:

class A():
    def __init__(self, *args, **kwargs):
        
        self.b=B(self)
        self.c=C(self)
        
        
class B():
    def __init__(self, parent):
    
        self.a1=3
    
class C():
    def __init__(self, parent):
        
        self.a2=4
        
        print(B.a1)
    
    def ab():
        print(B.a1)

C(object).ab()

Каков наилучший способ доступа в классе C к объекту класса B?

В принципе, я создал приложение TKinter, в котором каждый кадр является классом, и я хочу, чтобы в одном кадре был доступ к переменной другого кадра.

РЕДАКТИРОВАТЬ @all, спасибо всем за вклад. Я расширил минимальный рабочий пример для своей конкретной проблемы:

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox


class main_window(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.title("xxx")
        self.geometry("800x1000")
        
        #Build GUI
        self.inputframe = INPUTframe(self)
        self.imregframe = IMREGframe(self)
        self.calcframe = CALCframe(self)

class INPUTframe(tk.Frame):
    def __init__(self, parent, width=800, height=150):
        tk.Frame.__init__(self, parent, width=width, height=height)
        self.place(x=0, y=0)
        
        label= ttk.Label(self, text = "xxx", font=('Arial',12))
        label.place(x=10,y=0)

        self.create_widgets()
        
    def create_widgets(self):
        
        label1 = tk.Label(self, text = "xxx", font=('Arial',10))
        label1.place(x=30,y=30)
        button1 = tk.Button(self, text='xxx',command = lambda: self.path(1))
        button1.place(x=100,y=30)
        self.textentry1=tk.StringVar()
        self.entry1 = tk.Entry (self, textvariable= self.textentry1)
        self.entry1.place(x=160,y=30,width = "350")
        label11 = tk.Label(self, text = "xxx", font=('Arial',10))
        label11.place(x=500,y=30)
        
    def path(self,var):

        match var:
           case 1:
               self.entry1.delete(0, 'end')
               filename = filedialog.askopenfilename(parent=self,title='Choose a file')
               self.entry1.insert("end",filename)
               return filename

class IMREGframe(tk.Frame):
    def __init__(self, parent, width=250, height=150):
        tk.Frame.__init__(self, parent, width=width, height=height)
        self.place(x=0,y=150)

        label4 = tk.Label(self, text = "xxx", font=('Arial',12))
        label4.place(x=10,y=0)
        
        self.SFAtrue=tk.BooleanVar(value=True)
        
        self.create_widgets()      
        
    def create_widgets(self):
        #self.SFAtrue=tk.BooleanVar(value=True)
        checkb1 = tk.Checkbutton(self, text='xxx', variable=self.SFAtrue, onvalue=0, offvalue=1)
        checkb1.place(x=30,y=30)
        checkb2 = tk.Checkbutton(self, text='xxx', variable=self.SFAtrue, onvalue=1, offvalue=0)
        checkb2.place(x=30,y=50)
 

class CALCframe(tk.Frame):
    def __init__(self, parent, width=800, height=100):
        tk.Frame.__init__(self, parent, width=width, height=height)
        self.place(x=0,y=370)
        self.parent=parent
        
        self.create_widgets()
        
    
    def create_widgets(self):
        
        button4 = tk.Button(self, text='xxx', command = lambda: self.calculate(self.parent.imregframe.SFAtrue.get())) #bester Weg um Variabele aus anderer Klasse/Objekt zu holen?
        button4.config( height = 4, width = 12 )
        button4.place(x=50,y=0)

        self.Previewtrue=tk.BooleanVar(value=False)
        checkb3 = tk.Checkbutton(self, text='xxx',variable=self.Previewtrue, onvalue=1, offvalue=0)
        checkb3.place(x=150,y=0)
        label5 = tk.Label(self,text = "xxx", font=("Arial", 9))
        label5.place(x=150,y=30)
        label51 = tk.Label(self,text = "xxx", font=("Arial", 9))
        label51.place(x=150,y=50)
        
    def calculate (self, calcType):
        
        print(calcType)
        print(self.parent.inputframe.textentry1.get())
    
main_window().mainloop()  

То, чего мне не хватало, было self.parent=parent, как было предложено @Obaskly, и теперь оно работает. Это хороший способ сделать это? Переменные становятся довольно длинными: self.parent.inputframe.textentry1.get()

Вы можете разрешить классу передавать ссылку на себя в другой класс, раскрывая свое «я» как свойство или через метод. Обычно вы не хотите, чтобы классы выдавали всю информацию о себе таким образом, но это может сработать (обратной стороной является то, что вещи за пределами этого класса могут в основном испортить все, что касается класса, если вы слишком небрежны в передаче таких ссылок). Возможно, было бы лучше, если бы классы разрешали доступ только к тем вещам, которые они содержат, и предназначены для совместного использования.

topsail 28.08.2024 18:31

Итак, вы хотите получить доступ к экземпляру B из C?

Obaskly 28.08.2024 18:33

@topsail, как это могло выглядеть? А конкретно ограничение определенных атрибутов?

TMul 28.08.2024 18:37

@Obaskly Да, именно

TMul 28.08.2024 18:37

Вам нужно немного больше кода, чтобы быть минимальным примером: вы утверждаете, что ваше tkinter приложение имеет несколько фреймов, но ваш опубликованный код не создает экземпляры нескольких классов.

quamrana 28.08.2024 18:39

Доступ к атрибуту класса можно получить только через экземпляр класса. Для вашего опубликованного кода не создан экземпляр класса B.

acw1668 28.08.2024 18:39
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
6
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В вашем коде есть две ошибки:

  1. Вы пытаетесь получить доступ к B.a1, что неверно, поскольку a1 — это переменная экземпляра, а не переменная класса. Вы должны получить к нему доступ через экземпляр B, который принадлежит A.

  2. Метод ab() внутри C должен принимать self в качестве первого параметра, который будет методом экземпляра.

Вам необходимо сохранить ссылку на экземпляр B, созданный в классе A.

Это должно выглядеть так, чтобы класс C мог получить доступ к экземпляру B и его атрибутам через своего родителя A.

class A():
    def __init__(self, *args, **kwargs):
        self.b = B(self)
        self.c = C(self)


class B():
    def __init__(self, parent):
        self.a1 = 3 


class C():
    def __init__(self, parent):
        self.parent = parent  # Reference to the parent class
        self.a2 = 4
        print(self.parent.b.a1)  # Access B's a1 via the parent

    def ab(self):
        print(self.parent.b.a1) 


# Create an instance of A
instance_of_A = A()

# Access method ab() of class C through instance of A
instance_of_A.c.ab()

РЕДАКТИРОВАТЬ

Имена переменных могут быть довольно длинными, это точно. Есть несколько методов, которые вы можете использовать. Например, как и при использовании ссылок, вы можете создавать ярлыки ссылок на часто используемые виджеты или переменные.

class CALCframe(tk.Frame):
    def __init__(self, parent, width=800, height=100):
        # ...
        
        # Shortcut references
        self.imregframe = self.parent.imregframe
        self.inputframe = self.parent.inputframe
        
        self.create_widgets()
        
    def create_widgets(self):
        button4 = tk.Button(self, text='xxx', command=lambda: self.calculate(self.imregframe.SFAtrue.get()))
        button4.config(height=4, width=12)
        button4.place(x=50, y=0)
        
        # ...
        
    def calculate(self, calcType):
        print(calcType)
        print(self.inputframe.textentry1.get())

Или вы можете использовать класс контроллера; который хранит состояние вашего приложения и позволяет фреймам взаимодействовать через него.

class AController:
    def __init__(self):
        self.main_window = main_window(self)
        
    def get_SFAtrue(self):
        return self.main_window.imregframe.SFAtrue.get()
    
    def get_textentry1(self):
        return self.main_window.inputframe.textentry1.get()

class main_window(tk.Tk):
    def __init__(self, controller, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.controller = controller
        self.title("xxx")
        self.geometry("800x1000")
        
        self.inputframe = INPUTframe(self)
        self.imregframe = IMREGframe(self)
        self.calcframe = CALCframe(self)

class CALCframe(tk.Frame):
    def __init__(self, parent, width=800, height=100):
        tk.Frame.__init__(self, parent, width=width, height=height)
        self.controller = parent.controller
        
        self.create_widgets()
        
    def create_widgets(self):
        button4 = tk.Button(self, text='xxx', command=self.calculate)
        button4.config(height=4, width=12)
        button4.place(x=50, y=0)
        
    def calculate(self):
        calcType = self.controller.get_SFAtrue()
        textEntry1 = self.controller.get_textentry1()
        print(calcType)
        print(textEntry1)

AController()

Спасибо. Кажется, мне не хватало этого self.parent = родителя. Я добавил к своему вопросу расширенный минимальный пример. Не могли бы вы взглянуть на это, если это лучший способ?

TMul 28.08.2024 19:08

@TMul Я отредактировал свой ответ, добавив несколько предложений

Obaskly 28.08.2024 19:36

@Обласки, спасибо

TMul 28.08.2024 20:42

Если вы хотите избежать длинных цепочек точек и лучше соблюдать Закон Деметры, я бы попросил основной класс передавать переменные, а не требовать от других классов их извлечения.

В идеале CALCframe не нужно знать внутренности main_window, INPUTframe и IMREGframe. В противном случае, если вы решите повторно использовать CALCframe в другом месте или вам понадобится вложить его дальше, он перестанет работать.

В вашем конкретном случае простой способ обойти это — передать main_window переменные Tkinter INPUTframe и IMREGframe в CALCframe. Это отделяет CALCframe и позволяет использовать его с любым родителем, если вы передаете соответствующие переменные.

Вот ваш код обновлен, чтобы показать, что я имею в виду:

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox


class main_window(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.title("xxx")
        self.geometry("800x1000")
        
        #Build GUI
        self.inputframe = INPUTframe(self)
        self.imregframe = IMREGframe(self)
        self.calcframe = CALCframe(self, self.inputframe.textentry1, self.imregframe.SFAtrue)

class INPUTframe(tk.Frame):
    def __init__(self, parent, width=800, height=150):
        tk.Frame.__init__(self, parent, width=width, height=height)
        self.place(x=0, y=0)
        
        label= ttk.Label(self, text = "xxx", font=('Arial',12))
        label.place(x=10,y=0)

        self.create_widgets()
        
    def create_widgets(self):
        
        label1 = tk.Label(self, text = "xxx", font=('Arial',10))
        label1.place(x=30,y=30)
        button1 = tk.Button(self, text='xxx',command = lambda: self.path(1))
        button1.place(x=100,y=30)
        self.textentry1=tk.StringVar()
        self.entry1 = tk.Entry (self, textvariable= self.textentry1)
        self.entry1.place(x=160,y=30,width = "350")
        label11 = tk.Label(self, text = "xxx", font=('Arial',10))
        label11.place(x=500,y=30)
        
    def path(self,var):

        match var:
           case 1:
               self.entry1.delete(0, 'end')
               filename = filedialog.askopenfilename(parent=self,title='Choose a file')
               self.entry1.insert("end",filename)
               return filename

class IMREGframe(tk.Frame):
    def __init__(self, parent, width=250, height=150):
        tk.Frame.__init__(self, parent, width=width, height=height)
        self.place(x=0,y=150)

        label4 = tk.Label(self, text = "xxx", font=('Arial',12))
        label4.place(x=10,y=0)
        
        self.SFAtrue=tk.BooleanVar(value=True)
        
        self.create_widgets()      
        
    def create_widgets(self):
        #self.SFAtrue=tk.BooleanVar(value=True)
        checkb1 = tk.Checkbutton(self, text='xxx', variable=self.SFAtrue, onvalue=0, offvalue=1)
        checkb1.place(x=30,y=30)
        checkb2 = tk.Checkbutton(self, text='xxx', variable=self.SFAtrue, onvalue=1, offvalue=0)
        checkb2.place(x=30,y=50)
 

class CALCframe(tk.Frame):
    def __init__(self, parent, textentry1, SFAtrue, width=800, height=100):
        tk.Frame.__init__(self, parent, width=width, height=height)
        self.place(x=0,y=370)
        self.parent=parent

        self.SFAtrue = SFAtrue
        self.textentry1 = textentry1
        
        self.create_widgets()
        
    
    def create_widgets(self):
        
        button4 = tk.Button(self, text='xxx', command = lambda: self.calculate(self.SFAtrue.get())) #bester Weg um Variabele aus anderer Klasse/Objekt zu holen?
        button4.config( height = 4, width = 12 )
        button4.place(x=50,y=0)

        self.Previewtrue=tk.BooleanVar(value=False)
        checkb3 = tk.Checkbutton(self, text='xxx',variable=self.Previewtrue, onvalue=1, offvalue=0)
        checkb3.place(x=150,y=0)
        label5 = tk.Label(self,text = "xxx", font=("Arial", 9))
        label5.place(x=150,y=30)
        label51 = tk.Label(self,text = "xxx", font=("Arial", 9))
        label51.place(x=150,y=50)
        
    def calculate (self, calcType):
        
        print(calcType)
        print(self.textentry1.get())
    
main_window().mainloop()

большое спасибо! Это кажется хорошим. Я попробую.

TMul 28.08.2024 20:42

Другие вопросы по теме