面向對象
- 與面向過程對比:
- 面向過程:數學邏輯的映射,學會做個好員工
- 面向對象:生活邏輯的映射,學會做個好領導
- 官方定義:
- 類:具有相同特徵(屬性和行爲)的事物的抽象
- 對象:某個類的具象
- 編程語言:
- 類:是一種自定義的數據類型
- 對象:某個類類型的變量
面向對象語法
-
類的定義:
class 類名:
內容 -
語法說明:
- 定義類需要使用關鍵字class
- 類名:原則上只要符合標識符命名規範即可,但是通常我們都使用大駝峯(每個單詞首字母大寫)風格命名
- 如:UserName
- 類名後面的’:'不要忘記
- 類的內容要進行整體縮進
- 行爲:通過方法體現,在類中定義相關的函數即可(第一個參數通常是self)
- 屬性:通過變量體現,在需要時動態添加,很多時候定義類時沒有體現
- 成員訪問:
- 屬性:對象.屬性名
- 方法:對象.方法名()
-
示例:
# 定義類
class Person:
# 行爲通過方法體現# 吃飯 def eat(self): print('紅燒雞腿我喜歡吃') # 睡覺 def sleep(self): print('睡覺也是一種生活態度') # 定義對象 liang = Person() # 調用方法 liang.eat() liang.sleep() # 屬性時動態添加的 liang.name = '某某某' # 獲取屬性 print(liang.name)
-
self使用
class Person:
def run(self):
# self表示當前對象:誰調用該方法就表示誰
print(’{}每天以2m/s的速度慢跑5km’.format(self.name))def introduce(self): # 不但可以訪問成員屬性 print('我叫{}'.format(self.name)) # 還可以調用成員方法 self.run() fei = Person() fei.name = '路人甲' fei.run() fei.introduce() long = Person() long.name = '路人乙' long.introduce()
- 說明:
1.類中的每個成員方法都有一個self參數,調用的時候不需要傳遞該參數
2.該參數的名字可以不是self,只不過通常我們都使用這個
3.self表示當前對象,對調用該方法self就代表誰,哪個對象調用就表示哪個對象
4.通過self可以訪問成員屬性,也可以調用成員方法
- 說明:
-
__str__方法
class Person:
# 使用print方法打印對象,默認打印 類名 + 地址
# 若想打印特定內容,重寫該方法即可,要求返回一個字符串
def str(self):
return ‘我叫{},今年{}’.format(self.name, self.age)james = Person() james.name = '路人丙' james.age = 33 print(james)
-
構造方法:創建對象後,初始化屬性時,系統會自動調用該方法
class Cat:
def str(self):
return ‘name:{},age:{},color:{}’.format(self.name, self.age, self.color)# 構造方法:創建對象後,初始化系統就會自動調用該方法 def __init__(self, name, age, color): print('__init__') self.name = name self.age = age self.color = color # 這種形式比較繁瑣 # tom = Cat() # tom.name = 'Tom' # tom.age = 3 # tom.color = '藍色' # 這種比較簡潔 tom = Cat('Tom', 3, '藍色') print(tom)
-
析構方法:當對象釋放時系統會自動調用,通常用於釋放資源
class Pig:
# 析構方法:當對象釋放時,系統會自動調用
# 若手動使用del刪除,則會立即調用該方法
# 該方法一般做資源釋放處理:數據庫連接斷開,文件關閉
def del(self):
print(‘大師兄,我不行了’)bajie = Pig() del bajie print('八戒,一路走好!')
-
示例:小明手裏有兩張牌,左右♥K,右手♠A,小明交換兩手的牌後,手裏分別是什麼?
-
思路:
- 先找到對象:左手、右手、♥K、♠A、小明
- 根據對象抽象出對應的類:人、手、牌
- 根據需要寫出相應的邏輯,很可能反過來完善類的設計
- 按照題目要求創建相關對象,調用相關方法,實現相關功能
-
代碼:
對象:小明、左手、右手、♥K、♠A
類:人、手、牌
設計相應的類
撲克牌
class Poker:
def init(self, color, number):
self.color = color
self.number = numberdef __str__(self): return '{}{}'.format(self.color, self.number)
創建兩張牌
p1 = Poker(‘♥’, ‘K’)
p2 = Poker(‘♠’, ‘A’)手的類
class Hand:
def init(self, poker):
self.poker = pokerdef hold_poker(self, poker): self.poker = poker
創建左右兩隻手
left_hand = Hand(p1)
right_hand = Hand(p2)人的類
class Person:
def init(self, name, left_hand, right_hand):
self.name = name
self.left_hand = left_hand
self.right_hand = right_hand# 展示手裏的牌 def show(self): print('{}張開手'.format(self.name), end=' ') print('左手:{}'.format(self.left_hand.poker), end=',') print('右手:{}'.format(self.right_hand.poker)) # 交換兩手的牌 def swap(self): self.left_hand.poker, self.right_hand.poker = self.right_hand.poker, self.left_hand.poker print('{}交換兩手的牌'.format(self.name))
創建小明對象
xiaoming = Person(‘小明’, left_hand, right_hand)
展示手裏的牌
xiaoming.show()
交換兩手的牌
xiaoming.swap()
#再次展示
xiaoming.show()
-
常用內置函數
-
內置函數:在類的內部,特定時機自動觸發的函數。
-
示例:setattr、getattr、delattr
class Person:
def init(self, name):
self.name = namedef __str__(self): return '姓名:{}'.format(self.name) def __del__(self): print('對象即將銷燬') # 當獲取不存在的屬性時,會自動觸發該方法 def __getattr__(self, item): if item == 'age': return 18 else: return '你猜' # 當設置不存在的屬性時,會自動觸發該方法 def __setattr__(self, key, value): print(key, value) self.__dict__[key] = value # 銷燬對象成員屬性時,會自動觸發該方法 def __delattr__(self, item): print(item, '即將銷燬') xiaoming = Person('小明') xiaoming.age = 20 print(xiaoming.age) # 存放對象的所有屬性 # print(xiaoming.__dict__) # print(xiaoming) del xiaoming.age
-
將對象當做字典操作,特定時機會自動觸發的方法
class Person:
# 將對象當做字典操作,設置鍵值對時會觸發該方法
def setitem(self, key, value):
# print(key, value)
self.dict[key] = value# 將對象當做字典操作,根據鍵獲取值時會觸發該方法 def __getitem__(self, item): # print(item) return self.__dict__.get(item) # 將對象當做字典操作,刪除指定的鍵值對時自動觸發 def __delitem__(self, key): del self.__dict__[key] xiaoming = Person() xiaoming['name'] = '小明' print(xiaoming.dict) print(xiaoming['name']) del xiaoming['name']
-
將對象當做函數調用時,會自動觸發下面方法
class Person:
# 將對象當做函數調用時,會自動觸發該方法
def call(self, *args, **kwargs):
# print(‘call’)
return sum(args)xiaoming = Person() # 這樣操作,需要提供call方法 ret = xiaoming(1, 2, 3, name='小明') print(ret)
-
函數判斷
class A:
def call(self, *args, **kwargs):
passdef test(): pass a = A() # 判斷是否可調用 print(callable(test)) print(callable(a)) # 判斷是否擁有'__call__'屬性 print(hasattr(test, '__call__')) print(hasattr(a, '__call__')) # 判斷是否是函數 from inspect import isfunction print(isfunction(test)) print(isfunction(a))
面向對象三大特點
- 封裝:既可對數據結構進行封裝,又可對處理數據的方法進行封裝
- 繼承:強調的父子類的關係
- 多態:不同對象調用相同的方法,會有不同的響應
類的繼承
-
相關概念
- 繼承:父類的屬性和方法,子類直接擁有,稱爲繼承
- 派生:子類在父類的基礎上衍生出新的特徵(屬性和行爲)
- 總結:其實他們是一回事,只是描述問題的側重點不同(繼承強調相同點,派生強調不同點)
-
繼承語法
# class Animal(object):
# 當沒有指定父類時,默認繼承object
class Animal:
def init(self, name):
self.name = namedef eat(self): print('小動物喜歡一天到晚吃個不停') # 繼承自Animal class Dog(Animal): pass d = Dog('旺財') # 可以擁有父類的方法 d.eat() # 也可以擁有父類的屬性 print(d.name)
-
派生示例
class Animal:
def run(self):
print(‘小動物喜歡成天跑個不停’)class Cat(Animal): def eat(self): print('貓喜歡吃老鼠') tom = Cat() tom.run() # 多出來的行爲 tom.eat() # 多出來的屬性 tom.color = '藍色' print(tom.color)
-
重寫方法
-
若父類的方法完全不合適,可以進行覆蓋重寫
-
若父類的方法不夠完善,可以添枝加葉進行完善
-
示例:
class Animal:
def eat(self):
print(‘小動物一天到晚的出個不停’)def run(self): print('小動物一天到晚的四處連跑')
class Cat(Animal):
# 父類的方法完全不合適,覆蓋重寫
def run(self):
print(‘俺走的時貓步’)# 父類的方法部分合適,需要添加內容進行完善 def eat(self): # 保留父類的內容,不建議使用此方式 # Animal.eat(self) # super(Cat, self).eat() # 類名及self可以不傳 super().eat() print('不過俺喜歡吃魚')
jiafei = Cat()
jiafei.run()
jiafei.eat()
-
-
多繼承:一個子類可以擁有多個父類
class A:
def eat(self):
print(‘eat func in class A’)class B: def eat(self): print('eat func in class B') class C(A, B): def eat(self): # 這種方案是默認的繼承順序進行選擇的父類方法 # super().eat() # 人爲指定調用某個父類的方法 B.eat(self) c = C() c.eat()
訪問權限
-
權限
- 公有的:類中的普通的屬性和方法,默認都是公有的;可以在類內、類外、子類中使用
- 私有的:定義時在前面添加兩個’_’,就變成了私有的;只能在類內使用,不能在類外及子類中使用
-
示例:
class Person:
def init(self, name):
self.name = name
self.__age = 20def eat(self): print('民以食爲天') def __test(self): print('__test') xiaoming = Person('小明') print(xiaoming.name) xiaoming.eat() #不能在類外使用 #print(xiaoming.__age) xiaoming._Person__test() print(xiaoming.dict) #儘管可以這樣訪問私有屬性,但是強烈建議不要這樣使用 #print(xiaoming._Person__age) class Man(Person): def introduce(self): # 不能在子類中使用 # print(self.__age) print('我叫{}'.format(self.name)) self.eat() m = Man('亮亮') m.introduce()
類屬性
-
說明:定義類時,寫在方法外的屬性,通常會寫在類的開頭,這樣的屬性稱爲類屬性
-
示例:
class Person:
# 類屬性,通過類名訪問,屬於整個類,而不是某個對象
# nation = ‘中國’# 限制可以使用的屬性,提高訪問的效率 # 也可以提高訪問速度,減少內存使用 __slots__ = ('name', 'age', 'nation') def __init__(self, name): self.name = name self.nation = 'china' p1 = Person('小明') p2 = Person('小紅') print(p1.name, p2.name) print(Person.nation) #p1.nation = 'china' print(p1.nation) print(p2.nation) #print(Person.nation) p1.age = 20 #p1.height = 180 #特殊的類屬性 #表示類名的字符串 print(Person.name) #表示父類構成的元組 print(Person.bases) #存儲類的相關信息 print(Person.dict) #限制可以使用的屬性 print(Person.slots)
類方法
-
說明:
- 定義時使用classmethod裝飾器裝飾的方法
- 使用時通過類名調用
-
作用:
- 可以創建對象或者簡潔的創建對象
- 對外提供簡單易用的接口
-
示例1:創建對象
class Person: def eat(self): print('我喜歡吃麻辣燙,不要麻椒和辣椒') @classmethod def test(cls): print(cls) print('類方法') # 創建對象,或者簡潔的創建對象 @classmethod def create(cls): p = cls() p.age = 1 return p p1 = Person() p1.eat() # 通過類名調用 Person.test() # 創建或簡潔的創建對象 p2 = Person.create() print(type(p2))
-
示例2:提供接口
class Number:
def init(self, num1, num2):
self.num1 = num1
self.num2 = num2def add(self): return self.num1 + self.num2 def sub(self): return self.num1 - self.num2 def mul(self): return self.num1 * self.num2 def div(self): if self.num2 == 0: return None return self.num1 / self.num2 # 對外提供簡單易用的接口 @classmethod def pingfanghe(cls, num1, num2): n1 = cls(num1, num1) n12 = n1.mul() n2 = cls(num2, num2) n22 = n2.mul() n3 = cls(n12, n22) return n3.add() he = Number.pingfanghe(3, 4) print(he)
靜態方法
-
說明:
- 使用staticmethod裝飾器裝飾的方法(方法沒有cls參數)
- 通過類名進行調用
-
示例:
class Person:
@staticmethod
def test():
print(‘static method test’)# 創建對象 @staticmethod def create(): p = Person() return p #Person.test() #p = Person.create() #print(p)
、 class Animal:
def run(self):
pass
class Dog(Animal):
def run(self):
print(‘狗通常走S型’)
class Cat(Animal):
def run(self):
print(‘貓平時走貓步,偶爾突然加速’)
def test(obj):
obj.run()
d = Dog()
c = Cat()
test(d)
test©
屬性函數
-
說明:將成員方法當做屬性一樣進行訪問
-
作用:保護特定屬性,或者對特定屬性進行處理
-
示例:
class User: def __init__(self, username, password): self.username = username self.__password = password # 該方法可以像成員屬性一樣訪問 @property def password(self): print('有人想查看密碼') return '想偷看密碼,沒門' # return self.__password # 在設置密碼時,會自動調用 @password.setter def password(self, password): print('@password.setter', password) self.__password = '加密' + password + '加密' u = User('xiaoming', '111111') # print(u.password()) print(u.password) # 設置密碼,會自動調用setter方法 u.password = 'abcde'
面向對象及異常處理
內置函數
-
總結:init、del、str、attr系列、item系列、call
-
str__與__repr
- 使用print/str方法時會自動觸發__str__方法,當__str__不存在,嘗試__repr__
- 使用repr方法時會自動觸發__repr__方法
- repr方法通常用於返回對象的字符串表示形式
- 這兩個方法都只能返回字符串
- eval方法可以將一個字符串當做有效的代碼執行
-
算術運算符重載
-
示例:
class Number:
def init(self, num):
self.num = num# 對象出現在'+'左邊時會自動觸發 def __add__(self, other): print('__add__') return self.num + other # 對象出現在'+'右邊是會自動觸發 def __radd__(self, other): print('__radd__') return self.num + other # +=運算時會自動觸發,沒有時會觸發 __add__ def __iadd__(self, other): print('__iadd__') return Number(self.num + other)
n = Number(100)
ret = n + 200
ret = 200 + n
print(ret)
n += 200 # n = n + 200
print(n) -
自己測試
加法:add、radd、iadd
減法:sub、rsub、isub
乘法:mul、rmul、imul
除法:truediv、rtruediv、itruediv
求餘:__mod、rmod、imod
-
-
關係運算符重載
>: gt
=: ge
<: lt
<=: le
==: eq
!=: ne -
示例
class Number:
def init(self, num):
self.num = numdef __gt__(self, other): print('__gt__') return self.num > 200 def __lt__(self, other): print('__lt__') return self.num < other def __eq__(self, other): print('__eq__') return self.num == other # 當沒有此方法時,使用!=也會觸發__eq__方法 def __ne__(self, other): print('__ne__') return self.num != other n = Number(100) # print(n > 200) # print(200 > n) # print(200 == n) print(200 != n)
深淺拷貝
-
引用計數
- python中的所有變量都是對象,對象的管理採用的時引用計數的方式
- 當多一個變量指向對象計數值加1,當少一個變指向對象計數值減1,減到0是,釋放對象(del)
-
函數傳參
- 對於不可變的變量來說,函數中不可能改傳遞過來的變量
- 對於可變的容器對象及自定義對象,作爲函數參數傳遞時,傳遞的是引用,可以修改該對象
-
深淺拷貝
class Person:
def del(self):
print(‘對象釋放’)p1 = Person() p2 = p1 print(id(p1)) print(id(p2)) del p1 del p2 print('OVER') def test(m): # m += 1 m[0] = 300 # n = 100 n = [100, 200] test(n) print(n) import copy lt = [1, 2, [3, 4]] # 淺拷貝,只拷貝對象本身,不拷貝對象中的元素 # lt2 = lt.copy() # 淺拷貝 lt2 = copy.copy(lt) # 深拷貝:不但拷貝對象本身,還拷貝對象中的元素 lt2 = copy.deepcopy(lt) lt[0] = 100 lt2 = 300 print(id(lt)) print(id(lt2)) print(lt) print(lt2)
數據持久化(pickle)
-
說明:數據持久化存儲方案,普通文件、序列化、數據庫
-
示例:
import pickleclass Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return 'name:{},age:{}'.format(self.name, self.age) xiaoming = Person('xiaoming', 20) # 轉換爲bytes類型 # s = pickle.dumps(xiaoming) # print(s) # 從字節流中提取對象 # xm = pickle.loads(s) # print(xm) # 保存到文件中 # fp = open('data.txt', 'wb') # pickle.dump(xiaoming, fp) # 從文件中獲取對象 fp = open('data.txt', 'rb') xm = pickle.load(fp) print(xm)
異常處理
-
相關概念
- 錯誤:程序運行之前的語法問題,如:關鍵字、縮進、括號不成對等
- 異常:在程序運行過程中出現的問題,如:未定義變量、除數爲0、屬性不存在等
-
異常處理
- 說明:異常處理可以理解爲特殊的流程控制語句,可以提高代碼的健壯性。
-
異常語法:
try:
print(‘正常代碼’)
# print(a)
3/0
except Exception as e:
# Exception 是所有異常的基類,此處可以捕獲所有的異常
print(‘出現異常’)
print(e)print('其他內容')
-
多個異常
# 分類捕獲異常
‘’’
try:
# print(a)
# 3/0
d = {}
print(d[‘name’])
except NameError as e:
print(‘NameError:’, e)
except ZeroDivisionError as e:
print(‘ZeroDivisionError:’, e)
except Exception as e:
print(‘OtherError:’, e)
‘’’try: # print(a) # 3/0 fp = open('123.txt') except (NameError, ZeroDivisionError) as e: # 將某些異常進行統一處理,寫在一個元組中即可 print(e) except: print('其他異常')
-
完整結構(else-finally)
try:
print(‘正常代碼’)
print(a)
except:
# 出現異常時執行
print(‘出現異常’)
else:
# 正常結束(沒有異常)時會執行
print(‘正常結束’)
finally:
# 無論有無異常,都會執行
print(‘最後執行’)
else:正常結束時執行else中的代碼
finally:無論有無異常,最後都執行 -
拋出異常:raise
try:
print(‘正常代碼’)
# 根據業務邏輯的需要,手動拋出異常
raise Exception(‘手動拋出的異常’)
except Exception as e:
print(‘異常:’, e)print('OVER')
-
異常嵌套(try-except結構中再次使用try-except結構)
print(‘我要去上班,什麼事也阻止不了我上班的腳步’)
try:
print(‘我準備騎電動車’)
raise Exception(‘昨天晚上不知道哪個缺德的傢伙把我充電器拔了’)
print(‘騎車提前到達公司’)
except Exception as e:
print(e)
try:
print(‘我準備做公交車’)
raise Exception(‘等了20分鐘一直沒有公交車,果斷放棄’)
print(‘坐公交車準時到達公司’)
except Exception as e:
print(e)
print(‘我準備打車’)
print(‘打車還是快,一會就到達公司’)print('熱情滿滿的開始一天的工作')
-
自定義異常類(需要繼承自官方的異常基類Exception)
# 自定義異常類
class MyException(Exception):
def init(self, msg):
self.msg = msgdef __str__(self): return self.msg # 特定異常標準處理方案 def deal(self): print('處理特定的自定義異常') try: print('正常執行') # 手動拋出自定義異常 raise MyException('出現了自定義異常') except MyException as e: print(e) # 調用方法,處理異常 e.deal()
-
特殊場景