【類和對象】
1.什麼是對象(object):
對象是指現實中的物體或實體
2.什麼是面向對象:
把一切看成對象(實例),讓對象和對象之間建立關聯關係
3.對象都有什麼特徵:
對象有很多的屬性(名詞)
對象有很多的行爲(動作或動詞)
對象 = 屬性 + 方法
一個對象的特徵稱爲“屬性”、一個對象的行爲稱爲“方法”
# 特徵的描述稱爲屬性,在代碼層面來看其實就是變量
# 方法實際就是函數,通過調用這些函數來完成某些工作
例如:
烏龜
從靜態特徵描述:綠色的、有四條腿、10kg重、有外殼、大嘴巴...
從動態的行爲描述:會爬、會跑、咬人、吃東西、睡覺....
4.什麼是類:class
擁有相同屬性和行爲的對象分爲一組,即爲一個類
類是用來描述對象的工具,用類可以創建同類對象
5.類的創建:
語法:
class 類名(繼承列表):
‘’‘類的文檔字符串’‘’
實例方法定義(類內的函數稱爲方法method)
類變量定義
類方法定義
靜態方法定義
作用:
創建一個類
用於描述此類對象的行爲和屬性
類用於創建此類的一個或多個對象(實例(Istance))
注意:
類名後面跟着的小括號,這跟調用函數是一樣的
在python中,類名約定用大寫字母開頭,函數約定用小寫字母開頭
調用對象裏的方法,使用點操作符(.)即可
6.類和對象:
類 | 對象 | 實例對象
(class)| (object)| (Instance Objects)
7.構造函數:
表達式:
類名([創建傳參列表])
作用:
創建這個類的實例對象,並返回此實例對象的引用關係
8.實例對象說明:
實例有自己的作用域和名字空間,可以爲該實例添加實例變量(屬性)
實例可以調用類方法和實例方法
實例可以訪問類變量和實例變量
9.實例方法
語法:
class 類名(繼承列表):
def 實例方法名(self,參數1,參數2,...):
'''實例方法的文檔字符串'''
語句塊
作用:
用於描述一個對象的行爲,讓此類型的全部對象都擁有相同的行爲
說明:
實例方法實質是函數,是定義在類內的函數
實例方法至少有一個形參,第一個形參代表調用這個方法的實例,一般命名爲‘self’
實例方法的調用語法:
實例.實例方法名(調用傳參)
或
類名.實例方法名(實例,調用傳參)
10.屬性(attribute)也叫實例變量
每個實例都可以有自己的變量,此變量稱爲實例變量(也叫屬性)
屬性的使用語法:
實例.屬性名
賦值規則:
首次爲屬性賦值則創建此屬性
再次爲屬性賦值則必變屬性的綁定關係
作用:
用來記錄對象自身的數據
11.刪除屬性:
用del語句可以刪除一個對象的實例變量
語法:
del 對象.實例變量名
示例:
>>> class Cat:
pass
>>> c1 = Cat()
>>> c1.color = '白色'
>>> print(c1.color)
白色
>>> del c1.color
>>> print(c1.color)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
print(c1.color)
AttributeError: 'Cat' object has no attribute 'color'
-------------------------------------------------------------------
12.初始化方法__init__:
作用:
對新創建的對象添加實例變量(屬性)或相應的資源
語法格式:
class 類名(繼承列表):
def __init__(self [,形參列表]):
語句塊
說明:
1.初始化方法名必須爲__init__不可改變
2.初始化方法會在構造函數創建實例後自動調用,且實例自身通過第一個參數self傳入__init__方法
3.構造函數的實參將通過__init__方法的形參列表傳入__init__方法中
4.初始化方法內部如果需要返回則只能返回None
示例:
# 此示例示意__init__方法的調用和添加實例變量
class Car:
def __init__(self,c,b,m):
self.color = c # 顏色
self.brand = b # 品牌
self.model = m # 型號
def run(self,speed):
print(self.color,'的',self.brand,self.model,
'正在以',speed,'公里/小時的速度行駛!')
def set_color(self,clr):
# 此方法用來修改車的顏色
self.color = clr
a4 = Car('紅色','奧迪','A4')
a4.run(120)
a4.set_color('黑色')
a4.run(180)
# a4.__init__('白色','Tesla','Model S') # 顯示調用
終端打印:
紅色 的 奧迪 A4 正在以 120 公里/小時的速度行駛!
黑色 的 奧迪 A4 正在以 180 公里/小時的速度行駛!
--------------------------------------------------------------
13.析構方法__del__(self):
語法:
class 類名(繼承列表):
def __del__(self):
語句塊
說明:
析構方法在對象銷燬時被自動調用
作用:
清理此對象所佔用的資源
建議:
python不建議在析構方法內做任何事情,因爲對象銷燬的時間難以確定
示例:
# 此示例示意__del__方法的用法
class Car:
def __init__(self,name):
self.name = name
print('汽車',name,'對象以創建')
def __del__(self):
print(self.name,'對象已銷燬')
c1 = Car('BYD E6')
# del c1
c1 = Car('BMW X5')
while 1: # 死循環,爲了阻止程序退出
pass
汽車 BYD E6 對象以創建
汽車 BMW X5 對象以創建
BYD E6 對象已銷燬
--------------------------------------------
14.預置實例屬性:
__dict__屬性:
此屬性綁定一個存儲此實例自身實例變量(屬性)的字典
鍵表示的是屬性名,值表示的是屬性相應的數據值
__class__屬性:
此屬性用來綁定創建此實例的類
作用:
可以藉助此屬性來訪問創建此實例的類
示例:
class Dog:
pass
dog1 = Dog()
dog2 = Dog()
dog3 = dog1.__class__() # 創建dog1的同類對象
>>> class Dog:
pass
>>> dog1 = Dog()
>>> dog1.__class__
<class '__main__.Dog'>
>>> dog1.__class__ is Dog
True
>>> L = [1,2,3,4]
>>> L.__class__
<class 'list'>
>>> dog2 = dog1.__class__() # 等同於dog1=Dog()
>>> dog2.__class__
<class '__main__.Dog'>
-------------------------------------------------------
15.面向對象的綜合示例:
有兩個人:張三、35歲/李四、8歲。行爲:教別人學東西(teach)、賺錢、借錢。
事情:張三教李四學python、李四教張三學跳皮筋、張三上班賺了1000元錢、李四向張三借了200元。
代碼如下:
# 此示例示意如何用面向對象的方式創建對象
# 並建立對象與對象之間的邏輯關係
class Human:
'''人類,用於描述人的行爲和屬性'''
def __init__(self,n,a):
self.name = n # 姓名
self.age = a # 年齡
self.money = 0 # 錢數爲0
def teach(self,other,skill):
print(self.name,'教',other.name,'學',skill)
def works(self,money):
self.money += money
print(self.name,'工作賺了',money,'元')
def borrow(self,other,money):
if other.money > money:
print(other.name,'借給',self.name,money,'元')
other.money -= money
self.money += money
else:
print(other.name,'沒錢借給',self.name)
def show_info(self):
print(self.age,'歲的',self.name,
'存有',self.money,'元錢')
# 以下是類的使用
zhang3 = Human('張三',35)
li4 = Human('李四',8)
zhang3.teach(li4,'python')
li4.teach(zhang3,'跳皮筋')
zhang3.works(1000)
li4.borrow(zhang3,200)
zhang3.show_info()
li4.show_info()
--------------------
張三 教 李四 學 python
李四 教 張三 學 跳皮筋
張三 工作賺了 1000 元
張三 借給 李四 200 元
35 歲的 張三 存有 800 元錢
8 歲的 李四 存有 200 元錢
--------------------------------------------
16.用於類的函數:
0.查看python內鍵類的繼承關係的函數:
help(__builtins__)
1.【isinstance(obj,class_or_tuple)返回這個對象obj是否某個類class或某些類的實例對象
如果是則返回True,否則返回False】
type(obj) 返回對象的類型
以下是小甲魚書本:
isinstance(obj,classinfo)
如果第一個參數(object)是第二個參數(classinfo)的實例對象,則返回True,否則返回False:
(1)如果object是classinfo的子類的一個實例,也符合條件
(2)如果第一個參數不是對象,則永遠返回False
(3)classinfo可以是類對象組成的元組,只要object是其中一個候選對象的實例,則返回True
(4)如果第二個參數不是類或者由類組成的元組,會拋出一個TypeError異常
示例:
>>> class A:
pass
>>> class B(A):
pass
>>> class C:
pass
>>> b1 = B()
>>> isinstance(b1,B)
True
>>> isinstance(b1,C)
False
>>> isinstance(b1,A)
True
>>> isinstance(b1,(A,B,C))
True
----------------------------------------------------------------------------
2.issubclass(cls,class_or_tuple)
判斷一個類是否繼承自其他的類,如果此類cls是class或tuple中的一個派生子類則返回True,否則返回False
小甲魚教程:
issbuclass(class,classinfo)
如果第一個參數(class)是第二個參數(classinfo)的一個子類,則返回True,否則返回False
(1)一個類被認爲是其自身的的子類
(2)classinfo可以是類對象組成的元組,只要class是其中任何一個候選類的子類,則返回True
(3)在其他情況下,會拋出一個TypeError異常
示例:
>>> class A:
pass
>>> class B(A):
pass
>>> issubclass(B,A)
True
>>> issubclass(B,B)
True
>>> issubclass(B,object)
True
>>> class C:
pass
>>> issubclass(B,C)
False
---------------------------------------------------------------------------------------
17.類變量class variable(也叫類屬性):
類變量是類的屬性,此屬性屬於類
作用:
用來記錄類的數據
說明:
類變量可以通過類直接訪問
類變量可以通過類的實例直接訪問
類變量可以通過此類的實例的__class__屬性間接訪問
示例:
# 此示例示意實例可以訪問類變量的
class Human:
count = 0 # 創建類變量
print('Human的類變量count=',Human.count)
h1 = Human()
print('用h1對象訪問Human的count變量',h1.count)
h1.count = 100 # 實例變量
print(h1.count)
print(Human.count)
h1.__class__.count += 1 # class函數返回的是這個類
print('h1.count=',h1.count)
print('Human.count=',Human.count)
Human的類變量count= 0
用h1對象訪問Human的count變量 0
100
0
h1.count= 100
Human.count= 1
------------------------------------------------
18.類的文檔字符串:
類內第一個沒有賦值給任何變量的字符串是類的文檔字符串
說明:
類的文檔字符串用類的__doc__屬性可以訪問
類的文檔字符串可用類的help()函數查看
19.類的__slots__列表:
作用:
限定一個類的實例只能有固定的屬性(實例)
通常爲防止錯寫屬性名而發生運行時錯誤
說明:
含有__slots__列表的類創建的實例對象沒有__dict__屬性
即此實例不用字典來保存對象的屬性(實例變量)
示例:
# 此示例示意類的變量 __slots__列表的作用:
class Student:
__slots__ = ['name','score']
# 以上用法表示只能有name,score兩個屬性,如果有其他屬性則報錯
def __init__(self,name,score):
self.name = name
self.score = score
s1 = Student('小張',58)
print(s1.score)
# 以下錯寫了屬性名,但在運行時不會報錯(把score寫錯了)
s1.socre = 100
print(s1.score)
----------------------------------
58
Traceback (most recent call last):
File "p0_1.py", line 60, in <module>
s1.socre = 100
AttributeError: 'Student' object has no attribute 'socre'
-------------------------------------------------------------
20.類方法:
類方法是描述類的行爲的方法,類方法屬於類
說明:
1.類方法需要用@classmethond裝飾器定義
2.類方法至少有一個形參,第一個形參用於綁定類,約定寫爲'cls'
3.類和該類的實例都可以調用類方法
4.類方法不能訪問此類創建的實例的屬性(只能訪問類變量)
實例:
class Car:
count = 0
@classmethod
def get_info(cls):
return cls.count
c1 = Car()
c1.count = 100
v = c1.get_info()
print(v) # 0,cls綁定的是類,不是對象,不會因對象的改變而改變
--------------------------------------------------------------
class Car:
count = 0 # 類變量
@classmethod # 把這個去掉就是以前的實例方法(括號是(self))
def getTotalCount(cls):
'''此方法爲類方法,
第一個參數爲cls,代表調用此方法的類'''
return cls.count
@classmethod
def undateCount(cls,number):
cls.count += number
print(Car.getTotalCount()) # 用類來調用類方法
# Car.count += 1 # 面向對象思想不提倡直接操作屬性
Car.undateCount(1)
print(Car.getTotalCount())
c1 = Car() # 創建一個對象
c1.undateCount(100) # Car類的實例也可以調用類方法
print(c1.getTotalCount())
# cls 表示 c1傳過去的c1__class__
在終端打印:
0
1
101
-----------------------------------------------------
21.靜態方法@starticmethod
0.問題:
1.類方法屬於類
2.實例方法屬於該類的實例
3.類內能不能有函數,這個函數不屬於類,也不屬於實例
1.靜態方法:
靜態方法不屬於類,也不屬於類的實例
它相當於定義在類內普通函數,只是它的作用域屬於類
2.示例:
# 此示例示意靜態方法的創建和使用
class A:
@staticmethod
def myadd(x,y):
'''此方法爲靜態方法
此方法的形參不需要傳入類或實例'''
return x + y
print('1+2=',A.myadd(1,2))
a = A()
print('100+200=',a.myadd(100,200))
在終端打印:
1+2= 3
100+200= 300
------------------------------------------
【22繼承(inheritance)和 派生(derived):】
1.什麼是繼承/派生:
1.繼承是指從已有的類中衍生出新類,新類具有原類的行爲,並能擴展新的行爲
2.派生就是從一個已有類中衍生(創建)新類在新類上可以添加新的屬性和行爲
2.繼承和派生的目的:
繼承是延續舊類的功能
派生是爲了在舊類的基礎上添加新的功能
3.作用:
1.用繼承派生機制,可以將一些共有功能加在基類中,實現代碼的共享
2.在不改變基類的基礎上改變原有的功能
4.繼承/派生的名詞:
被繼承的類成爲 ----> 基類(base class)、 超類(super class)、 父類(father class)
繼承者稱爲 ------ > 派生類(derived class)/ 子類(child class)
一個子類可以繼承它的父類的任何屬性和方法
注意:如果子類中定義與父類同名的方法或屬性,則會自動覆蓋父類對應的方法或屬性
5.單繼承:
語法:
class 類名(被繼承的類(基類)):
語句塊
說明:
單繼承是指派生類由一個基類衍生出來的類
6.繼承說明:
任何類都直接或間接的繼承自object類
object類是一切類的超類(祖類)
7.類的__base__屬性:
__base__屬性用來記錄此類的基類
示例:
>>> class Human:
pass
>>> class Student(Human):
pass
>>> class Teacher(Student):
pass
>>> Teacher.__base__
<class '__main__.Student'>
>>> Student.__base__
<class '__main__.Human'>
>>> Human.__base__
<class 'object'>
---------------------------------------------------------------
8.覆蓋override:
什麼叫覆蓋:
覆蓋是指在有繼承關係的類中,子類中實現了與基類同名的方法
在子類實例調用該方法時,實例調用的是子類中的覆蓋版本方法,這種現象叫做覆蓋
注意:
如果子類中定義與父類同名的方法或屬性,則會自動覆蓋父類對應的方法或屬性
示例:
class Human:
def say(self,that):
print('說:',that)
def hello(self):
print('hello xxx')
class Student(Human):
def hello(self):
print('xxx,I Love You!')
s1 = Student()
s1.say('心動的感覺~')
s1.hello()
說: 心動的感覺~
xxx,I Love You!
--------------------------------------------------------------------------
問題:
當覆蓋發生時,子類對象能否調用父類的方法
答案:
子類對象顯示調用基類方法的方式:
基類名.方法名(實例,實際調用傳參)
9.super函數:
super(type,obj) 返回綁定超類的實例
super() 返回綁定超類的實例,等同於:
super(__class__,實例方法的一個參數)(必須在方法內調用)
示例:
class A:
def work(self):
print('A.work()被調用')
class B(A):
def work(self):
print('B.work()被調用')
def super_work(self):
# 此處能調用父類的work方法嗎
# self.work() # B.work()
# super(B,self).work() # A.work()
# super(__class__,self).work() # A.work()
super().work() # A.work()
b = B()
# 調用父類的word方法
# b.__class__.__base__.work(b)
# 以上太麻煩
super(B,b).work() # 調用超類的方法
b.super_work()
-------------
A.work()被調用
A.work()被調用
-------------------------------------------------------------------
顯式調用基類的初始化方法:
當子類中實現了__init__方法時,基類的__init__方法並不會被自動調用,此時需要顯式調用
實例:
class Human:
def __init__(self,n,a):
'''此方法爲人的對象添加,姓名和年齡'''
self.name = n
self.age = a
def iffos(self):
print('姓名:',self.name)
print('年齡:',self.age)
class Student(Human):
def __init__(self,n,a,s=0):
super().__init__(n,a)
self.score = s
def iffos(self):
super().iffos()
print('成績:',self.score)
s1 = Student('小張',18,100)
s1.iffos()
姓名: 小張
年齡: 18
成績: 100
----------------------------------------------------------------
10.封裝:
封裝是指隱藏類的實現細節,讓使用者不用關心這些細節
封裝的目的是讓使用者儘可能少的實例變量(屬性)進行操作
11.公有和私有:
1.公有屬性:
默認對象的屬性和方法都是公開的,可以直接通過點操作符(·)進行訪問
示例:
>>> class Person:
name = '小甲魚'
>>> p = Person()
>>> p.name
'小甲魚'
2.私有屬性:
python類中,以雙下劃線'__'開頭,不以雙下劃線結尾的標識符爲私有成員,在類的外部無法直接訪問
在外部你使用“_類名__變量名”即可訪問雙下劃線開頭的私有變量了
示例:
class A:
def __init__(self):
self.__p1 = 100
def test(self):
print(self.__p1)
self.__m1() # 可以訪問,A類的方法可以訪問A類的私有變量
def __m1(self):
print('我是A類的__m1方法!')
a = A()
# print(a.__p1) 失敗,在類外看不到__p1屬性,訪問失敗
a.test()# 在外部的將變量名隱藏起來了,需要從內部進行訪問,可以訪問
# a.__m1() 出錯,無法調用私有變量
print(a._A__p1) # 可以
# 在外部你使用“_類名__變量名”即可訪問雙下劃線開頭的私有變量了
--------------------------------------------------------------
12.多態(polymorphic):
字面的意思是:“多種狀態”
多態是指在繼承/派生關係的類中,調用基類對象的方法,實際能調用子類的覆蓋版本方法的現象叫做多態
說明:
多態調用的方法與對象相關,不與類型相關
python的全部對象都只有“運行時狀態(動態)”,沒有“C++/java”裏的“編譯時狀態(靜態)”
示例:
class Shape:
def draw(self):
print('Shape.draw被調用')
class Point(Shape):
def draw(self):
print('正在畫一個點')
class Circle(Point):
def draw(self):
print('正在畫一個圈')
def my_draw(s):
s.draw() # 此處顯示出多態中的動態
s1 = Circle()
s2 = Point()
my_draw(s1) # 調用Circle裏的draw
my_draw(s2) # 調用Point裏的draw
正在畫一個圈
正在畫一個點
-----------------------------------------------------------------------------
13.面向對象編程語言的特徵:
1.需要有繼承機制
2.需要封裝
3.多態
如:C++/Java/Python/Swift/C#
14.多繼承(multiple inheritance):
多繼承是指一個子類繼承自兩個或兩個以上的基類,就是同時繼承多個父類的屬性和方法。
語法:
class 類名(父類名1、父類名2,...):
語句塊
說明:
1.一個子類同時繼承自多個父類,父類中的方法可以同時被繼承下來
2.如果兩個父類中有同名的方法,而在子類中又沒有覆蓋此方法時,調用結果難以確定
示例:
class Car:
def run(self,speed):
print('汽車以',speed,'公里/小時的速度行駛')
class Plane:
def fly(self,height):
print('飛機以海拔',height,'的高度飛行')
class PlaneCar(Car,Plane):
'''PlaneCar同時繼承汽車和飛機類'''
pass
p1 = PlaneCar()
p1.fly(10000)
p1.run(300)
--------------------------------------------------------------------------------
缺陷:
標識符(名字空間衝突的問題)
要謹慎使用多繼承
容易導致代碼混亂,有時會出現不可預見的BUG
示例:
class A:
def m(self):
print('A.m被調用')
class B:
def m(self):
print('B.m被調用')
class C(A,B):
pass
c = C()
c.m() # 請問調用誰?A.m---->(A,B)誰在前就調用誰
------------------------------------------------------------------------------
多繼承的MRO(Method Resolution Order)問題:
類內的__mro__屬性用來記錄繼承的方法的查找順序
示例:
# 此示例示意在多繼承中的對象方法查找順序問題
class A:
def m(self):
print('A.m')
class B:
def m(self):
print('B.m')
class C(A):
def m(self):
print('C.m')
class D(B,C):
def m1(self):
print('D.m')
d = D()
print(D.__mro__)
d.m()
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
<class '__main__.A'>, <class 'object'>)
B.m
-------------------------------------------------------------------------------
示例:
# 此示例示意在多繼承中的對象方法查找順序問題
class A:
def m(self):
print('A.m')
class B:
def m(self):
print('B.m')
super().m()
class C(A):
def m(self):
print('C.m')
class D(B,C):
def m(self):
print('D.m')
super().m()
d = D()
d.m()
在終端打印:(順序亂了,不是按照父類的來的)
D.m
B.m
C.m
<class '__main__.D'>
<class '__main__.B'>
<class '__main__.C'>
<class '__main__.A'>
<class 'object'>
-------------------------------------------------------------
15.函數重寫override:
重寫是指在自定義的類內添加相應的方法,讓自定義的類生成的對象(實例)像內鍵對象一樣進行內建的函數操作
對象轉字符串函數重寫:
repr(obj) 返回一個能代表此對象的表達式字符串,(這是個表達式、給電腦看的)通常:
eval(rper(obj)) == obj
str(obj) 通過給定的對象返回一個字符串(這個字符串通常是給人看的)
示例:
class MyNumber:
pass
# def __len__(self):
# return 100
n1 = MyNumber()
x = len(n1) # 重寫了__len__方法纔可以
print(x)
-----------------------------------------------------------------------------------------
>>> s = "I'm a Teacher"
>>> s1 = repr(s)
>>> s2 = str(s)
>>> print(s1)
"I'm a Teacher"
>>> print(s2)
I'm a Teacher
>>> s3 = eval(repr(s))
>>> print(s3)
I'm a Teacher
----------------------------------------------------------------------
對象轉字符串函數重寫方法:
repr() 函數的重寫方法:
def __repr__(self):
return 能夠表達self內容的字符串
str() 函數的重寫方法:
def __str__(self):
return 人能看懂的字符串
說明:
1.str(obj) 函數優先調用obj.__str__()方法返回字符串
2.如果obj沒有__str__()方法,則調用obj.__repr__()方法返回的字符串
3.如果obj沒有__repr__()方法,則調用object類的__repr__()實例方法顯示<xxxx>格式的字答鼓足
數值轉換函數的重寫:
def __complex__(self) complex(obj) 函數調用
def __int__(self) int(obj) 函數調用
def __float__(self) float(obj) 函數調用
def __bool__(self) bool(obj) 函數調用
示例:
# 此示意示例一個自定義的類能夠轉換成爲數值類型
class MyNumber:
def __init__(self,value):
self.date = value
def __repr__(self):
return 'MyNumber(%d)' % self.date
def __int__(self):
'''此方法用於int(obj)函數重載,必須返回整數
此方法通常用於制定自定義對象如何轉換爲整數的規則'''
return 10000
n1 = MyNumber(-100)
print(type(n1))
# if n1 == 100:
# print('n1 == 100')
n = int(n1)
print(type(n))
print(n)
-------------------------------------------------------------
內建函數的重寫:
__abs__ abs(obj)
__len__ len(obj)
__reversed__ reversed(obj)
__round__ round(obj)
示例:
# 此示例示意內建函數的重寫
# 自定義一個MyList類,與系統內建的類一樣,用來保存數據
class MyList:
'''自定義列表類'''
def __init__(self,iterator=[]):
self.date = [x for x in iterator]
def __repr__(self):
return 'MyList(%r)' % self.date
def __abs__(self):
# return MyList([abs(x) for x in self.date])
# 上一句語句可以用如下生成器表達式來代替,以防止佔用內存
return MyList((abs(x) for x in self.date))
def __len__(self):
return len(self.date)
myl = MyList([1,-2,3,-4])
print(myl)
print(abs(myl)) # 相當於myl.__abs__()
print('原來的列表是:',myl)
myl2 = MyList(range(10))
print(myl2)
print('myl2的長度是:',len(myl2))
print('myl的長度是:',len(myl))
在終端打印如下:
MyList([1, -2, 3, -4])
MyList([1, 2, 3, 4])
原來的列表是: MyList([1, -2, 3, -4])
MyList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
myl2的長度是: 10
myl的長度是: 4
-----------------------------------------------------------------
布爾測試函數的重寫
格式:
def __bool__(self):
...
作用:
用於bool(obj) 函數取值
用於if語句真值表達式中
用於while語句真值表達式中
說明:
1.優先調用__bool__方法取值
2.如果不存在__bool__方法,則用__len__()方法取值
後判斷是否爲零值,如果不爲零返回True,否則返回Fasle
3.如果沒有__len__方法,則直接返回True
16.迭代器(高級)
什麼是迭代器?
可以通過next(obj)函數取值的對象就是迭代器
迭代器協議:
迭代器協議是指對象能夠使用next函數獲取下一項數據
在沒有下一項數據時觸發一個StopIterator來終止迭代的約定
實現方法:
類內需要有__next__(self)方法來實現迭代器協議
語法形式:
class MyItorator:
def __next__self:
迭代器協議的實現
return 數據
什麼是可迭代對象?
是指能用iter(obj)函數返回迭代器的對象(實例)
可迭代對象內部一定要定義__iter__(self)方法來返回迭代器
(這個方法實際上就是返回迭代器本身)
可迭代對象的語法形式:
class MyIterable:
def __iter__(self):
語句塊
return 迭代器
示例:
# 此示例示意可迭代對象和迭代器的定義及使用方法
class MyList:
def __init__(self,iterator):
'''此方法創建一個date實例變量來綁定一個用來存儲數據的列表'''
self.date = list(iterator)
def __repr__(self):
'''此方法爲了打印此列表的數據'''
return 'MyList(%r)' % self.date
def __iter__(self):
'''有此方法就是可迭代對象,但要求必須返回迭代器'''
print('__iter__方法被調用!')
return MyListIterator(self.date)
class MyListIterator:
'''此類用來創建一個迭代器對象,
用此迭代器對象可以訪問MyList類型的數據'''
def __init__(self,iter_date):
self.cur = 0 # 設置迭代器的初始值爲0,代表列表的下標
self.it_date = iter_date
# 以上it_date綁定要迭代的列表
def __next__(self):
'''有此方法的對象才叫迭代器
此方法一定要實現迭代器協議'''
print('__next__方法被調用')
# 如果self.cur已經超出了列表的索引範圍就報迭代結束
if self.cur >= len(self.it_date):
raise StopIteration
# 否則尚未迭代完成,需要返回數據
r = self.it_date[self.cur] # 拿到要送回去的數
self.cur += 1 # 將當前值向後移動一個單位
return r
myl = MyList([2,3,5,7])
print(myl)
for x in myl:
print(x)
在終端打印:
MyList([2, 3, 5, 7])
__iter__方法被調用!
__next__方法被調用
2
__next__方法被調用
3
__next__方法被調用
5
__next__方法被調用
7
__next__方法被調用
-------------------------------------------------------------
17.異常(高級):
回顧異常相關的語句:
try- except 用來捕獲異常通知
try - finally 用來做一定要做的事情
raise 用來發生異常通知
assert 用來根據條件來發出AssertionError類型的異常通知
with語句:
語法:
with 表達式1 [as 變量1],表達式2 [as 變量2]:
語句塊
作用:
使用於對資源進行訪問的場合,確保使用過程中不管是否發生異常,都會執行必須的'清理'操作,並釋放資源
如文件使用後自動關閉,線程中鎖的自動獲取和釋放等
對文件操作使用with語句,將大大減少代碼量,而且你再也不用擔心出現文件打開了忘記關閉的問題
(with語句會自動幫你關閉文件)
說明:
能夠用於with語句進行管理的對象必須是環境管理器
示例:(見異常文檔)
18.環境管理器:
1.類內有__enter__和__exit__實例方法的類被稱爲環境管理器
2.夠用with語句管理的對象必須是環境管理器
3.__enter__方法將在進入with語句時被調用,並返回由as變量管理的對象
4.__exit__將在離開with語句時被調用,且可以用參數來判斷在離開whit語句時是否有異常發生並做出相應的處理
示例:
# 此示例示意環境管理器的定義及使用
class A:
def __enter__(self):
print('已進入with語句')
return self # 返回的對象將由as綁定
def __exit__(self,exc_type,exc_val,exc_tb):
print('已離開with語句')
with A() as a:
print('這是with語句內的一條語句')
int(input('請輸入整數:'))
(不管有沒有報錯,都會離開with語句)
在終端打印:
已進入with語句
這是with語句內的一條語句
請輸入整數:100
已離開with語句
tarena@tedu:~/python$ python3 p0_1.py
已進入with語句
這是with語句內的一條語句
請輸入整數:abcd
已離開with語句
Traceback (most recent call last):
File "p0_1.py", line 38, in <module>
int(input('請輸入整數:'))
ValueError: invalid literal for int() with base 10: 'abcd'
-------------------------------------------------------------------------
__exit__的三個參數:
'''此方法會在退出with語句時自動調用
exc_type在沒有異常時爲None,在出現異常時爲異常類型
exc_val在沒有異常時爲None,在出現異常時綁定錯誤對象
exc_tb在沒有異常時爲None,在出現異常時綁定traceback對象
'''
示例:
# 此示例示意環境管理器三個參數的用法
class A:
def __enter__(self):
print('已進入with語句')
return self # 返回的對象將由as綁定
def __exit__(self,exc_type,exc_val,exc_tb):
'''此方法會在退出with語句時自動調用
exc_type在沒有異常時爲None,在出現異常時爲異常類型
exc_val在沒有異常時爲None,在出現異常時綁定錯誤對象
exc_tb在沒有異常時爲None,在出現異常時綁定traceback對象
'''
if exc_type is None:
print('已離開with語句,離開時狀態正常')
else:
print('已離開with語句,離開時狀態異常')
print('異常類型是:',exc_type)
print('錯誤對象是',exc_val)
print('traceback對象是:',exc_tb)
with A() as a:
print('這是with語句內的一條語句')
int(input('請輸入整數:'))
在終端打印如下:
已進入with語句
這是with語句內的一條語句
請輸入整數:abcd
已離開with語句,離開時狀態異常
異常類型是: <class 'ValueError'>
錯誤對象是 invalid literal for int() with base 10: 'abcd'
traceback對象是: <traceback object at 0x7f2116ee2a48>
Traceback (most recent call last):
File "p0_1.py", line 48, in <module>
int(input('請輸入整數:'))
ValueError: invalid literal for int() with base 10: 'abcd'
------------------------------------------------------------------------
19.用於類的函數:
對象的屬性管理函數
示例(用這個例子)
>>> class C:
def __init__(self,x = 0):
self.x = x
>>> c1 = C()
函數
【getattr(obj, name[, default]) 】
返回對象指定的屬性值
從一個對象得到對象的屬性;getattr(x, 'y') 等同於x.y;
當屬性不存在時,如果給出default參數,則返回default
如果沒有給出default 則產生一個AttributeError錯誤
示例:
>>> getattr(c1,'x')
0
>>> getattr(c1,'y')
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
getattr(c1,'y')
AttributeError: 'C' object has no attribute 'y'
>>> getattr(c1,'y','您所訪問的屬性不存在')
'您所訪問的屬性不存在'
【hasattr(obj, name)】
用給定的name返回對象obj是否有此屬性,此種做法可以避免在getattr(obj, name)時引發錯誤
作用就是測試一個對象裏是否有指定的屬性
第一個參數(object)是對象,第二個參數(name)是屬性名(屬性的字符串名字)
示例:
>>> class C:
def __init__(self,x = 0):
self.x = x
>>> c1 = C()
>>> hasattr(c1,'x') # 注意,屬性名要用引號括起來
True
【setattr(obj, name, value) 】
給對象obj的名爲name的屬性設置相應的值value, set(x, 'y', v) 等同於 x.y = v
可以設置對象中指定屬性的值,如果指定的屬性不存在,則會新建屬性並賦值
示例:
>>> setattr(c1,'y','FishC')
>>> getattr(c1,'y')
'FishC'
【delattr(obj, name)】
刪除對象obj中的name屬性, delattr(x, 'y') 等同於 del x.y
刪除對象指定的屬性,如果屬性不存在,則拋出AttributeError異常
示例:
>>> delattr(c1,'y')
>>> delattr(c1,'z')
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
delattr(c1,'z')
AttributeError: z
20.運算符重載:
讓自定義的類生成的對象(實例)能夠使用運算符進行操作
作用:
讓自定義的類的實例像內鍵對象一樣進行運算符操作
讓程序簡潔易讀
對自定義的對象,將運算符賦予新的運算規則
算術運算符的重載:
__add__(self,other) 加法
__sub__(self,other) 減法
__mul__(self,other) 乘法
__truediv__(self,other) 真除法/
__floordiv__(self,other) 整數除法//(地板除)
__mod__(self,other) 取模算法%(求餘)
__divmod__(self,other) 定義當被divmod()調用時的行爲
__pow__(self,other[,modulo]) 定義當被power()調用或**運算時的行爲
__lshift__(self,other) 定義按位左移位的行爲:<<
__rshift__(self,other) 定義按位右移位的行爲:>>
__and__(self,other) 定義按位與操作的行爲:&
__xor__(self,other) 定義按位異或操作的行爲:^
__or__(self,other) 定義按位或操作的行爲:|
示例:
>>> class Try_int(int):
def __add__(self,other):
return self + other
def __sub__(self,other):
return self - other
>>> a = Try_int(1)
>>> b = Try_int(3)
>>> a + b
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
a + b
File "<pyshell#5>", line 3, in __add__
return self + other
File "<pyshell#5>", line 3, in __add__
return self + other
File "<pyshell#5>", line 3, in __add__
return self + other
[Previous line repeated 327 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object
(這樣做是錯的,當對象涉及加法操作時,自動調用魔法方法__add__()
但上面寫的是return self + other,也就是返回對象本身加另外一個對象,
這不就又自動觸發調用__add__方法了嗎,這樣就形成了無限遞歸。)
------------------------------------------------------------------------------------------
>>> class New_int(int):
def __add__(self,other):
return int(self) + int(other)
def __sub__(self,other):
return int(self) - int(other)
>>> a = New_int(1)
>>> b = New_int (3)
>>> a + b
4
>>> a - b
-2
(當對象進行了相關的算術運算,自然而然的就會自動觸發對應的魔法方法)
-----------------------------------------------------------------------------------------------
示例:
class MyList:
def __init__(self,iterable):
self.date = list(iterable)
def __add__(self,other): # other代表L2(第二個參數)
return MyList(self.date + other.date)
def __repr__(self):
return 'MyList(%r)' % self.date
def __mul__(self,other): # other綁定整數
return MyList(self.date * other)
L1 = MyList([1,2,3])
L2 = MyList([4,5,6])
L3 = L1 + L2 # 等同於L1.__add__(L2)
print(L3)
L4 = L2 + L1 # 等同於L2.__add__(L1)
print(L4)
L5 = L1 * 2
print(L5)
在終端打印:
MyList([1, 2, 3, 4, 5, 6])
MyList([4, 5, 6, 1, 2, 3])
MyList([1, 2, 3, 1, 2, 3])
----------------------------------------------------------------------------------------
21.反運算
反向運算符的重載
當左手邊的類型爲內鍵類型,右手邊爲自定義類型時,要實現運算必須用下方法重載
__radd__(self,other) 加法(當左操作數不支持相應的操作時被調用)
__rsub__(self,other) 減法(當左操作數不支持相應的操作時被調用)
__rmul__(self,other) 乘法(當左操作數不支持相應的操作時被調用)
__rtruediv__(self,other) 真除法/(當左操作數不支持相應的操作時被調用)
__rfloordiv__(self,other) 整數除法//(地板除)(當左操作數不支持相應的操作時被調用)
__rmod__(self,other) 取模算法%(求餘)(當左操作數不支持相應的操作時被調用)
__rdivmod__(self,other) 定義當被divmod()調用時的行爲(當左操作數不支持相應的操作時被調用)
__rpow__(self,other[,modulo]) 定義當被power()調用或**運算時的行爲(當左操作數不支持相應的操作時被調用)
__rlshift__(self,other) 定義按位左移位的行爲:<<(當左操作數不支持相應的操作時被調用)
__rrshift__(self,other) 定義按位右移位的行爲:>>(當左操作數不支持相應的操作時被調用)
__rand__(self,other) 定義按位與操作的行爲:&(當左操作數不支持相應的操作時被調用)
__rxor__(self,other) 定義按位異或操作的行爲:^(當左操作數不支持相應的操作時被調用)
__ror__(self,other) 定義按位或操作的行爲:|(當左操作數不支持相應的操作時被調用)
以上用法是other --- self
示例:
def __rmul__(self,other):
print('__rmul__被調用')
return MyList(self.date * other)
L6 = 2 * L1 # 此時調用__rmul__方法
print(L6)
--------------------------------------------------------------------------------------
22.複合賦值算術運算符的重載:
__iadd__(self,other) 加法 self+=other
__isub__(self,other) 減法 self-=other
__imul__(self,other) 乘法 self*=other
__itruediv__(self,other) 真除法/ self/=other
__ifloordiv__(self,other) 整數除法//(地板除) self//=other
__imod__(self,other) 取模算法%(求餘) self%=other
__idivmod__(self,other) 定義當被divmod()調用時的行爲
__ipow__(self,other[,modulo]) 定義當被power()調用或**運算時的行爲 self**=other
示例:
class MyList:
def __init__(self,iterable):
self.date = list(iterable)
def __add__(self,other): # other代表L2(第二個參數)
return MyList(self.date + other.date)
print('~~~~~~~')
def __repr__(self):
return 'MyList(%r)' % self.date
def __iadd__(self,other):
print('__iadd__被調用!!!!')
self.date.extend(other.date)
return self
L1 = MyList([1,2,3])
L2 = MyList([4,5,6])
L1 += L2 # 當沒有__iadd__方法時,等同於調用L1 = L1 + L2,當有__iadd__時,優先調用iadd
print(L1)
在終端打印:
__iadd__被調用!!!!
MyList([1, 2, 3, 4, 5, 6])
------------------------------------------------------------------------------------------
23.比較運算符的重載:
__lt__(self,rhs) self < rhs 小於
__le__(self,rhs) self <= rhs 小於等於
__gt__(self,rhs) self > rhs 大於
__ge__(self,rhs) self >= rhs 大於等於
__eq__(self,rhs) self == rhs 等於
__ne__(self,rhs) self != rhs 不等於
注意:
比較運算符通常返回True或False
24.一元操作符:
一元操作符就是隻有一個操作數的意思。像a+b這樣加號左右有a,b兩個操作數,叫做二元操作數
python支持的一元操作符主要有:
__neg__(self)表示負號行爲
__pos__(self)定義正號行爲
__abs__(self)定義當被abs()調用時的行爲,就是取絕對值的意思
__invert__(self)定義按位取反的行爲
重載方法:
class 類名:
def __xxx__(self):
...
注意:
運算符重載不能改變運算符的優先級
示例:
class MyList:
def __init__(self,iterable):
print('__init__被調用')
self.date = list(iterable)
def __repr__(self):
return 'MyList(%r)' % self.date
def __neg__(self):
L = [-x for x in self.date]
return MyList(L)
L1 = MyList([1,-2,3,-4])
L2 = -L1
print(L2)
在終端打印:
__init__被調用
__init__被調用
MyList([-1, 2, -3, 4])
------------------------------------------------------------------
25.駝峯命名法:
python類名最好用駝峯命名法:
MyList MyRange 大駝峯(所有單詞首字母大寫,其餘小寫)
getStudentAge 小駝峯(第一個單詞首字母小寫,其他首字母大寫)
26.in/not in 運算符的重載:
重載方法:
def __contains__(self,e):
...
相當於e in self 成員運算
示例:
class MyList:
def __init__(self,iterable):
print('__init__被調用')
self.date = list(iterable)
def __repr__(self):
return 'MyList(%r)' % self.date
def __contains__(self,e):
'''此方法用來實現in / not in 運算符的重載'''
print('__contains__被調用')
for x in self.date:
if x == e:
return True
return False
L1 = MyList([1,-2,3,-4])
if -2 in L1:
print('-2 在 L1 中')
else:
print('-2 不在 L1 中')
if -3 not in L1:
print('-3 不在 L1 中')
else:
print('-3 不在 L1 中')
在終端打印:
__init__被調用
__contains__被調用
-2 在 L1 中
__contains__被調用
-3 不在 L1 中
--------------------------------------------------------
27.索引和切片運算符的重載:
__getitem__(self,i) x = self[i] 索引/切片取值
__setitem__(self,i,v) self[i] = v 索引/切片賦值
__delitem__(self,i) del self[i] del語句刪除索引等
作用:
讓自定義的類型的對象能夠支持索引和切片操作
示例:
# 此示例示意[]運算符的重載
class MyList:
def __init__(self,iterable):
print('__init__被調用')
self.date = list(iterable)
def __repr__(self):
return 'MyList(%r)' % self.date
def __getitem__(self,i):
print('__getitem__被調用,i = ',i)
return self.date[i]
def __setitem__(self,i,v):
print('__setitem__被調用,i =',i,'v = ',v)
self.date[i] = v
L1 = MyList([1,-2,3,-4])
v = L1[-1]
print(v)
L1[1] = 2
print(L1)
__init__被調用
__getitem__被調用,i = -1
-4
__setitem__被調用,i = 1 v = 2
MyList([1, 2, 3, -4])
------------------------------------------
28.slice函數:
作用:
用於創建一個Slice切片對象
此對象存儲一個節片的起始值,終止值和步長信息
slice(start,stop=None,step=None) 創建一個切片對象
slice的對象的屬性:
s.start 切片起始值,默認爲None
s.stop 切片終止值, 默認爲None
s.step 切片步長, 默認爲None
示例:
# 此示例示意[]運算符的重載
class MyList:
def __init__(self,iterable):
print('__init__被調用')
self.date = list(iterable)
def __repr__(self):
return 'MyList(%r)' % self.date
def __getitem__(self,i):
print('__getitem__被調用,i = ',i)
if type(i) is int:
print('正在做索引操作')
elif type(i) is slice:
print('正在做切片操作')
print('切片的起始值',i.start)
print('切片的終止值',i.stop)
print('切片的步長',i.step)
else:
raise KeyError
return self.date[i]
L1 = MyList([1,-2,3,-4,5,-6])
print(L1[::2]) # 等同於調用L1[slice(None,None,2)]
在終端打印:
__init__被調用
__getitem__被調用,i = slice(None, None, 2)
正在做切片操作
切片的起始值 None
切片的終止值 None
切片的步長 2
[1, 3, 5]
--------------------------------------------------------------