類和對象

【類和對象】

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]
--------------------------------------------------------------
發佈了103 篇原創文章 · 獲贊 2 · 訪問量 6796
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章