【问题标题】:How to share data between two classes如何在两个类之间共享数据
【发布时间】:2013-02-25 09:25:57
【问题描述】:

问题

有没有办法让两个类同时相互继承?

背景

我目前正在处理一个 Socket Server 项目。在这个项目中,我有两个类,一个Server 类和一个GUI 类。他们的目的是不言自明的。但是,我显然需要让这两个类相互通信。在程序中,我首先声明了Socket_Server 类,然后是GUI 类。

我问了一个类似的问题,How to Access Functions from Methods in Python,但从未得到令人满意的回答。请尝试回答。

代码和错误

GUI 类中,我有一个名为self.message. 的文本框,用于向所有客户端发送消息。我试图通过使用这种语法来继承这个类:

class Socket_Server(GUI.messageFun):

接下来,GUI类继承自Socket_Server——

class GUI(Frame, Socket_Server): 

第二个继承GUI(Socket_Server) 工作正常,但第一个失败。

按钮的命令是这样的

        self.send = Button (self.messageFrame, text = "Send",
                        command = lambda: new_server.send_cmd()) 

new_serverSocket_Server 类的一个实例。

当前的错误信息是这样的:

Socket Created
Socket Bind Complete
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
    return self.func(*args)
  File "D:\Python Programs\Sockets\IM Project\Server\Server GUI InDev Class.py", line 129, in <lambda>
    command = lambda: new_server.send_cmd())
  File "D:\Python Programs\Sockets\IM Project\Server\Server GUI InDev Class.py", line 82, in send_cmd
    message = self.message.get("0.0",END)
AttributeError: Socket_Server instance has no attribute 'message'

【问题讨论】:

  • 注意:这可能不是最合适的标题——我不知道该怎么称呼它。如果您有更好的想法,请随时编辑它。如果您需要更多代码,请发表评论。
  • 您似乎对面向对象编程和类层次结构应该如何工作感到困惑。例如,您不应该尝试让GUI 继承自Socket_Server。这种继承关系是在说“GUI 是套接字服务器的特例”,这不是真的! GUI 是 GUI,服务器是服务器。我建议您在继续之前阅读一些介绍性 OOP 教程。
  • @poorsod 哈哈 - 我以为没有人会知道我是新人 :) 我已经阅读了 [this one] (sthurlow.com/python/lesson08) 并且正在努力通过 Dev Shed Tutorial on OOP。我把我的Python Book 租给了我在学校的一位朋友——我很高兴编程开始流行起来。无论如何,您还有其他建议吗?
  • 为了帮助您进行搜索,您应该使用对象 composition(其中类包含对其他类实例的引用),而不是继承。
  • 关于您之前评论中的链接 - 在我看来,第一个评论充满了废话,您应该忘记它告诉您的一切。 Dev Shed 教程是一个很好的语法参考,但似乎并没有说明 OOP 是什么for

标签: python oop user-interface python-2.7 tkinter


【解决方案1】:

这个问题的标题与您在开始时提出的问题不同(而且更准确一点),但它们都不符合您的要求。您需要的是沟通不同类的两个实例;这不是继承。

当你使用继承时,你应该问自己是否每个 X 也是一个 Y:

  • 每个Employee 都是Person 吗? 是的
  • 每个Person 都是Employee 吗? 没有
  • 每个Employee 都是Car 吗? 没有

考虑到这个概念,您可能会看到第三个示例与GUIServer 之间的关系最相似。这是因为你真正的问题是:

  • GUI 实例是否使用Server 对象?

如果答案是肯定的,那么服务器应该是你的GUI 类的一个属性。在下面的示例中,您可以看到GUI 调用了Server 对象的方法,但反之则不然:

import Tkinter as tk
import threading
import socket

class Server():
    def __init__(self):
        self.addresses = [('localhost', 12345)] # All addresses to send data
    def sendtoall(self, data):
        for address in self.addresses:
            self._send(address, data)
    def _send(self, address, data):
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect(address)
            sock.sendall(data)
        finally:
            sock.close()

class GUI(tk.Tk):
    def __init__(self, server):
        tk.Tk.__init__(self)
        self.server = server
        self.entry = tk.Entry(self)
        self.button = tk.Button(self, text="Send", command= self.sendmessage)
        self.entry.pack()
        self.button.pack()
    def sendmessage(self):
        message = self.entry.get()
        threading.Thread(target=self.server.sendtoall, args=(message,)).start()

server = Server()
gui = GUI(server)
gui.mainloop()

编辑:此代码看起来更像是客户端而不是服务器,因此最好将其重命名为与您的概念更相似的名称(例如,Notifier )

【讨论】:

  • threading.Thread(target=self.server.sendtoall, args=(message,)).start() 是什么意思?能否请您详细说明。另外,感谢您提供的出色示例。
  • @xxmbabanexx 它启动一个执行self.server.sendtoall(message) 方法的新线程。如果花费的时间太长,则当它们在同一个线程中运行时,GUI 会冻结。这就是多线程在您的情况下可能有用的原因。
  • 抱歉没有说明清楚。我对target =args = 的含义感到困惑。非常感谢所有的帮助 - 这个问题一直困扰着我。
  • 我在移动设备上发布了上述问题,忘记通知您。
【解决方案2】:

您遇到的问题是面向对象。你需要去看看继承是如何工作的,对象是如何交互的,所以第四个是一个有点沉重的主题,需要在一篇文章中介绍,并且需要经验来理解它。

关于 tkinter 的附注,命令属性是 Button 需要一个引用。这样每次单击按钮时都会调用该函数。

self.send = Button (self.messageFrame, text = "Send",
                        command = new_server.send_cmd())

也就是说 command 等于 new_server.send_cmd() 的结果。

self.send = Button (self.messageFrame, text = "Send",
                    command = new_server.send_cmd)

这是说 command 等于对 send_cmd 方法的引用。

【讨论】: