Python数据结构与算法

Python数据结构与算法-------基础篇(类与继承)

1 导论

1.2 Python基础

1.2.6 Python面向对象编程:定义类

​ Python是一门面向对象的编程语言,面向对象的编程语言最强大的一项特性是允许程序员创建全新的类来对求解问题所需的数据进行建模,每当用户需要实现抽象数据类型时,就可以创建全新的类。

1.2.6.1 Fraction类

​ 展示如何实现用户定义的类,一个常用的例子就是构建实现抽象数据类型Fraction的类,构建出的Fraction类的主要功能是创建出看起来很像分数的数据对象,并且可以针对分数进行加减乘除等操作,最后的结果也必须是最简分数,以下从Fraction类的构建过程来讲解Python类的构建过程。

—定义类:Python中定义类的方法是,提供一个类名以及一整套与函数定义语法类似的方法定义,一个方法定义框架如下:

class Fraction#方法定义

​ Python中所有的类应该首先提供的是构造方法,构造方法定义了数据对象的创建方式,在本例中,要创建一个Fraction对象需要提供分子和分母两个数据,构造方法总是命名为_init_,示例代码如下:

class Fraction:
        
        def __init__(self, top, bottom):
                self.num = top
                self.den = bottom

​ 需要注意的是,形参有三项,self是一个总是指向对象本身的特殊参数,它必须是第一个参数,在调用方法时,不需要给其提供实参,即如果提供了两个参数,则其默认分配给后面两个形参,而不会分配给self。同时,在构造方法中将后两个形参分配给了两个内部数据对象,作为状态的一部分,方便数据在各个方法之间流通。

—创建Fraction类的实例

​ 使用类名,传入状态的实际值,调用构造方法,就可完成创建,不需要直接调用构造方法,传入状态实际值构造方法自动被调用,示例代码如下:

myfraction = Fraction(3, 5)

在这里插入图片描述

—打印Fraction实例对象

​ 直接打印Fraction实例对象,输出的结果是实例对象本身的地址,而非我们想要的结果,示例代码如下:

class Fraction:
        
        def __init__(self, top, bottom):
                self.num = top
                self.den = bottom
                
                
myfraction = Fraction(3, 5)
print(myfraction)

输出:<__main__.Fraction object at 0x000001E9714DCA30>

​ Python要求对象将自己转换成一个可以被写到输出段的字符串,有两种方法可以实现这一目的。

—method 1:show方法

​ 定义一个show方法,使得调用该方法Fraction对象能够将自己作为字符串打印出来,示例代码如下:

class Fraction:
        
        def __init__(self, top, bottom):
                self.num = top
                self.den = bottom
                
        def show(self):
            print(self.num, "/", self.den)
myfraction = Fraction(3, 5)
myfraction.show()
print(myfraction)

输出:3 / 5
	 <__main__.Fraction object at 0x000002784E754850>

—method 2:重写默认实现

​ Python为所有的类都提供了一套标准的方法,但是这些方法可能没有正常工作,其中之一就是将对象转换为字符串的方法_str_,我们只需要重写该方法的实现,就可以实现将对象转换为字符串输出,示例代码如下:

class Fraction:
        
        def __init__(self, top, bottom):
                self.num = top
                self.den = bottom
                
        def __str__(self) -> str:
                return str(self.num) + "/" +str(self.den)
            
myfraction = Fraction(3, 5)
print(myfraction)

输出:3/5

—Fraction对象相加

​ 我们可以重写Fraction类中的许多方法来实现我们需要的功能,未重写方法前,如果试图将两个对象相加,示例代码和输出结果如下:

class Fraction:
        
        def __init__(self, top, bottom):
                self.num = top
                self.den = bottom
                
        def __str__(self) -> str:
                return str(self.num) + "/" +str(self.den)
            
f1 = Fraction(1, 4)
f2 = Fraction(1, 2)
f3 = f1 + f2

输出:Traceback (most recent call last):
 	   File "c:\Users\test4.py", line 12, in <module>
    	 f3 = f1 + f2
	 TypeError: unsupported operand type(s) for +: 'Fraction' and 'Fraction

​ 在上述代码中,我们发现加号无法处理Fraction对象,可以重写Fraction类的__add__方法来修正这个错误,在此,两个分数相加的公式为:
a b + c d = a d b d + c b b d = a d + c b b d \frac{a}{b} +\frac{c}{d}=\frac{ad}{bd}+\frac{cb}{bd}=\frac{ad+cb}{bd} ba+dc=bdad+bdcb=bdad+cb
​ 根据此公式,重写的__add__方法示例代码如下:

class Fraction:
        
        def __init__(self, top, bottom):
                self.num = top
                self.den = bottom
                
        def __str__(self) -> str:
                return str(self.num) + "/" +str(self.den)
        
        def __add__(self, otherfraction):
                
                newnum = self.num*otherfraction.den + self.den*otherfraction.num
                newden = self.den*otherfraction.den
                return Fraction(newnum, newden)                       
            

f1 = Fraction(1, 4)
f2 = Fraction(1, 2)
f3 = f1 + f2
print(f3)

输出:6/8

—化最简分数

​ 上述代码实现了Fraction对象的加法运算,但是仍有瑕疵,最后得到的结果不是最简分数,需要一个辅助算法将结果化为最简分数。只需将分子分母除以其最大公因数即可。寻找最大公因数最著名的算法就是欧几里得算法,欧几里得算法指出,对于整数m和n,如果m能被n整除,那么他们的最大公因数就是n,否则它们的最大公因数是n与m除以n的余数的最大公因数。基于上述陈述,完善后的示例代码如下:

class Fraction:
        
        def __init__(self, top, bottom):
                self.num = top
                self.den = bottom
                
        def __str__(self) -> str:
                return str(self.num) + "/" +str(self.den)
        
        def __add__(self, otherfraction):
                def gcd(m, n):
                        while m%n != 0:
                                oldm = m
                                oldn = n
                                
                                m = oldn
                                n = oldm%oldn
                        return n 
                
                newnum = self.den*otherfraction.num + self.num*otherfraction.den
                newden = self.den*otherfraction.den
                common = gcd(newnum, newden)
                return Fraction(newnum//common, newden//common)                     
            

f1 = Fraction(1, 4)
f2 = Fraction(1, 2)
f3 = f1 + f2
print(f3)

输出:3/4

—浅相等和深相等

​ 假设有两个Fraction对象,f1和f2,只有在它们是同一个对象引用时,f1 == f2才为True,这被称为浅相等,根据值来判断相等可以建立深相等。

在这里插入图片描述

1.2.6.2 继承:以逻辑门电路为例

​ 继承是使一个类与另一个类相联系,Python中的子类可以从父类继承特征数据和行为,父类也称为超类。例如Python集合类中的相互联系,列表是有序集合的子,因此我们可以将有序集合称为父,列表称为有序集合的子,这种关系称为IS-A关系。列表从有序集合继承了重要特征,也就是内部数据的顺序以及诸如拼接、重复和索引等方法。

在这里插入图片描述

​ 子类继承了父类的共同特征,但是子类之间通过额外的特征来区分彼此。通过将类组合成继承层次,能够大大的提高代码的复用率,使以前编写的代码可以更好的应用到新的场景中,同时使系统之间的结构层次更加清晰。

—以逻辑门电路为例

​ 逻辑门电路的基本逻辑单元分别为与门、或门和非门,通过不同的模式将这些逻辑门组合起来并且提供一系列的值,可以构建出具有逻辑功能的电路,下图是逻辑门电路的继承层次结构:

在这里插入图片描述

—LogicGate类

​ LogicGate类代表的是逻辑门的通用特性,其主要包含的是逻辑门的标签和一个输出,两个特性也是三个基本逻辑门所共有的。其代码实现如下所示:

class LogicGate:
    
    def __init__(self, n) -> None:
        self.label = n
        self.output = None
        
    def getLabel(self):
        return self.label
    
    def getOutput(self):
        self.output = self.performGateLogic()
        return self.output

​ 在上述构建的超类中,暂时还不用实现performGateLogic函数,在后续继承层次结构中的每一个逻辑门中来实现该函数,这也是面向对象编程中一个非常强大的思想——我们创建了一个方法,但是该方法的代码还不存在。

​ 接下来是依据逻辑门的输入个数来对逻辑门进一步分类,整理出每一类的共有特性。

—BinaryGate和UnaryGate类

​ BinaryGate和UnaryGate是LogicGate的一个子类,BinaryGate有两个输入,与门与或门就属于这一类,而UnaryGate有一个输入,非门属于这一类,这两个类的代码实现如下所示:

class BinaryGate(LogicGate):
    
    def __init__(self, n) -> None:
        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() +"-->"))
class UnaryGate(LogicGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
        self.pin = None
        
    def getPin(self):
        return int(input("Enter Pin input for gate" + self.getLabel() +"-->"))

​ 在这两个构造方法中首先使用super函数来调用其父类LogicGate的构造方法,初始化从父类LogicGate中继承的数据项(输出和标签),接着构造方法添加输入特性,子类的构造方法需要先调用父类的构造方法,然后再初始化自己独有的数据项。

—AndGate类

​ 与门有两个输入,因此AndGate类是BinaryGate的子类,以下代码是AndGate类的具体代码实现:

class AndGate(BinaryGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
    def performGateLogic(self):
        
        a = self.getPinA()
        b = self.getPinB()
        if a == 1 and b == 1:
            return 1
        else:
            return 0

​ 或门与非门也用同样的方式创建,示例代码如下所示:

class OrGate(BinaryGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
    def performGateLogic(self):
        
        a = self.getPinA
        b = self.getPinB
        if a == 0 and b == 0:
            return 0
        else:
            return 1 
class NotGate(UnaryGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
    def performGateLogic(self):
        
        a = self.getPin
        if a == 0:
            return 1
        else:
            return 0

​ 在构建与、或、非子类时,不需要添加任何新的数据,其所需要的数据均从父类继承而来。

​ 是与门整体代码的验证输出如下列代码所示:

class LogicGate:
    
    def __init__(self, n) -> None:
        self.label = n
        self.output = None
        
    def getLabel(self):
        return self.label
    
    def getOutput(self):
        self.output = self.performGateLogic()
        return self.output
    
class BinaryGate(LogicGate):
    
    def __init__(self, n) -> None:
        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() +"-->"))
    
    
class UnaryGate(LogicGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
        self.pin = None
        
    def getPin(self):
        return int(input("Enter Pin input for gate" + self.getLabel() +"-->"))


class AndGate(BinaryGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
    def performGateLogic(self):
        
        a = self.getPinA()
        b = self.getPinB()
        if a == 1 and b == 1:
            return 1
        else:
            return 0
                    
g1 = AndGate("G1")
print(g1.getOutput())

输出:Enter Pin A input for gateG1-->1
	 Enter Pin B input for gateG1-->0
	 0

—连接器Connector类

​ 在基本逻辑门构建完成之后,接下来就是使用基本逻辑门搭建逻辑门电路,搭建逻辑门电路除了基本逻辑门之外,还需要一个连接器的类,主要功能是将上一个逻辑门的输出连入下一逻辑门的输入,实现逻辑门之间的连接。Connector类并不在逻辑门的继承层次结构中,但是它会使用该结构,从而使得每一个连接器两端都有一个逻辑门,这被称为HAS-A关系。Connector 与 LogicGate 是 HAS-A 关系,连接器内部包含LogicGate实例。示例代码如下所示:

 class Connector:
     
     def __init__(self, fgate, tgate) -> None:
         
         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.getPinB = source
            else:
                raise RuntimeError("Error: NO EMPTY PINS")

—逻辑门电路验证

​ 两个与门( g1 和 g2)的输出与或门( g3)的输入相连接,或门的输出又与非门( g4)的输入相连接。非门的输出就是整个电路的输出 ,电路图和完整代码如下所示:

在这里插入图片描述

class LogicGate:
    
    def __init__(self, n) -> None:
        self.label = n
        self.output = None
        
    def getLabel(self):
        return self.label
    
    def getOutput(self):
        self.output = self.performGateLogic()
        return self.output
    

    
class BinaryGate(LogicGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
        self.pinA = None
        self.pinB = None
        
    def getPinA(self):
        if self.pinA == None:
            return int(input("Enter Pin A input for gate" + self.getLabel() + "-->"))
        else:
            return self.pinA.getFrom().getOutput()
    
    def getPinB(self):
        if self.pinB == None:
            return int(input("Enter Pin B input for gate" + self.getLabel() +"-->"))
        else:
            return self.pinB.getFrom().getOutput()
    
    def setNextPin(self, source):
        if self.pinA == None:
            self.pinA = source
        else:
            if self.pinB == None:
                self.getPinB = source
            else:
                raise RuntimeError("Error: NO EMPTY PINS")
    
    
class UnaryGate(LogicGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
        self.pin = None
        
    def getPin(self):
        if self.pin == None:
            return int(input("Enter Pin input for gate" + self.getLabel() +"-->"))
        else:
            return self.pin.getFrom().getOutput()
    
    def setNextPin(self, source):
        if self.pin == None:
            self.pin = source
        else:
            raise RuntimeError("Error: NO EMPTY PINS")


class AndGate(BinaryGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
    def performGateLogic(self):
        
        a = self.getPinA()
        b = self.getPinB()
        if a == 1 and b == 1:
            return 1
        else:
            return 0

class OrGate(BinaryGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
    def performGateLogic(self):
        
        a = self.getPinA
        b = self.getPinB
        if a == 0 and b == 0:
            return 0
        else:
            return 1    
        
class NotGate(UnaryGate):
    
    def __init__(self, n) -> None:
        super().__init__(n)
        
    def performGateLogic(self):
        
        a = self.getPin
        if a == 0:
            return 1
        else:
            return 0
 
class Connector:
    def __init__(self, fgate, tgate) -> None:
         
         self.fromgate = fgate
         self.togate = tgate
         
         tgate.setNextPin(self)
         
    def getFrom(self):
        return self.fromgate
    
    def getTo(self):
        return self.togate
    
                    
g1 = AndGate("G1")
g2 = AndGate("G2")
g3 = OrGate("G3")
g4 = NotGate("G4")
c1 = Connector(g1, g3)
c2 = Connector(g2, g3)
c3 = Connector(g3, g4)
print(g4.getOutput())

输出:Enter Pin A input for gateG1-->0
	 Enter Pin B input for gateG1-->1
	 Enter Pin A input for gateG2-->1
	 Enter Pin B input for gateG2-->1
	 0
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thefuture9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值