Python面向对象编程(五)

一、继承父类属性和方法

1. 继承

       面向对象的编程带来的好处之一是代码的重用,实现这种重用方法之一是通过继承机制。继承(Inheritance)是两个类或多个类之间的父子关系,子类继承了父类的所有公有数据属性和方法,并且可以通过编写子类的代码扩充子类的功能。想象一下,如果人类可以做到子女继承父母的所有才学并加以扩展,那么人类的发展至少是现在的数万倍。继承实现了数据属性和方法的重用,减少了代码的冗余度。
      在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承自狗。
 
       特定狗种类继承狗类,狗类继承动物类,狗类编写了描述所有狗种共有的行为和方法,而特定狗种类则增加了该狗种特有的行为。不过继承也有一定弊端,可能父类对于子类也有一定特殊的地方,如某种特定狗种不具有绝大部分狗种的行为,当程序员没有理清类间的关系时,可能使得子类具有了不该有的方法。另外,如果继承链太长的话,任何一点小的变化都会引起一连串变化,使用的继承要注意控制继承链的规模。
      在Python中继承有以下一些特点。
      (1) 在继承中基类初始化方法__init__不会被自动调用。如果希望子类调用基类的__init__方法,需要在子类的__init__方法显示调用它。这与C++和C#区别很大。
      (2) 在调用基类的方法时,需要加上基类的类名前缀,且带上self参数变量。注意在类中调用在该类定义的方法是不需要self参数的。
      (3) Python总是首先查找对应类的方法,如果在子类没有对应的方法,Python才会在继承链的基类中按顺序查找。
      (4) 在Python继承中,子类不能访问基类的私有成员。
      最后一次修改类Cat的代码。
>>>class Cat():
...    def __init__(self):
...        self.name = '猫'
...        self.age = 4
...        self.info = [self.name,self.age]
...        self.index = -1
...    def run(self):
...        print( self.name,'--在跑')
...    def getName(self):
...        return self.name
...    def getAge(self):
...        return self.age
...    def __iter__(self):
...        print('名字 年龄')
...        return self
...    def next(self):
...        if self.index == len(self.info)-1:
...            raise StopIteration
...        self.index += 1
...        return self.info[self.index]
>>>class Bosi(Cat):
...   def setName(self, newName):
...        self.name = newName
...   def eat(self):
...        print( self.name,'--在吃')
...        
>>>bs = Bosi()                              # 创建对象
>>>print( 'bs的名字为:',bs.name)               # 继承父类的属性和方法
bs的名字为: 猫
>>>print( 'bs的年龄为:',bs.age)
bs的年龄为: 4
>>>print(bs.run())
猫 --在跑
>>>bs.setName('波斯猫')                      # 子类的属性和方法
>>>bs.eat()
波斯猫 --在吃
>>>iterator = iter(bs.next,1)                    # 迭代输出父类的属性
>>>for info in iterator:
...    print(info)

4
       定义了Bosi类的父类Cat,将猫共有的属性和方法都放到父类中,子类仅仅需要向父类传输数据属性即可。这样做可以很轻松地定义其他基于Cat类的子类。因为假如有数百只猫,使用继承的方法大大减少了代码量,且当需要对全部猫整体修改时仅需要修改Cat类即可。Bosi类的__init__函数中显示调用了Cat类的__init__函数,并向父类传输数据,这里注意要加self参数。
在继承里面不能继承父类的私有属性,所以也不用担心父类和子类出现因继承造成的重名情况。为了能更清晰地讲述这个问题,这里再举一个例子。
>>>class animal():
...    def __init__(self,age):
...        self.__age = age
...    def print2(self):
...        print(self.__age)
>>>class dog(animal):
...    def __init__(self,age):
...        animal.__init__(self,age)
...    def print2(self):
...        print(self.__age)

>>>a_animal = animal(10)
>>>a_animal.print2()
10
>>>a_dog = dog(10)
>>>a_dog.print2()                                 # 程序报错
AttributeError: 'dog' object has no attribute '_dog__age'

2. 多继承

      如果有多个父类,则父类名需要全部都写在括号里,这种情况称为多继承,格式为Class子类名(父类名1,父类名2,•••)。
>>>class A(object):                                 # 定义一个父类
...    def __init__(self):
...        print ("   ->Input A")
...        print ("   <-Output A")
>>>class B(A):                                   # 定义一个子类
...    def __init__(self):
...        print ("  -->Input B")
...        A.__init__(self)
...        print ("  <--Output B")
>>>class C(A):                                  # 定义另一个子类
...    def __init__(self):
...        print (" --->Input C")
...        A.__init__(self)
...        print (" <---Output C")
>>>class D(B, C):                                # 定义一个子类
...    def __init__(self):
...        print ("---->Input D")
...        B.__init__(self)
...        C.__init__(self)
...        print ("<----Output D")

>>>d = D()                   # Python中是可以多继承的,父类中的方法、属性,子类会继承。
---->Input D
  -->Input B
   ->Input A
   <-Output A
  <--Output B
 --->Input C
   ->Input A
   <-Output A
 <---Output C
<----Output D
>>>issubclass(C,B)            # 判断一个类是不是另一个类的子孙类
False
>>>issubclass(C,A)
True
       实现继承之后,子类将继承父类的属性,也可以使用内建函数issubclass函数来判断一个类是不是另一个类的子孙类,前项参数为子类,后项参数为父类。

二、掌握其他方法

       面向对象的三大特性是指重载、封装和多态。

1. 重载

       所谓重载,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。
>>>class Cat:
...    def sayHello(self):
...        print("喵-----1")
>>>class Bosi(Cat):
...    def sayHello(self):
...        print("喵喵----2")

>>>bosi = Bosi()
>>>bosi.sayHello()                  # 子类中的方法会覆盖掉父类中同名的方法
喵喵----2

2. 封装

       既然Cat实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Cat类的内部定义访问数据的函数。这样,就把数据给“封装”起来了。
       封装(Encapsulation)就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类);封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是通过外部接口,特定的访问权限来使用类即可。简而言之,就是将内容封装到某个地方,以后再去调用被封装在某处的内容。

3. 多态

       多态性(Polymorphism)是对象通过他们共同的属性和动作来操作及访问,允许将父对象设置成和一个或多个它的子对象相等的技术,比如Car=Land_Rover。多态性使得能够利用同一类(父类)类型的指针来引用不同类的对象,以及根据所引用对象的不同,以不同的方式执行相同的操作。

      Python是动态语言,可以调用实例方法,不检查类型,只要方法存在,参数正确就可以调用,这就是与静态语言(例如Java)最大的差别之一。表明了动态(运行时)绑定的存在,允许重载或运行时类型确定和验证。

三、练习

       在创建的Car类上产生子类Land_Rover,要使子类Land_Rover拥有2个父类属性(品牌、颜色)和两个自带属性(车轮数、废气涡轮增压),然后输出子类属性。

参考代码:

class Car():
    def __init__(self,brand, newWheelNum, newColor,T):
        self.brand = brand
        self.wheelNum = newWheelNum
        self.color = newColor
        self.T = T                              # T为废气涡轮增压
        self.info = [self.brand,self.wheelNum,self.color,self.T]
        self.index = -1
    def getBrand(self):
        return self.brand
    def getNewheelnum(self):
        return self.wheelNum
    def getNewcolor(self):
        return self.color
    def getT(self):
        return self.T
    def __iter__(self):
        print('品牌 车轮数 颜色 废气涡轮增压')
        return self
    def next(self):
        if self.index == 3:
            raise StopIteration
        self.index += 1
        return self.info[self.index]

class Land_Rover(Car):
    def __init__(self,brand, newColor):
        self.brand = brand
        self.wheelNum = 4
        self.color = newColor
        self.T = 3
        Car.__init__(self,self.brand,self.wheelNum,self.color,self.T)

Luxury_car = Car('BMW',4, 'green',2.4)               # 创建对象
print(Luxury_car.getNewcolor())                    # 访问属性
iterator = iter(Luxury_car.next,1)                    # 调用迭代函数
for info in iterator:
    print(info)

 

 

Python面向对象编程系列文章两周一更!

 

文章未经博主同意,禁止转载!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章