例:逻辑门与电路

子类从父类继承共同的特征,但是通过额外的特征彼此区分。为了进一步探索这个概念,我们来构建一个模拟程序,用于模拟数字电路。逻辑门是这个模拟程序的基本构造单元,它们代表其输入和输出之间的布尔代数关系。一般来说,逻辑门都有单一的输出。输出值取决于提供的输入值。

  • 与门( AND gate):有两个输入,每一个都是 0 或 1(分别代表 False 和 True)。如果两个输入都是 1,那么输出就是 1;如果至少有一个输入是 0,那么输出就是 0。

  • 或门( OR gate):同样也有两个输入。当至少有一个输入为 1 时,输出就为 1;当两个输入都是 0 时,输出是 0。

  • 非门( NOT gate):与其他两种逻辑门不同,它只有一个输入。输出刚好与输入相反。如果输入是 0,输出就是 1。反之,如果输入是 1,输出就是 0。

为了实现电路,首先将逻辑门组织成类的继承层次结构。

顶部的 LogicGate 类代表逻辑门的通用特性:逻辑门的标签和一个输出。下面一层子类将逻辑门分成两种:有一个输入的逻辑门和有两个输入的逻辑门。再往下,就是具体的逻辑门。

超类 LogicGate

每一个逻辑门都有一个用于识别的标签以及一个输出。此外,还需要一些方法,以便用户获取逻辑门的标签。

所有逻辑门还需要能够知道自己的输出值。这就要求逻辑门能够根据当前的输入值进行合理的逻辑运算。为了生成结果,逻辑门需要知道自己对应的逻辑运算是什么。这意味着需要调用一个方法来进行逻辑运算。

class LogicGate:

    def __init__(self, n):
        self.label = n
        self.output = None

    def getLabel(self):
        return self.label

    def getOutput(self):
        self.output = self.performGateLogic()
        return self.output

参数 self 是指向实际调用方法的逻辑门对象的引用。任何添加到继承层次结构中的新逻辑门都仅需要实现之后会被调用的 performGateLogic 函数。一旦实现完成,逻辑门就可以提供运算结果。

子类 BinaryGate 和 UnaryGate

我们依据输入的个数来为逻辑门分类。与门和或门有两个输入,非门只有一个输入。BinaryGateLogicGate 的一个子类,并且有两个输入。 UnaryGate 同样是 LogicGate 的子类,但是仅有一个输入。

两个类中的构造方法首先使用 super 函数来调用其父类的构造方法。

当创建 BinaryGate 类的实例时,首先要初始化所有从 LogicGate 中继承来的数据项,在这里就是逻辑门的标签。接着,构造方法添加两个输入( pinA 和 pinB)。这是在构建类继承层次结构时常用的模式。子类的构造方法需要先调用父类的构造方法,然后再初始化自己独有的数据。

class BinaryGate(LogicGate):

    def __init__(self, n):
        super().__init__(n)

        self.pinA = None
        self.pinB = None

    def getPinA(self):
        return int(input("Enter Pin A input for gate " + \
            self.getLabel() + "-->"))

    def getPinB(self):
        return int(input("Enter Pin B input for gate " + \
            self.getLabel() + "-->"))

BinaryGate 类增添的唯一行为就是取得两个输入值。由于这些值来自于外部,因此通过一条输入语句来要求用户提供。UnaryGate 类也有类似的实现,不过它只有一个输入。

class UnaryGate(LogicGate):

    def __init__(self, n):
        super().__init__(n)

        self.pin = None

    def getPin(self):
        return int(input("Enter Pin input for gate " + \
            self.getLabel() + "-->"))

子类 AndGate

有了不同输入个数的逻辑门所对应的通用类之后,就可以为有独特行为的逻辑门构建类。例如,由于与门需要两个输入,因此 AndGateBinaryGate的子类。

和之前一样,构造方法的第一行调用父类(BinaryGate)的构造方法,该构造方法又会调用它的父类(LogicGate)的构造方法。注意,由于继承了两个输入、一个输出和逻辑门标签,因此 AndGate 类并没有添加任何新的数据。

AndGate 类唯一需要添加的是布尔运算行为。这就是提供 performGateLogic 的地方。对于与门来说, performGateLogic 首先需要获取两个输入值,然后只有在它们都为 1 时返回 1。

class AndGate(BinaryGate):

    def __init__(self, n):
        super().__init__(n)

    def performGateLogic(self):

        a = self.getPinA()
        b = self.getPinB()
        if a==1 and b==1:
            return 1
        else:
            return 0

或门和非门都能以相同的方式来构建。 OrGate 也是 BinaryGate 的子类, NotGate 则会继承 UnaryGate 类。由于计算逻辑不同,这两个类都需要提供自己的 performGateLogic 函数。

实例:

>>> g1 = AndGate("G1")
>>> g1.getOutput()
Enter Pin A input for gate G1-->1
Enter Pin B input for gate G1-->0
0
>>> g2 = OrGate("G2")
>>> g2.getOutput()
Enter Pin A input for gate G2-->1
Enter Pin B input for gate G2-->1
1
>>> g2.getOutput()
Enter Pin A input for gate G2-->0
Enter Pin B input for gate G2-->0
0
>>> g3 = NotGate("G3")
>>> g3.getOutput()
Enter Pin input for gate G3-->0
1

Connector 类

有了基本的逻辑门之后,便可以开始构建电路。为此,需要将逻辑门连接在一起,前一个的输出是后一个的输入。为了做到这一点,我们要实现一个叫作 Connector 的新类。

Connector 类并不在逻辑门的继承层次结构中。但是,它会使用该结构,从而使每一个连接器的两端都有一个逻辑门。这意味着连接器内部包含 LogicGate 类的实例,但是不在继承层次结构中。

class Connector:

    def __init__(self, fgate, tgate):
        self.fromgate = fgate
        self.togate = tgate

        tgate.setNextPin(self)

    def getFrom(self):
        return self.fromgate

    def getTo(self):
        return self.togate

setNextPin 方法:

def setNextPin(self, source):
    if self.pinA == None:
        self.pinA = source
    else:
        if self.pinB == None:
            self.pinB = source
        else:
            raise RuntimeError("Error: NO EMPTY PINS")

每一个连接器对象都包含 fromgatetogate 两个逻辑门实例,数据值会从一个逻辑门的输出“流向”下一个逻辑门的输入。对 setNextPin 的调用对于建立连接来说非常重要。需要将这个方法添加到逻辑门类中,以使每一个 togate 能够选择适当的输入。

BinaryGate 类中,逻辑门有两个输入,但连接器必须只连接其中一个。如果两个都能连接,那么默认选择 pinA。如果 pinA 已经有了连接,就选择 pinB。如果两个输入都已有连接,则无法连接逻辑门。

现在的输入来源有两个:外部以及上一个逻辑门的输出,这需要对方法 getPinAgetPinB 进行修改。

如果输入没有与任何逻辑门相连接(None),那就和之前一样要求用户输入。如果有了连接,就访问该连接并且获取 fromgate 的输出值。这会触发 fromgate 处理其逻辑。该过程会一直持续,直到获取所有输入并且最终的输出值成为正在查询的逻辑门的输入。在某种意义上,这个电路反向工作,以获得所需的输入,再计算最后的结果。

修改后的 getPinA 方法:

def getPinA(self):
    if self.pinA == None:
        return input("Enter Pin A input for gate " + \
            self.getLabel() + "-->")
    else:
        return self.pinA.getFrom().getOutput()

实例:

>>> g1 = AndGate("G1")
>>> g2 = AndGate("G2")
>>> g3 = OrGate("G3")
>>> g4 = NotGate("G4")
>>> c1 = Connector(g1, g3)
>>> c2 = Connector(g2, g3)
>>> c3 = Connector(g3, g4)

两个与门( g1 和 g2)的输出与或门( g3)的输入相连接,或门的输出又与非门( g4)的输入相连接。非门的输出就是整个电路的输出。

>>> g4.getOutput()
Pin A input for gate G1-->0
Pin B input for gate G1-->1
Pin A input for gate G2-->1
Pin B input for gate G2-->1
0
>>>
文章目录