Skip to content

Latest commit

 

History

History
676 lines (502 loc) · 38.8 KB

File metadata and controls

676 lines (502 loc) · 38.8 KB

十五、理解 OOP 概念

在本章中,我们将介绍以下主题:

  • 面向对象编程
  • 在 GUI 中使用类
  • 使用单一继承
  • 使用多级继承
  • 使用多重继承

面向对象编程

Python 支持面向对象编程OOP。面向对象支持可重用性;也就是说,以前编写的代码可以重用以生成大型应用,而不是从头开始。OOP 中的术语对象是指一个类的变量或实例,其中类是由方法和变量组成的结构的模板或蓝图。类中的变量称为数据成员,方法称为成员函数。创建类的实例或对象时,对象会自动访问数据成员和方法。

创建类

class语句用于创建类。以下是创建类的语法:

class class_name(base_classes):
    statement(s)

这里,class_name是标识类的标识符。class语句之后是构成类主体的语句。class主体由该类中定义的不同方法和变量组成。

您可以创建单个类或继承另一个类的类。被继承的类称为基类。语法中class_name之后的base_classes参数表示该类将继承的所有基类。如果有多个基类,则它们需要用逗号分隔。被继承的类称为超类基类,继承类称为派生类子类。派生类可以使用基类的方法和变量,从而实现可重用性:

class Student:
    name = ""
    def __init__(self, name):
        self.name = name
    def printName(self):
        return self.name

在本例中,Student是一个包含名为name的属性的类,该属性被初始化为 null。

使用内置的类属性

class语句会自动将某些值分配给某些固定的类属性。这些类属性可用于获取有关该类的信息。类属性列表如下所示:

  • __name__:此属性表示class语句中使用的类名
  • __bases__:此属性表示class语句中提到的基类名称
  • __dict__:表示其他类属性的 dictionary 对象
  • __module__:该属性表示定义类的模块名称

一个类可以有任意数量的方法,每个方法可以有任意数量的参数。方法中始终定义一个必需的第一个参数,该第一个参数通常命名为self(尽管您可以为该参数指定任何名称)。self参数引用调用该方法的类的实例。在类中定义方法的语法如下所示:

class class_name(base_classes):
    Syntax:
        variable(s)
    def method 1(self):
        statement(s)
    [def method n(self):
        statement(s)]

类可以具有以下两种类型的数据成员:

  • 类变量:这些是所有实例都可以共享的变量,任何一个实例对这些变量所做的更改也可以被其他实例看到。这些是在类的任何方法之外定义的数据成员。
  • 实例变量:这些在方法内部定义的变量只属于对象的当前实例,称为实例变量。任何实例对实例变量所做的更改仅限于该特定实例,不会影响其他实例的实例变量。

让我们看看如何创建实例方法以及如何使用它来访问类变量。

在实例方法中访问类变量

要访问类变量,类变量必须以类名作为前缀。例如,要访问Student类的name类变量,需要按如下方式访问:

Student.name

您可以看到,name类变量的前缀是Student类名称。

实例

要使用任何类的变量和方法,我们需要创建它的对象或实例。类的实例获取自己的变量和方法副本。这意味着一个实例的变量不会干扰另一个实例的变量。我们可以根据需要创建任意多个类实例。要创建类的实例,您需要编写类名,后跟参数(如果有)。例如,下面的语句创建了一个名为studentObjStudent类实例:

studentObj=Student()

您可以创建任意数量的Student类实例。例如,以下行创建了Student类的另一个实例:

courseStudent=Student()

现在,实例可以访问类的 class 属性和方法。

定义方法时需要明确指定self。调用方法时,self不是必需的,因为 Python 会自动添加它。

为了定义类的变量,我们从__init__()方法获得帮助。__init__()方法类似于传统 OOP 语言中的构造函数,是创建实例后执行的第一个方法。它用于初始化类的变量。取决于__init__()方法在类中的定义方式,即有无参数,参数可能传递给__init__()方法,也可能不传递给__init__()方法。

如前所述,每个类方法的第一个参数都是一个名为self的类实例。在__init__()方法中,self是指新创建的实例:

class Student:
    name = ""
    def __init__(self):
        self.name = "David"
        studentObj=Student()

在上例中,studentObj实例是正在创建的Student类的实例,其类变量将初始化为David字符串。

甚至参数也可以传递给__init__()方法,如下例所示:

class Student:
    name = ""
    def __init__(self, name):
        self.name = name
        studentObj=Student("David")

在前面的示例中,创建了studentObj实例,并将David字符串传递给它。该字符串将被分配给__init__()方法中定义的name参数,该参数反过来将用于初始化实例的类变量name。记住,__init__()方法不能返回值。

与类变量一样,类的实例可以访问该类的方法,然后是方法名称,中间有一个句点(.。假设Student类中有printName()方法,可以通过studentObj实例访问,语句如下:

studentObj.printName()

在 GUI 中使用类

通过 GUI 从用户接收的数据可以通过使用简单变量直接处理,处理后的数据只能通过变量显示。但是,为了使数据保持结构化格式并获得 OOP 的好处,我们将学习以类的形式保存数据。也就是说,用户通过 GUI 访问的数据可以分配给类变量,通过类方法进行处理和显示。

让我们创建一个应用,它将提示用户输入名称,在输入名称后单击按钮,应用将显示一条 hello 消息以及输入的名称。用户输入的名称将分配给一个类变量,并且还将通过调用该类的 class 方法生成 hello 消息。

怎么做。。。

本配方的重点是了解用户输入的数据如何分配给类变量,以及如何通过类方法访问显示的消息。让我们基于无按钮对话框模板创建一个新的应用,并执行以下步骤:

  1. 将两个标签小部件、一个行编辑小部件和一个按钮小部件拖放到表单上。
  2. 将第一个标签小部件的文本属性设置为Enter your name

Let's not change the text property of the second Label widget and keep its text property to its default value of TextLabel. This is because its text property will be set through code to display the hello message.

  1. 将按钮小部件的文本属性设置为Click
  2. 将行编辑小部件的 objectName 属性设置为lineEditName
  3. 将标签小部件的 objectName 属性设置为labelResponse
  4. 将按钮小部件的 objectName 属性设置为ButtonClickMe
  5. 使用名称LineEditClass.ui保存应用。应用将显示在以下屏幕截图中:

使用 Qt Designer 创建的用户界面存储在一个.ui文件中,该文件是一个 XML 文件,需要转换为 Python 代码。

  1. 要进行转换,需要打开命令提示符窗口,导航到保存文件的文件夹,然后发出以下命令行:
C:\Pythonbook\PyQt5>pyuic5 LineEdit.uiClass -o LineEditClass.py

生成的 Python 脚本LineEditClass.py可以在本书的源代码包中看到。

  1. 将前面的代码视为头文件,并将其导入到将从中调用其用户界面设计的文件中。
  2. 创建另一个名为callLineEditClass.pyw的 Python 文件,并将LineEditClass.py代码导入其中:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from LineEditClass import *
class Student:
    name = ""
    def __init__(self, name):
        self.name = name
    def printName(self):
        return self.name
class MyForm(QDialog):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.ButtonClickMe.clicked.connect(self.dispmessage)
        self.show()
    def dispmessage(self):
        studentObj=Student(self.ui.lineEditName.text())
        self.ui.labelResponse.setText("Hello 
        "+studentObj.printName())
if __name__=="__main__":
    app = QApplication(sys.argv)
    w = MyForm()
    w.show()
    sys.exit(app.exec_())

它是如何工作的。。。

LineEditClass.py文件中,使用Ui_ prepended创建了一个名为顶级对象的类。也就是说,对于顶级对象Dialog,创建了Ui_Dialog类,并存储了我们小部件的接口元素。该类有两种方法,setupUi()retranslateUi()setupUi()方法创建用于在 Qt Designer 中定义用户界面的小部件。此外,小部件的属性在该方法中设置。setupUi()方法只接受一个参数,它是应用的顶级小部件,是QDialog的一个实例。retranslateUi()方法转换接口。

callLineEditClass.py文件中,您可以看到定义了一个名为Student的类。Student类包括一个名为name的类变量和以下两种方法:

  • __init__():取强制self参数和name参数的构造函数,用于初始化name类变量
  • printName:此方法只返回名称类变量中的值

按钮小部件的clicked()事件连接到dispmessage()方法;在 Line Edit 小部件中输入名称后,当用户单击按钮时,dispmessage()方法将被调用。dispmessage()方法通过名称studentObj定义Student类的对象,并将用户在行编辑小部件中输入的名称作为参数传递。因此,将调用Student类的构造函数,并将用户输入的名称传递给构造函数。在行编辑小部件中输入的名称将分配给类变量name。之后,名为labelResponse的标签小部件被设置为显示字符串Hello,并调用Student类的printName方法,该方法返回分配给 name 变量的字符串。

因此,单击按钮时,标签小部件将被设置为显示字符串Hello,后跟用户在行编辑框中输入的名称,如以下屏幕截图所示:

使应用更加复杂

我们还可以在类中使用两个或多个类属性。

假设除了类名Student之外,我们还想向类中添加学生代码。在这种情况下,我们需要向类添加一个属性code,以及一个getCode()方法,该方法将访问指定的学生代码。除了类之外,GUI 也将改变。

我们需要在应用中再添加一个标签小部件和一个行编辑小部件,然后用另一个名称demoStudentClass保存它。添加标签和行编辑小部件后,用户界面将显示如下屏幕截图所示:

用户界面文件demoStudentClass.ui需要转换为 Python 代码。生成的 Python 脚本demoStudentClass.py可以在本书的源代码包中看到。

让我们创建另一个名为callStudentClass.pyw的 Python 文件,并将demoStudentClass.py代码导入其中。callStudentClass.pyw中的代码如下:

import sys
from PyQt5.QtWidgets import QDialog, QApplication
from demoStudentClass import *
class Student:
    name = ""
    code = ""
    def __init__(self, code, name):
        self.code = code
        self.name = name
    def getCode(self):
        return self.code
    def getName(self):
        return self.name
class MyForm(QDialog):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.ButtonClickMe.clicked.connect(self.dispmessage)
        self.show()
    def dispmessage(self):
        studentObj=Student(self.ui.lineEditCode.text(),             
        self.ui.lineEditName.text())
        self.ui.labelResponse.setText("Code: 
        "+studentObj.getCode()+", Name:"+studentObj.getName())
if __name__=="__main__":
    app = QApplication(sys.argv)
    w = MyForm()
    w.show()
    sys.exit(app.exec_())

在前面的代码中,您看到定义了一个名为Student的类。Student类包括两个名为namecode的类变量。Student类除了两个类变量外,还包括以下三种方法:

  • __init__():取强制self参数和两个参数codename的构造函数,用于初始化两个类变量codename
  • getCode():此方法只返回code类变量中的值
  • getName():此方法只返回name类变量中的值

按钮小部件的clicked()事件连接到dispmessage()方法;在 Line Edit 小部件中输入代码和名称后,当用户单击按钮时,dispmessage()方法将被调用。dispmessage()方法通过名称studentObj定义Student类的对象,并将用户在行编辑小部件中输入的代码和名称作为参数传递。将调用Student类的构造函数__init__(),并将用户输入的代码和名称传递给它。输入的代码和名称将分别分配给类变量 code 和 name。之后,名为labelResponse的标签小部件被设置为显示通过Student类的studentObj对象调用getCodegetName两种方法输入的代码和名称。

因此,单击按钮后,标签小部件将在两行编辑小部件中显示用户输入的代码和名称,如以下屏幕截图所示:

遗产

继承是一个概念,通过这个概念,一个现有类的方法和变量可以被另一个类重用,而无需重新编码。也就是说,测试和运行的现有代码可以立即在其他类中重用

继承类型

以下是三种类型的继承:

  • 单继承:一个类继承另一个类
  • 多级继承:一个类继承另一个类,而另一个类又被另一个类继承
  • 多重继承:一个类继承两个或多个类

使用单一继承

单一继承是最简单的继承类型,其中一个类派生自另一个单一类,如下图所示:

B继承类A。这里,类A称为超类或基类,类B称为派生类或子类。

下面的语句定义了Marks类继承Student类的单一继承:

class Marks(Student):

在前面的语句中,Student是基类,Marks是派生类。因此,Marks类的实例可以访问Student类的方法和变量。

准备

为了通过一个正在运行的示例理解单一继承的概念,让我们创建一个应用,该应用将提示用户输入学生的代码、姓名、历史和地理标记,并在单击按钮时显示它们。

用户输入的代码和名称将分配给名为Student的类的类成员。历史和地理分数将分配给另一个名为Marks的班级成员。

为了访问代码和名称,以及历史和地理标记,Marks类将继承Student类。通过继承,Marks类的实例将访问并显示Student类的代码和名称。

怎么做。。。

通过执行以下步骤,启动 Qt Designer 并基于对话框(不带按钮)模板创建新的应用:

  1. 在应用中,将五个标签小部件、四行编辑小部件和一个按钮小部件拖放到表单上。
  2. 将四个标签小部件的文本属性设置为Student CodeStudent NameHistory MarksGeography Marks
  3. 删除第五个标签小部件的文本属性,因为它的文本属性将通过代码设置以显示代码、名称、历史和地理标记。
  4. 将按钮小部件的文本属性设置为Click
  5. 将四行编辑小部件的 objectName 属性设置为lineEditCodelineEditNamelineEditHistoryMarkslineEditGeographyMarks
  6. 将标签小部件的 objectName 属性设置为labelResponse,将按钮小部件的 objectName 属性设置为ButtonClickMe
  7. 使用名称demoSimpleInheritance.ui保存应用。应用将显示如下屏幕截图所示:

用户界面文件demoSimpleInheritance.ui是一个 XML 文件,使用pyuic5实用程序将其转换为 Python 代码。您可以在本书的源代码包中找到生成的 Python 脚本demoSimpleInheritance.py。前面的代码将用作头文件,并将导入另一个 Python 脚本文件,该脚本文件将调用demoSimpleInheritance.py文件中定义的用户界面设计。

  1. 创建另一个名为callSimpleInheritance.pyw的 Python 文件,并将demoSimpleInheritance.py代码导入其中。Python 脚本callSimpleInheritance.pyw中的代码如下所示:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from demoSimpleInheritance import *
class Student:
    name = ""
    code = ""
    def __init__(self, code, name):
        self.code = code
        self.name = name
    def getCode(self):
        return self.code
    def getName(self):
        return self.name
class Marks(Student):
    historyMarks = 0
    geographyMarks = 0
    def __init__(self, code, name, historyMarks, 
    geographyMarks):
        Student.__init__(self,code,name)
        self.historyMarks = historyMarks
        self.geographyMarks = geographyMarks
    def getHistoryMarks(self):
        return self.historyMarks
    def getGeographyMarks(self):
        return self.geographyMarks
class MyForm(QDialog):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.ButtonClickMe.clicked.connect(self.dispmessage)
        self.show()
    def dispmessage(self):
        marksObj=Marks(self.ui.lineEditCode.text(),                           
        self.ui.lineEditName.text(), 
        self.ui.lineEditHistoryMarks.text(),     
        self.ui.lineEditGeographyMarks.text())
        self.ui.labelResponse.setText("Code:     
        "+marksObj.getCode()+", Name:"+marksObj.getName()+"
        nHistory Marks:"+marksObj.getHistoryMarks()+", Geography         
        Marks:"+marksObj.getGeographyMarks())
if __name__=="__main__":
    app = QApplication(sys.argv)
    w = MyForm()
    w.show()
    sys.exit(app.exec_())

它是如何工作的。。。

在这段代码中,您可以看到定义了一个类,称为StudentStudent类包括两个名为namecode的类变量,以及以下三种方法:

  • __init__():取强制self参数和两个参数codename的构造函数,用于初始化两个类变量codename
  • getCode():此方法只返回code类变量中的值
  • getName():此方法只返回name类变量中的值

Marks类继承Student类。因此,Marks类的实例不仅可以访问其自身的成员,还可以访问Student类的成员。

Marks类包括两个名为historyMarksgeographyMarks的类变量,以及以下三种方法:

  • __init__():取强制self参数和四个参数codenamehistoryMarksgeographyMarks的构造函数。在此构造函数中,将调用Student类的构造函数,并将codename参数传递给此构造函数。historyMarksgeographyMarks参数将用于初始化类成员historyMarksgeographyMarks
  • getHistoryMarks():此方法只返回historyMarks类变量中的值。
  • getGeographyMarks():此方法只返回geographyMarks类变量中的值。

按钮的clicked()事件连接到dispmessage()方法。在 Line Edit 窗口小部件中输入代码、名称、历史和地理标记后,当用户单击按钮时,dispmessage()方法将被调用。dispmessage()方法通过名称marksObj定义Marks类的对象,并将用户在 Line Edit 小部件中输入的代码、名称、历史和地理标记作为参数传递。将调用Marks类的构造函数__init__(),并将用户输入的代码、名称、历史记录和地理标记传递给它。从Marks类的构造函数调用Student类的构造函数,并将codename传递给该构造函数。codename参数将分别分配给Student类的codename类变量。

同样,历史和地理标记将分别分配给Marks类的historyMarksgeographyMarks类变量。之后,名为labelResponse的标签小部件被设置为通过marksObj对象调用getCodegetNamegetHistoryMarksgetGeographyMarks四种方法来显示输入的代码、名称、历史和地理标记。由于使用继承,Marks类的marksObj对象获得了访问Student类的getCodegetName方法的权限。

因此,点击按钮后,标签小部件将显示用户通过名为labelResponse的标签小部件输入的代码、名称、历史标记和地理标记,如本屏幕截图所示:

使用多级继承

多级继承是指一个类继承另一个类。继承类依次被第三个类继承,如下图所示:

在上图中,您可以看到类B继承类A和类C,依次继承类B

下面的语句定义了多级继承,Result类继承Marks类,Marks类继承Student类:

class Student:
    class Marks(Student):
        class Result(Marks):

在前面的语句中,Student是基类,Marks类继承Student类。Result类继承Marks类。因此,Result类的实例可以访问Marks类的方法和变量,Marks类的实例可以访问Student类的方法和变量。

准备

为了理解多级继承的概念,让我们创建一个应用,它将提示用户输入学生的代码、姓名、历史分数和地理分数,并在单击按钮时显示总分数和百分比。总分为历史分和地理分之和。假设最高分数为 100,计算百分比的公式为:总分/200*100。

用户输入的代码和名称将分配给名为Student的类的类成员。历史和地理分数将分配给另一个名为Marks的班级成员。

为了访问代码和名称以及历史和地理标记,Marks类将继承Student类。

使用此多级继承,Marks类的实例将访问Student类的代码和名称。为了计算总分和百分比,使用了另一个类,称为Result类。Result类将继承Marks类。因此,Result类的实例可以访问Marks类的类成员以及Student类的类成员。Result类有两个类成员totalMarkspercentagetotalMarks类成员将被分配Marks类的historyMarksgeographyMarks成员之和。百分比成员将被分配根据历史和地理分数获得的百分比。

怎么做。。。

总共有三个类,StudentMarksResult,其中Result类继承Marks类,Marks类依次继承Student类。因此,Result类的成员可以访问Marks类的类成员以及Student类的类成员。以下是创建此应用的分步过程:

  1. 启动 Qt Designer 并基于对话框创建新的应用,而不使用按钮模板。

  2. 将六个标签小部件、六个行编辑小部件和一个按钮小部件拖放到表单上。

  3. 将六个标签小部件的文本属性设置为Student CodeStudent NameHistory MarksGeography MarksTotalPercentage

  4. 将按钮小部件的文本属性设置为Click

  5. 将六行编辑小部件的 objectName 属性设置为lineEditCodelineEditNamelineEditHistoryMarkslineEditGeographyMarkslineEditTotallineEditPercentage

  6. 将按钮小部件的 objectName 属性设置为ButtonClickMe

  7. 通过从属性编辑器窗口取消选中 Enable 属性,禁用lineEditTotallineEditPercentage框。lineEditTotallineEditPercentage小部件被禁用,因为这些框中的值将通过代码分配,我们不希望用户更改它们的值

  8. 使用名称demoMultilevelInheritance.ui保存应用。应用将显示在以下屏幕截图中:

用户界面文件demoMultilevelInheritance.ui是一个 XML 文件,通过使用pyuic5实用程序将其转换为 Python 代码。您可以在本书的源代码包中看到生成的 Python 脚本demoMultilevelInheritance.pydemoMultilevelInheritance.py 文件将用作头文件,并将导入另一个 Python 脚本文件,该文件将使用在demoMultilevelInheritance.py中创建的 GUI。

**9. 创建另一个名为callMultilevelInheritance.pyw的 Python 文件,并将demoMultilevelInheritance.py代码导入其中。Python 脚本callMultilevelInheritance.pyw中的代码如下所示:

import sys
from PyQt5.QtWidgets import QDialog, QApplication
from demoMultilevelInheritance import *
class Student:
    name = ""
    code = ""
    def __init__(self, code, name):
        self.code = code
        self.name = name
    def getCode(self):
        return self.code
    def getName(self):
        return self.name
class Marks(Student):
    historyMarks = 0
    geographyMarks = 0
    def __init__(self, code, name, historyMarks, 
    geographyMarks):
        Student.__init__(self,code,name)
        self.historyMarks = historyMarks
        self.geographyMarks = geographyMarks
    def getHistoryMarks(self):
        return self.historyMarks
    def getGeographyMarks(self):
        return self.geographyMarks
class Result(Marks):
    totalMarks = 0
    percentage = 0
    def __init__(self, code, name, historyMarks, 
    geographyMarks):
        Marks.__init__(self, code, name, historyMarks, 
        geographyMarks)
        self.totalMarks = historyMarks + geographyMarks
        self.percentage = (historyMarks + 
        geographyMarks) / 200 * 100
    def getTotalMarks(self):
        return self.totalMarks
    def getPercentage(self):
        return self.percentage
class MyForm(QDialog):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.ButtonClickMe.clicked.connect(self.dispmessage)
        self.show()
    def dispmessage(self):
        resultObj=Result(self.ui.lineEditCode.text(),                      
        self.ui.lineEditName.text(),           
        int(self.ui.lineEditHistoryMarks.text()),      
        int(self.ui.lineEditGeographyMarks.text()))
        self.ui.lineEditTotal.setText(str(resultObj.
        getTotalMarks()))
        self.ui.lineEditPercentage.setText(str(resultObj.
        getPercentage()))
if __name__=="__main__":
    app = QApplication(sys.argv)
    w = MyForm()
    w.show()
    sys.exit(app.exec_())

它是如何工作的。。。

在前面的代码中,在callMultilevelInheritance.pyw文件中,您可以看到定义了一个名为Student的类。Student类包括两个名为namecode的类变量,以及以下三种方法:

  • __init__():取强制self参数和两个参数codename的构造函数,用于初始化两个类变量codename
  • getCode():此方法只返回code类变量中的值
  • getName():此方法只返回name类变量中的值

Marks类继承Student类。因此,Marks类的实例不仅可以访问其自身的成员,还可以访问Student类的成员。

Marks类包括两个名为historyMarksgeographyMarks的类变量,以及以下三种方法:

  • __init__():取强制self参数和四个参数codenamehistoryMarksgeographyMarks的构造函数。在此构造函数中,将调用Student类的构造函数,并将codename参数传递给此构造函数。historyMarksgeographyMarks参数将用于初始化historyMarksgeographyMarks类成员。
  • getHistoryMarks():此方法只返回historyMarks类变量中的值。
  • getGeographyMarks():此方法只返回geographyMarks类变量中的值。

Result类继承Marks类。Result类的实例不仅可以访问自己的成员,还可以访问Marks类和Student类的成员。

Result类包括两个类变量totalMarkspercentage,以及以下三种方法:

  • __init__():取强制self参数和四个参数codenamehistoryMarksgeographyMarks的构造函数。从该构造函数调用Marks类的构造函数,并将codenamehistoryMarksgeographyMarks参数传递给该构造函数。historyMarksgeographyMarks之和将分配给totalMarks类变量。假设每个的最大分数为 100,历史和地理分数的百分比将被计算并分配给百分比类变量。
  • getTotalMarks():此方法只返回historyMarksgeographyMarks类变量之和。
  • getPercentage():此方法只返回历史和地理分数的百分比。

按钮小部件的clicked()事件连接到dispmessage()方法。在 Line Edit 窗口小部件中输入代码、名称、历史标记和地理标记后,当用户单击按钮时,dispmessage()方法将被调用。dispmessage()方法通过名称resultObj定义Result类的对象,并将用户在 Line Edit 小部件中输入的代码、名称、历史和地理标记作为参数传递。将调用Result类的构造函数__init__(),并将用户输入的代码、名称、历史标记和地理标记传递给它。从Result类的构造函数调用Marks类的构造函数,并将代码、名称、历史标记和地理标记传递给该构造函数。从Marks类的构造函数调用Student类构造函数,并向其传递codename参数。在Student类的构造函数中,codename参数将分别分配给类变量codename。同样,历史和地理标记将分别分配给Marks类的historyMarksgeographyMarks类变量。

historyMarksgeographyMarks之和将分配给totalMarks类变量。此外,历史和地理分数的百分比将被计算并分配给percentage类变量。

之后,通过resultObj调用getTotalMarks方法,将名为lineEditTotal的行编辑小部件设置为显示总分,即历史和地理标记的总和。此外,名为lineEditPercentage的行编辑小部件被设置为通过resultObj调用getPercentage方法来显示标记的百分比。

因此,点击按钮后,名为lineEditTotallineEditPercentage的行编辑小部件将显示用户输入的总分数以及历史和地理分数的百分比,如以下屏幕截图所示:

使用多重继承

多重继承是指一个类继承两个或多个类,如下图所示:

C继承了类A和类B这两个类。

下面的语句定义了多级继承,其中Result类继承Marks类,Marks类依次继承Student类:

class Student:
    class Marks:
        class Result(Student, Marks):

在前面的语句中,StudentMarks是基类,Result类继承Student类和Marks类。因此,Result类的实例可以访问MarksStudent类的方法和变量。

准备

为了实际理解多级继承的概念,让我们创建一个应用,它将提示用户输入学生的代码、姓名、历史分数和地理分数,并在单击按钮时显示总分数和百分比。总分为历史分和地理分之和。假设每个人的最高分数为 100,计算百分比的公式为:总分/200*100。

用户输入的代码和名称将分配给名为Student的类的类成员。历史和地理分数将分配给另一个名为Marks的班级成员。

为了访问代码和名称,以及历史和地理标记,Result类将继承两个类,Student类和Marks类。通过这种多重继承,Result类的实例可以访问Student类的代码和名称,以及Marks类的historyMarksgeographyMarks类变量。换句话说,Result类的实例通过多重继承可以访问Marks类的类成员,也可以访问Student类的类成员。Result类有两个类成员,totalMarkspercentagetotalMarks类成员将被分配Marks类的historyMarksgeographyMarks成员之和。百分比成员将被分配根据历史和地理分数获得的百分比。

怎么做。。。

让我们通过一个循序渐进的过程来了解如何将多级继承应用于三个类:StudentMarksResultResult类将继承StudentMarks两个类。这些步骤解释了Result类的成员如何通过多级继承访问StudentMarks类的类成员:

  1. 启动 Qt Designer 并基于对话框创建新的应用,而不使用按钮模板。

  2. 在应用中,将六个标签小部件、六个行编辑小部件和一个按钮小部件拖放到表单上。

  3. 将六个标签小部件的文本属性设置为Student CodeStudent NameHistory MarksGeography MarksTotalPercentage

  4. 将按钮小部件的文本属性设置为Click

  5. 将六行编辑小部件的 objectName 属性设置为lineEditCodelineEditNamelineEditHistoryMarkslineEditGeographyMarkslineEditTotallineEditPercentage

  6. 将按钮小部件的 objectName 属性设置为ButtonClickMe

  7. 通过从属性编辑器窗口取消选中 Enable 属性,禁用lineEditTotallineEditPercentage框。lineEditTotallineEditPercentage框被禁用,因为这些框中的值将通过代码分配,我们不希望用户更改它们的值。

  8. 使用名称demoMultipleInheritance.ui保存应用。应用将显示在以下屏幕截图中:

用户界面文件demoMultipleInheritance .ui是一个 XML 文件,使用pyuic5实用程序将其转换为 Python 代码。您可以在本书的源代码包中找到生成的 Python 代码demoMultipleInheritance.pydemoMultipleInheritance.py文件将用作头文件,并将导入另一个 Python 脚本文件,该脚本文件将调用在demoMultipleInheritance.py文件中创建的 GUI。

  1. 创建另一个名为callMultipleInheritance.pyw的 Python 文件,并将demoMultipleInheritance.py代码导入其中:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from demoMultipleInheritance import *
class Student:
    name = ""
    code = ""
    def __init__(self, code, name):
        self.code = code
        self.name = name
    def getCode(self):
        return self.code
    def getName(self):
        return self.name
class Marks:
    historyMarks = 0
    geographyMarks = 0
    def __init__(self, historyMarks, geographyMarks):
        self.historyMarks = historyMarks
        self.geographyMarks = geographyMarks
    def getHistoryMarks(self):
        return self.historyMarks
    def getGeographyMarks(self):
        return self.geographyMarks
class Result(Student, Marks):
    totalMarks = 0
    percentage = 0
    def __init__(self, code, name, historyMarks, 
    geographyMarks):
        Student.__init__(self, code, name)
        Marks.__init__(self, historyMarks, geographyMarks)
        self.totalMarks = historyMarks + geographyMarks
        self.percentage = (historyMarks + 
        geographyMarks) / 200 * 100
    def getTotalMarks(self):
        return self.totalMarks
    def getPercentage(self):
        return self.percentage
class MyForm(QDialog):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.ButtonClickMe.clicked.connect(self.dispmessage)
        self.show()
    def dispmessage(self):
        resultObj=Result(self.ui.lineEditCode.text(),         
        self.ui.lineEditName.text(),
        int(self.ui.lineEditHistoryMarks.text()),  
        int(self.ui.lineEditGeographyMarks.text()))
        self.ui.lineEditTotal.setText(str(resultObj.
        getTotalMarks()))
        self.ui.lineEditPercentage.setText(str(resultObj.
        getPercentage()))
if __name__=="__main__":
    app = QApplication(sys.argv)
    w = MyForm()
    w.show()
    sys.exit(app.exec_())

它是如何工作的。。。

在这段代码中,您可以看到定义了一个名为Student的类。Student类包括两个名为namecode的类变量,以及以下三种方法:

  • __init__():取强制self参数和两个参数codename的构造函数,用于初始化两个类变量codename
  • getCode():此方法只返回code类变量中的值
  • getName():此方法只返回name类变量中的值

Marks类包括两个类变量historyMarksgeographyMarks,以及以下三种方法:

  • __init__():取强制self参数和两个参数historyMarksgeographyMarks的构造函数。historyMarksgeographyMarks参数将用于初始化historyMarksgeographyMarks类成员。
  • getHistoryMarks():此方法只返回historyMarks类变量中的值。
  • getGeographyMarks():此方法只返回geographyMarks类变量中的值。

Result类继承Student类和Marks类。Result类的实例不仅可以访问自己的成员,还可以访问Marks类和Student类的成员。

Result类包括两个名为totalMarkspercentage的类变量,以及以下三种方法:

  • __init__():取强制self参数和四个参数codenamehistoryMarksgeographyMarks的构造函数。从该构造函数调用Student类构造函数,并将codename参数传递给该构造函数。另外,从这个构造函数中,Marks类构造函数将被调用,historyMarksgeographyMarks参数将被传递给该构造函数。historyMarksgeographyMarks之和将分配给totalMarks类变量。假设每项的最大分数为 100,历史和地理分数的百分比将被计算并分配给percentage类变量。
  • getTotalMarks():此方法只返回historyMarksgeography类变量之和。
  • getPercentage():此方法只返回历史和地理分数的百分比。

按钮小部件的clicked()事件连接到dispmessage()方法。在 Line Edit 窗口小部件中输入代码、名称、历史标记和地理标记后,当用户单击按钮时,dispmessage()方法将被调用。dispmessage()方法通过名称resultObj定义Result类的对象,并将用户在行编辑小部件中输入的代码、名称、历史标记和地理标记作为参数传递。将调用Result类的构造函数__init__(),并将用户输入的代码、名称、历史标记和地理标记传递给它。从Result类的构造函数调用Student类构造函数和Marks类构造函数。代码和名称将传递给Student类构造函数,历史和地理标记将传递给Marks类构造函数。

Student类构造函数中,代码和名称将分别分配给codename类变量。类似地,在Marks类构造函数中,历史和地理标记将分别分配给Marks类的historyMarksgeographyMarks类变量。

historyMarksgeographyMarks之和将分配给totalMarks类变量。此外,历史和地理分数的百分比将被计算并分配给percentage类变量。

之后,通过resultObj调用getTotalMarks方法,将名为lineEditTotal的行编辑小部件设置为显示总分,即历史和地理标记的总和。此外,名为lineEditPercentage的行编辑小部件被设置为通过resultObj调用getPercentage方法来显示标记的百分比。

因此,点击按钮后,名为lineEditTotallineEditPercentage的行编辑小部件将显示用户输入的历史和地理标记的总分数和百分比,如以下屏幕截图所示:

**