目錄
封裝:
特點:
私有化:
Python中的dir()函數:
舉例說明:
@property裝飾器:
格式:
繼承:
has a:
is a:
特點:
對于super關鍵詞的用法介紹:
使用格式:
多繼承(了解):
多态:
封裝:
特點:
1.可了解為私有化屬性
2.在類中定義公有set方法和get方法,通過調用這兩個方法來操作私有化屬性
3.被私有化的屬性不能被繼承
在Python中,封裝的概念可了解為私有化。那麼我們為什麼要提出私有化呢,其實将屬性私有化可以提升它的安全級别,通路僅限于類中,不能随意被外界修改。下面簡單介紹一下私有化的概念。
私有化:
它的好處:
1.隐藏屬性不能随意被外界修改
2.當需要修改私有化時,可通過函數進行修改
def setXXX(self,xxx): #通過set對私有化屬性進行重新指派
if xxx符合條件:
self.__xxx = xxx
else:
不指派
3.當需要擷取私有化時,也通過函數擷取
def getXXX(self):
return self.__xxx
Python中的dir()函數:
dir()函數不帶參數時,以清單形式傳回目前範圍内的變量,方法和定義的類型;
帶參數時,以清單形式傳回參數的屬性,方法。
同 __dir__() 的用法相同
舉例說明:
class Student:
__age = 18 # 私有化的類屬性
def __init__(self, name, age):
self.__name = name
self.__age = age
self.__score = 60 # 外部無法通路,無法修改
# 定義公有set和get方法
# set是為了指派
def setAge(self, age): # 可以改變私有化屬性的值
if age > 0 and age <= 100:
self.__age = age
else:
print('年齡不符合規定值!')
# get是為了取值
def getAge(self):
return self.__age
def __str__(self):
return '姓名:{},年齡:{},分數:{}'.format(self.__name, self.__age, self.__score)
one = Student('JSY', 20)
print(one)
one.age = 21
one.name = 99
print(one)
one.setAge(28)
print(one)
# dir(對象名/類名) 或者 (對象名/類名).__dir__()
print(dir(one))
print(dir(Student))
print(one._Student__age) # 其實它就是__age,隻是系統自動改名了,由此可見,私有化也是僞私有的
print(Student._Student__age) # 通過該方法通路到了類的私有化屬性
print(one.__dir__())
print(__name__) # 表示目前程式運作在哪一個子產品中
輸出:
姓名:JSY,年齡:20,分數:60
姓名:JSY,年齡:20,分數:60
姓名:JSY,年齡:28,分數:60
['_Student__age', '_Student__name', '_Student__score', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'getAge', 'name', 'setAge']
['_Student__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'getAge', 'setAge']
28
18
['_Student__name', '_Student__age', '_Student__score', 'age', 'name', '__module__', '__init__', 'setAge', 'getAge', '__str__', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
__main__
解讀:第一行輸出為建立對象,而分數的屬性已經被固定,無需傳入參數;
第二行輸出,是想要直接修改類中的私有化屬性,修改失敗,故屬性無變化
第三行輸出,采用(set和get)函數的方式修改私有化屬性,修改成功
接下兩行輸出,為對象和類中的屬性及方法輸出
接下兩行輸出,為調用系統改名後的私有化屬性,故私有化本身是僞私有的
倒數第二行輸出,為使用__dir__()的方法擷取對象的屬性及方法
最後一行輸出,__name__方法可傳回目前程式在哪一個子產品中運作
@property裝飾器:
開發中會使用這種方式進行私有化的處理,代替set和get,來擷取和修改屬性。
格式:
@property
def func1(self): #先定義擷取私有屬性
return self.__xxx #傳回私有屬性值
@func1.setter
def func2(self,xxx): #再定義修改私有屬性
self.__xxx=xxx
###代碼示例###
class Student:
__age = 18 # 私有化的類屬性
def __init__(self, name, age):
self.__name = name
self.__age = age
self.__score = 60 # 外部無法通路,無法修改
#先有getxxx
@property
def acquire_age(self):
return self.__age
#再有setxxx,因為get依賴set
@acquire_age.setter
def assignment_age(self, age):
if age > 0 and age < 100:
self.__age = age
else:
print('年齡不符合規定值 !')
def __str__(self):
return '姓名:{},年齡:{},分數:{}'.format(self.__name, self.__age, self.__score)
s = Student('Tom', 99)
s.assignment_age = 98 #給__age指派
print(s.acquire_age) #擷取__age的值
輸出:
98
此時,可直接通過函數名assignment_age來給私有屬性__age賦一個新的值
再通過函數acquire_age,傳回私有屬性__age的值
繼承:
在講繼承之前我們先看一個例子:
import random
# 聲明(Road)
class Road:
def __init__(self, name, len):
self.name = name
self.len = len
# 聲明(Car)
class Car:
def __init__(self, brand, speed):
self.brand = brand
self.speed = speed
def get_time(self, road): # 需注意:road與r指向同一位址空間
ran_time = random.randint(1, 10)
message = '{}品牌的車在{}上以{}速度行駛{}小時'.format(self.brand, road.name, self.speed, ran_time)
print(message)
def __str__(self):
return '{}品牌,速度:{}'.format(self.brand, self.speed)
# 建立執行個體化對象
r = Road('魏州大道', 6666)
c = Car('凱迪拉克', 66)
print(c)
c.get_time(r)
輸出:
凱迪拉克品牌,速度:66
凱迪拉克品牌的車在魏州大道上以66速度行駛6小時
代碼注意:在Car類裡的get_time方法,其參數可以是一個對象類型,且該對象可以是其他類的, 故在調用該方法的時候,可以傳入Road的對象,借此來調用Road裡的屬性或是方法。
該例其實是一個 has a 的包含關系
在這個例子中我們看到了類與類直接的聯系,而繼承則是面向對象中的重要的類之間的關系。python中的繼承可分為兩種 has a 和 is a,下面介紹這兩種情況。
has a:
has a 強調一種包含的關系,比如:如果A中有B,那麼,B就是A的組成部分;當一個類中使用了另外一種自定義的類型(類或對象等),此時我們認為出現了has a 的包含現象。
###舉例###
class Computer:
def __init__(self, brand, type, color):
self.brand = brand
self.type = type
self.color = color
def online(self):
print('正在玩LOL中!')
def __str__(self):
return self.brand + '---' + self.type + '---' + self.color
class Book:
def __init__(self, book_name, author, number):
self.book_name = book_name
self.author = author
self.number = number
def __str__(self):
return self.book_name + '---' + self.author + '---' + str(self.number)
class Student:
def __init__(self, name, computer, book):
self.name = name
self.computer = computer
self.books = []
self.books.append(book)
def borrow_book(self, input_book):
# print('書籍資訊:')
# print(book) #相當與調用對象名book1
for book in self.books:
if book.book_name == input_book.book_name:
print('已經借過此書!')
break
else:
self.books.append(input_book)
print('借書成功!')
break
print(self.books)
def show_book(self):
for book in self.books:
print(book.book_name)
def __str__(self):
return self.name + '---' + str(self.computer) + '---' + str(self.books) # 不能傳回一個對象
# 建立對象
computer = Computer('hp', 'pro++', 'red') # computer 對象類型
book = Book('武動乾坤', '天蠶洋芋', 15)
stu = Student('JJ', computer, book) # 此時出現了 包含關系 (stu對象包含了computer對象和book對象)
print(stu)
book1 = Book('鬥破蒼穹', '天蠶洋芋', 20)
stu.show_book()
stu.borrow_book(book1)
輸出:
JJ---hp---pro++---red---[<__main__.Book object at 0x0000021AA3F7C610>]
武動乾坤
借書成功!
[<__main__.Book object at 0x0000021AA3F7C610>, <__main__.Book object at 0x0000021AA3F7C520>]
武動乾坤
鬥破蒼穹
從代碼:stu = Student('JJ', computer, book) 可以看出,定義一個Student對象的時候,傳入了 Computer的對象和Book的對象,故在Student類中可以對它們的(非私有化)屬性進行調 用。
is a:
is a 可以說是真正意義上的繼承關系:如果A是B的一種,那麼B就是A的基類(base class / 父類);比如:等邊三角形是三角形,則三角形是等邊三角形的基類。
特點:
1.私有化屬性不能被繼承
2.如果子類中沒有定義__init__,自動調用父類的__init__方法
3.如果子類繼承父類需要定義自己的__init__方法(來初始化新的屬性等操作),需要在__init__裡先 調用一下父類的__init__,再去初始化新的屬性或其他操作。
4.調用父類的方法:super關鍵詞
5.如果父類有eat(),子類也定義一個eat()方法,按照就近原則:先找目前類,再去找父類
如果子類出現父類同名的方法,可稱之為 override--重寫(覆寫)。
此情況一般出現在:父類提供的方法不能滿足子類的需求
6.子類中可調用父類的同名方法:
使用super().方法名(參數) #可調用父類的方法,再寫新增的方法(内容)
對于super關鍵詞的用法介紹:
Python中的super()方法設計目的是用來解決多重繼承時父類的查找問題,是以在單重繼承中用不用super都沒關系;但是,使用super()是一個好的習慣。一般我們在子類中需要調用父類的方法時才會這麼用。
1.super(cls,obj) #即傳入類名+對象名
obj對象必須是cls類的對象(cls的子類的對象當然也是cls類的對象),記作 type(obj) <= cls;具有判斷功能
2.super(cls1,cls2) #即傳入兩個類名
cls2必須是cls1的子類或是本身,記作cls2<=cls1 (右的範圍小于左);具有判斷功能
3.super().__init() #等同于 super(A,self).__init__()
使用格式:
class Student(Person): #可了解為所有學生都是人,Person是父類
pass
###舉例說明###
# 可以定義一個“總類”
class Person:
def __init__(self, name,age):
self.name = name
self.age = age
def eat(self):
print(self.name + '正在吃飯!','年齡:'+str(self.age))
# 在定義這種相似的類時,可能會出現許多重複的屬性或方法,此時需要使用到*繼承*
class Student(Person): # 此為繼承的格式
def __init__(self, name,age):
print('--->Student的init')
# 如果要定義自己的__init__,還需調用父類的__init__;可使用super來調用父類的方法
# super(Student, self).__init__() # super另一種使用方式
super().__init__(name,age) # super() == 父類Person ;調用父類的__init__方法
super(Student, self).__init__()
class Employee(Person):
def __init__(self,name,age,work): #新增的屬性work,需要在本類中進行處理
print('--->Employee的init')
super().__init__(name,age) #調用父類的__init__先将self.name 和 self.age 擷取
self.work = work #獨有屬性在本類的__init__裡進行初始化
def __str__(self):
return '姓名:'+self.name+'年齡:'+str(self.age)+'職業:'+self.work
def eat(self):
super(Employee, self).eat() #在子類中調用父類的同名方法,再向下寫新增的内容
print(self.name+'正在吃飯! ')
class Doctor(Person):
def __init__(self,name,age,department):
super(Doctor, self).__init__(name,age) #super(類名,對象).__init__();底層進行判斷:判斷該對象是否是以這個類建構出來的
self.department = department
def __str__(self):
return '姓名:'+self.name+'年齡:'+str(self.age)+'部門:'+self.department
s = Student('Jack',16)
s.eat()
print(s.name)
e = Employee('Tom',29,'程式員')
print(e)
e.eat() #此時由于Employee本類中就有eat方法,無需去調用父類(Person)裡的eat方法,故此為就近原則
d = Doctor('華佗',88,'老中醫')
print(d)
d.eat()
輸出:
--->Student的init
Jack正在吃飯! 年齡:16
Jack
--->Employee的init
姓名:Tom年齡:29職業:程式員
Tom正在吃飯! 年齡:29
Tom正在吃飯!
姓名:華佗年齡:88部門:老中醫
華佗正在吃飯! 年齡:88
本例中,Person為父類,Student,Employee,Doctor,均繼承它,而這些子類都有一些本身才具有的性質,故需要重寫父類的__init__方法;此外,繼承中的檢索按照就近原則,先檢索本類,再到父類。
多繼承(了解):
Python允許多重繼承:一個子類可以有多個父類(java不可以) #開發中很少使用
class 子類(父類1,父類2...):
pass
多重繼承的搜尋順序:在Python2中,對于經典類和新式類有所不同
經典類: #Python2中是從左至右,深度優先
class Base:
pass
class A(Base):
pass
class B(Base):
pass
新式類:
class Base(object): #差別在此,python2中是廣度優先
pass
class A(Base):
pass
class B(Base):
pass
注意:在python3裡,無論經典類還是新式類都是廣度優先
檢視搜尋順序的方法:1.類名.__mro__ #直接使用檢視
2.import inspect #導包後,使用getmro()方法檢視
print(inspect.getmro(類名))
###舉例說明###
#C繼承了A和B,而A和B繼承了Base
#Base-->A,B-->C
class Base:
def test(self):
print('--->Base')
class A(Base):
def test1(self):
print('--->A')
class B(Base):
def test2(self):
print('--->B')
class C(A,B): #同時繼承多個父類
def test(self):
print('--->C')
import inspect
print(inspect.getmro(C)) #顯示的可了解為搜尋順序
print(C.__mro__) #同上
c = C()
c.test1() #可以使用多個父類提供的方法
c.test2()
# c.test3()
c.test() #出現與父類同名方法時,搜尋順序為就近原則
輸出:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
--->A
--->B
--->C
注意:前兩行輸出為繼承類的搜尋順序
最後的輸出是 調用的C類中的test方法,由于出現與父類同名的方法,搜尋順序為就近原則
多态:
實際上,python并無嚴格多态概念。多态可了解為:一種事物的多個形态,對于繼承可以認為是為了精簡代碼,而多态是對精簡後代碼的靈活使用,因而多态依賴于繼承。
##示例##
class Person:
def __init__(self,name):
self.name = name
def feed_pet(self,pet): #此時出現了多态;參數pet既可以接收cat,也可以接收dog,還可以接收tiger(不是Pet的子類)或其他對象
if isinstance(pet,Pet): #判斷了pet是否是Pet或Pet子類的對象
print('{}喜歡養寵物:{},昵稱是:{}'.format(self.name,pet.role,pet.nickname))
else:
print('這個不是寵物!可能有危險!!!')
class Pet:
role = 'Pet'
def __init__(self,nickanme,age):
self.nickname = nickanme
self.age = age
def show(self):
print('昵稱:{},年齡:{}'.format(self.nickname,self.age))
class Cat(Pet):
role = '貓'
def catch_mouse(self):
print('抓老鼠...')
class Dog(Pet):
role = '狗'
def watch_house(self):
print('看家...')
class Tiger:
def eat(self):
print('兇猛東北虎!')
cat = Cat('妙妙',3)
dog = Dog('旺财',2)
tiger = Tiger()
p = Pet('山竹',1)
person = Person('JJ')
person.feed_pet(cat) #此時涉及到了多态
person.feed_pet(tiger)
person.feed_pet(p)
輸出:
JJ喜歡養寵物:貓,昵稱是:妙妙
這個不是寵物!可能有危險!!!
JJ喜歡養寵物:Pet,昵稱是:山竹
注意:
1.對同一個函數feed_pet(),傳入不同參數(對象),可以實作不同功能。
2.由于python中所有類均繼承于祖先類Object,故多态總能應用到很多場景(無論類與類之間是否有繼承關系)。