1. 程式人生 > >面向物件,特性之繼承

面向物件,特性之繼承

面向物件三大特性之繼承

1,什麼是繼承?

  • 繼承指的是一種關係,必須存在兩個物件才能產生這種關係

  • 被繼承的稱為父,繼承的一方稱為子

  • 在程式中,繼承指的是類與類之間的關係

2,為什麼要使用繼承?

繼承可以擴充套件已存在的程式碼模組(類)

在程式中,通過繼承可以直接使用父類已有的程式碼

3,怎麼使用繼承

繼承類的語法

class Father:
  pass

class Som(Father):     #子類後面加上括號,寫上父類的名稱
  pass
在python中,一個字類可以有多個父類,多個父類在括號中用逗號隔開

 

繼承父類中物件和方法的語法

class Parent:
  year = 2018
  def coding(self):
      print('coding')
   
class Sub(Parent):
  pass
print(Sub.year)
s1 = Sub()
s1.coding()
4,繼承對程式設計的作用

假設有個學生管理需求:

class Student:
  def __init__(self,name,age,sex):
      self.name = name
      self.age = age
      self.sex = sex
  def study(self):
      print('正在學習中')
  def eat(self):
      print('正在吃飯中')

 

後續又要求新增老師管理需求:

學生的屬性和技能也適用於老師,此時可以引用繼承,減少程式碼的冗餘
class Teacher(Student):
  pass
t1 = Teacher('alex',30,'man')
t1.eat()
t1.study()

從邏輯上來說,如果學生有打遊戲的技能,而老師並沒有這個需求,因此繼承到了老師並不需要的技能

 

正確姿勢: 抽取公共的父類 (抽象)

抽象 : 抽取多個類中相同得部分,形成另一個類

把學生和老師共有的內容抽到另一個類中,學生和老師分別繼承這個類,這樣就避免了一個類繼承到不需要的內容 因此應該先抽象,再繼承

 

 

小結:
  • 通過繼承,避免了重複程式碼的編寫。

  • 繼承的作用是可以直接使用父類已有的程式碼

  • 通過抽象,避免了一個類繼承到不需要的內容

  • 抽象的作用是儲存多個子類相同的屬性和技能

  • 正確地順序應該先抽象,再繼承

 

 

派生和覆蓋

1,派生

class Person:
•  def __init__(self,name,age,sex):
•      self.name = name
•      self.age = age
•      self.sex = sex

•  def sayhi(self):
•      print('hello,')

class Student(Person):                     # 學生屬於人類,可以直接繼承
•  def __init__(self,number):             # 加上一些學生特有的屬性
•      self.number = number

•  def study(self):
•      print('%s正在學習'%self.name)
  • 派生指某個子類繼承父類,並且擁有自己獨特的屬性或技能

  • 該子類稱之為派生類

  • 只要子類中出現任何新內容,這就是一個派生類

 

2,覆蓋

class A:
  age = 18#age相同
  def f1(self):#f1相同
      print(" A f1" )
  pass

class B(A):
  age1 = 19
  def f1(self):#f1
      self.f1()
      print(" B f1")

b1 = B()
print(b1.age)

b1.f1()

 

小結:
  • 子類出現了與父類重複的名字 稱之為覆蓋

  • 子類出現了與父類不同的名字 稱之為派生

 

 

子類訪問父類的方法

方法1 從父類去調
class Person:
  def __init__(self,name,age,sex):
      self.name = name
      self.age = age
      self.sex = sex

  def sayhi(self):
      print('hello,')

class Student(Person):                     # 學生屬於人類,可以直接繼承
  def __init__(self,name,age,sex,number): # 加上一些學生特有的屬性

#self.name = name
#self.age = age
#self.sex = sex           #程式碼重複

      Person.__init__(self,name,age,sex)   #在子類訪問父類的方式1
      self.number = number

      def study(self):
          print('%s正在學習'%self.name)
stu1 = Student('成龍',20,'man',4235)
print(stu1.__dict__)

 

方法2 super()函式
class Person:
  def __init__(self,name,age,sex):
      self.name = name
      self.age = age
      self.sex = sex

  def sayhi(self):
      print('hello,')

class Student(Person):                     # 學生屬於人類,可以直接繼承
  def __init__(self,name,age,sex,number): # 加上一些學生特有的屬性

      super(Student,self).__init__(name,age,sex)     #super()表示特殊的物件
      super().__init__(name,age,sex)#效果一樣,這是py3的語法
self.number = number

  def study_1(self):
      super().sayhi()               #訪問父類的方法1
      Person.sayhi(self)           #訪問父類的方法2
      print('%s正在學習'%self.name)

stu1 = Student('成龍',20,'man',4235)
stu1.study_1()
print(stu1.__dict__)

瞭解,在python2中,super的用法不同。super(Student,self).init()

super一定用在存在繼承關係的子類中

 

 

子類訪問父類的順序

1,存在一個父類的情況
class A:
  age = 10
class B(A):
  age = 11
class C(B):
  age = 12

c1 = C()
c1.age = 13
print(c1.age)
順序 : 物件 >>> 類 >>> 父類 >>> 父類的父類...

沿著繼承關係找,無論是屬性還是方法! 如果沒有則報錯!

 

2,存在多個父類的查詢順序

(1) ,繼承的父類,沒有父類,按照繼承順序,從左往右依次查詢
class A:
  a = 0
  pass
class B:
  a = 1
  pass
class C:
  a = 2
  pass
class D(A,B,C):
  a = 3
  pass
d = D()
d.a = 4
print(d.a)

 

查詢順序為 : A -- B -- C -- D

(2),深度優先,沿著一條線找到底

繼承的父類,存在父類,則先把父類找完 ​ 這種查詢方式僅僅在非菱形繼承(沒有共同父類)的情況下

class Q:
  a = 10
class A(Q):
  a = 0
  pass
class W:
  a = 11
class B(W):
  a = 1
  pass
class C:
  a = 2
  pass
class D(A,B,C):
  a = 3
  pass
d = D()
d.a = 4
print(d.a)

 

 

查詢順序為E -- A -- F -- B --同理

3,廣度優先,並非絕對的廣度優先,而是基於深度的廣度優先。

先是深度優先,如果發現有共同父類,則返回。最後再查詢共同父類 ​ 該查詢順序是通過C3演算法得來

 

 

瞭解:

可以用 mro(類) 獲取 類查詢的路徑

 

 

 

新式類和經典類

主要針對 python2 和 python3 區分的

所有直接繼承或間接繼承object的類 都是新式類

object 稱之為根類 意思是 所有類 都源自於object類
為什麼這麼設計?
  1. 例如:建立物件時,需要申請記憶體空間,建立新的名稱空間,將物件的屬性放入名稱空間, ​ 這一些了複雜的基礎操作,都yo由object來完成 ​ 簡單地說object提供了一些常用的基礎操作

  2. 即所有類都屬於新式類(在python3中)

在python3中預設所有類都是新式類

而python2中預設是經典類(不會自動繼承Object)

 

class S:
  pass
class Student(S):
  pass

 

bases__用於檢視父類

print(Student.__bases__)

顯示屬性的查詢順序列表,屬性查詢屬性就是按照該列表來查詢的

print(Student.mro())

 

mro

檢視訪問路徑

"super訪問父類內容時 按照mro列表屬性查詢"


class S:
  def f1(self):
      print("s f1")

class A(S):
  pass

class B(S):
  def f1(self):
      print("b f1")
  pass

class C(A,B):
  def f2(self):
      print("c f2")
      super().f1()


print(C.mro())
c1 = C()
c1.f2()



組合

1,什麼是組合

  • 多個物件放在一起就是組合

  • 一個物件將另一個物件作為自己的屬性,就是組合

2,組合的目的

  • 處理資料更有效率,有效降低耦合度和程式碼的冗餘

3,組合的實現

  • 程式碼1 是通過繼承實現程式碼的精簡

  • 但是如果繼續新增一些功能,比如學生和老師的手機

  • 手機的屬性有品牌,價格,運營商,號碼

  • 如果全部都放到程式碼1 的父類中,看起來非常的臃腫,管理成本非常高,擴充套件性變得很差

  • 我們可以通過組合實現程式碼的管理,使程式碼的可讀性和整潔性都得到有效提高

  • 程式碼2 是新增手機功能後通過繼承和組合一起實現.

  • 組合其實我們在使用者互動的練習就實現過了

 

程式碼1,繼承
class Person:
  def __init__(self, name, sex, age, number):
      self.name = name
      self.sex = sex
      self.age = age
      self.number = number
       
class Student(Person):
  def __init__(self,name,sex,age,number,game):
      super().__init__(name,sex,age,number)
      self.game = game
  def show_info(self):
      print(self.__dict__)
  def select(self):
      print('%s正在選課'%self.name)
       
class Teacher(Person):
  def __init__(self,name,sex,age,number,teach_class):
      super().__init__(name,sex,age,number)
      self.teach_class = teach_class
  def show_info(self):
      print(self.__dict__)
  def set_score(self):
      print('%s正在為學生打分'%self.name)
s1 = Student('韓信','man',18,51,'王者榮耀')
s1.show_info()
t1 = Teacher('韓信','man',18,51,'王者榮耀')
t1.show_info()

 

程式碼2 ,組合的形式

 


class Phone:#組合的程式碼
  def __init__(self,phonenumber,operator,address):
      self.phonenumber = phonenumber
      self.operator = operator
      self.address = address

  def call(self,t):
      print("%s 正在撥號 %s" % (t.name,self.phonenumber))


class Person:#父類
  def __init__(self,name,sex,age):
      self.name = name
      self.sex = sex
      self.age = age
       
class Student(Person):#子類
  def __init__(self,name,sex,age,number):
      super().__init__(name, sex, age)
      self.number = number
  def show_info(self):
      print(self.__dict__)
  def select_cursor(self):
      print("%s 正在選課...." % self.name)
       
class Teacher(Person):#子類
  def __init__(self,name,sex,age,salary,level):
      super().__init__(name,sex,age)
      self.salary = salary
      self.level = level
  def set_score(self):
      print("%s 正在為學生打分..." % self.name)

stu = Student("喬峰","男",38,"007")

p1 = Phone("1999999999","中國小米移動","上海浦東")

stu.q = p1             #這裡的q是變數名,相當於把p1賦值給q,通過stu來呼叫

stu.q.call(stu) #不能直接stu.p1.call()