1.面向對象概述
1.類是用來描述對象的工具,把擁有相同屬性和行為的對象分為一組
2.對象是由類實例化出來的一個具體的對象
屬性: 對象擁有的名詞,用變量表示
行為: 對象擁有的動作,用方法表示
3.面向對象是把一切看成對象,對象和對象之間用方法建立關聯關系
4.面向過程是一件事怎么一步一步實現,面向對象是一件事情誰去實現
5.可以用類創建一個或多個此類的對象,類中的變量和方法能被此類所創建的所有對象所共同擁有
6.面向對象編程語言的特征封裝,繼承,多態
7.類和對象流程圖: https://www.processon.com/view/link/5ee0e5b5f346fb1ae558f314

類 對象 屬性 方法 車 BYD E6(京A.88888) 行駛 狗 小京巴 戶籍號:00001 喝水
2.類的概述
1.類的創建語法
class 類名(繼承列表): """類文檔字符串""" 實例方法(methd) 的定義 類變量(classVariable) 的定義 類方法(@classmethod) 的定義 靜態方法(@staticmethod) 的定義
2.類名必須是標識符,即由數字字母下划線組成且不能以數字開頭和不能是關鍵字,建議用大駝峰命名法,如: WebServer
3.類名實質上就是變量,它綁定一個類實例,屬性是這類事務具有什么樣的特征,方法是這類事務具有什么樣的行為
4.在Python2中不指定繼承object會定義經典類,Python3中無論是否制定繼承object,都會創建新式類

# Python2中不指定繼承object會創建經典類 class A: pass # Python2中定義新式類 class B(object): pass # Python3中無論是否制定繼承object,都會創建新式類 class C: pass
5.使用help(__builtins__)可以查看Python中所有的類的繼承派生關系

# 查看類的繼承派生關系 help(__builtins__)
3.實例與實例化概述
實例概述:
1.實例就是有類誕生出的一個對象,實例有自己的作用域或名字空間,可以為該實例添加實例變量
2.同一個類實例化出的對象是不同的個體,實例可以調用類的方法和訪問類中的類變量
實例化概述:
1.實例化就是類創建一個對象,並返回此實例對象的引用關系的過程
2.語法: obj = 類名([創建傳參列表]) # 創建這個類的實例化對象,並返回此實例對象的引用關系 []里的形參可省略

class Dog: # 定義一個狗類 pass dog1 = Dog() # 用類來創建一個對象用dog1綁定 print(id(dog1)) # 打印dog1所在的內存地址 dog2 = Dog() # 創建第二個對象 用dog2綁定 print(id(dog2)) # 判斷兩只示例化出來的狗對象是不是同一條狗 print(dog1 is dog2) # False
4.類的組成成員: 字段,方法,靜態屬性
1.公有靜態字段: 屬於類在內存中只保存一份,類和實例都可以訪問,類內部和派生類中也可以訪問
2.私有靜態字段: 屬於類在內存中只保存一份,僅類內部可以訪問
3.公有普通字段: 屬於對象在每個對象中都要保存一份,實例可以訪問,類內部和派生類中也可以訪問
4.私有普通字段: 屬於對象在每個對象中都要保存一份,僅類內部可以訪問
5.實例方法: 屬於類在內存中只保存一份,由對象調用,至少一個self參數,實例可以訪問,類內部和派生類中也可以訪問
6.私有方法: 屬於類在內存中只保存一份,僅類內部可以訪問,命名以雙下划線開頭,沒有參數
7.類方法: 屬於類在內存中只保存一份,由類調用,至少一個cls參數,執行類方法時,自動將調用該方法的類賦值給cls
8.靜態方法: 屬於類在內存中只保存一份,由類調用,無默認參數
9.靜態屬性: 屬於類在內存中只保存一份,實例可以訪問,類內部和派生類中也可以訪問

class ClassMember: """此示例闡述類的組成成員""" company_name = "x公司" # 公有靜態字段(公有靜態變量,類變量),類和實例都可以訪問,類內部和派生類中也可以訪問 __iphone = "135xxxxxxxx" # 私有靜態字段(私有靜態變量,私有類變量),僅類內部可以訪問 def __init__(self, name, age): # 初始化方法 self.name = name # 公有普通字段(實例屬性),實例可以訪問,類內部可以訪問,派生類中可以訪問 self.__age = age # 私有普通字段(私有實例屬性),僅類內部可以訪問 def func1(self): # 實例方法,至少有一個self參數,實例可以訪問,類內部可以訪問,派生類中可以訪問 pass def __func(self): # 私有方法,僅類內部可以訪問 print(666) @classmethod # 類方法 def class_func(cls): """定義類方法,至少有一個cls參數""" print("類方法") @staticmethod # 靜態方法 def static_func(): """定義靜態方法,無默認參數""" print("靜態方法") @property # 靜態屬性,將方法偽裝成屬性來使用 def prop(self): pass
5.公有靜態字段與私有靜態字段
1.公有靜態字段概述(公有靜態變量,公有類變量)
1.類變量類可以通過類名.來訪問,類內部和派生類中也可以訪問
2.類變量是類的屬性,此屬性屬於類,不屬於此類創建的實例
3.類變量可以通過此類實例化對象訪問,可以通過實例的 '__class__'屬性間接修改

class Human: total_count = 0 # 類變量, 用於記錄對象的個數 print(Human.total_count) h1 = Human() print(h1.total_count) # 0 # 不會出錯 Human.total_count = 1 # 修改類變量 h1.total_count = 2 # 添加了自己的實例屬性total_count h1.__class__.total_count = 3 # 間接修改類變量

class Human: total_count = 0 # 類變量,用於記錄對象的個數 def __init__(self, name): self.name = name self.__class__.total_count += 1 # 人數加1 print(name, "對象創建") def __del__(self): self.__class__.total_count -= 1 # 總人數減1 print("當前對象的個數是:", Human.total_count) # 0 h1 = Human("張飛") h2 = Human("趙雲") print("當前對象的個數是:", Human.total_count) # 2 del h2 # 或 h2 = None print("當前對象的個數是:", Human.total_count) # 1

class C: name = "公有靜態字段" def func(self): print(C.name) class D(C): def show(self): print(C.name) C.name # 類訪問 obj = C() obj.func() # 類內部可以訪問 obj_son = D() obj_son.show() # 派生類中可以訪問
2.私有靜態字段(私有靜態變量,私有類變量)
1.用戶在創建類是可以在屬性名前加雙下划線來聲明該屬性只屬於類所有,僅類內部可以訪問
2.私有類變量不能被外界的實例對象直接訪問,也不可以通過該類直接訪問,不可在派生類中訪問
3.私有類變量可以通過_類名__變量名來間接訪問

class C: __name = "私有靜態字段" def func(self): print(C.__name) class D(C): def show(self): print(C.__name) # C.__name # 不可在外部訪問 obj = C() # obj.__name # 不可在外部訪問 obj._C__name # 可以通過_類名__屬性名來間接訪問 obj.func() # 類內部可以訪問 obj_son = D() obj_son.show() # 不可在派生類中訪問
3.變量的賦值規則: 首次為變量賦值則創建此變量,再次為變量賦值則改變變量的綁定關系
4.在定義變量時如果不確定設置什么初始值,可以設置為None
5.刪除變量del語句,語法: del 類名.變量名
6.類名.變量查找順序: 先從本類空間找,如果找不到,再從父類找...
7.可以通過類的__dict__方法查看類的所有屬性所在的字典
8.可以通過dir函數查看類的所有屬性和方法
6.公有普通字段與私有普通字段
1.公有普通字段概述(公有實例屬性,公有實例變量)
1.每個實例可以有自己的屬性,實例屬性的使用語法: 實例.屬性名
2.實例屬性: 對象可以訪問,類內部和派生類中也可以訪問

class C: def __init__(self): self.foo = "公有普通字段" def func(self): print(self.foo) # 類內部訪問 class D(C): def show(self): print(self.foo) # 派生類中訪問 obj = C() obj.foo # 通過對象訪問 obj.func() # 類內部訪問 obj_son = D() obj_son.show() # 派生類中訪問

class Dog: def __init__(self): self.name = None self.color = None dog1 = Dog() dog1.kinds = "京巴" # 為dog1綁定的實例添加kinds屬性 dog1.color = "白色" # 添加屬性 print(dog1.kinds, dog1.color) # 訪問屬性 dog1.color = '黃色' # 修改dog1.color 的綁定關系 print(dog1.color)
2.私有普通字段概述(私有實例屬性,私有實例變量)
1.每個實例可以有自己的私有屬性,稱為私有實例屬性或私有實例變量,使用語法: 實例.類中的引用方法名
2.私有實例屬性: 僅類內部可以訪問,但可以通過_類名__屬性名來間接訪問

class C: def __init__(self): self.__foo = "私有字段" def func(self): print(self.foo) # 類內部訪問 class D(C): def show(self): print(self.foo) # 派生類中訪問 obj = C() obj.__foo # 通過對象訪問會報錯 obj.func() # 類內部可以訪問 obj_son = D() obj_son.show() # 派生類中訪問會報錯

class Women: def __init__(self, name): self.name = name self.__age = 18 def secret(self): print("%s的年齡是%d" % (self.name, self.__age)) xiaomei = Women("小美") # print(xiaomei.__age) # 私有屬性不能被外界的實例對象直接訪問 xiaomei.secret() # 在對象內部的方法可以直接訪問對象的私有屬性 print(xiaomei._Women__age) # 私有屬性可以通過_類名__屬性名來間接訪問 xiaomei.__dict__ # {'name': '小美', '_Women__age': 18} dir(xiaomei)[0:3] # ['_Women__age', '__class__', '__delattr__']
3.屬性的賦值規則: 首次為屬性賦值則創建此屬性,再次為屬性賦值則改變屬性的綁定關系
4.在定義屬性時如果不確定設置什么初始值,可以設置為None
5.刪除屬性del語句,語法: del 對象.屬性名

class Student: pass stu = Student() stu.name = 'xiaozhang' # 創建屬性 print(stu.name) del stu.name # 刪除此屬性 print(stu.name) # 屬性錯誤,因為屬性已經不存在了
6.對象.屬性查找順序: 先從對象空間找,如果找不到,再從類空間找,再找不到,再從父類找...
7.可以通過對象的__dict__方法查看對象所有屬性所在的字典
8.可以通過dir函數查看對象的所有屬性和方法
7.實例方法與私有方法
1.實例方法概述
1.實例方法的實質是函數,是定義在類內的函數,用於描述一個對象的行為,讓此類型的全部對象都擁有相同的行為
2.實例方法至少有一個self參數代表調用這個方法的實例,聲明該方法是實例方法
3.實例方法對象可以訪問,類內部和派生類中也可以訪問
4.實例方法的調用語法:
實例.實例方法名(調用參數)
類名.實例方法名(實例, 調用參數)

class C: def __init__(self): pass def add(self): print("this is add") class D(C): def show(self): print("this is show") def func(self): self.show() obj = D() obj.show() # 通過對象訪問 obj.func() # 類內部訪問 obj.add() # 派生類中訪問 D.func(obj) # 類名調用
2.私有方法概述
1.用戶在創建類是可以在方法名前加雙下划線(__方法名)來聲明該屬性只屬於類所有
2.私有方法不能被外界的實例對象直接訪問
3.用戶自定義的私有方法可以通過_類名__方法名來間接訪問

class Women: def __init__(self, name): self.name = name self.__age = 18 def __secret(self): print("%s的年齡是%d" % (self.name, self.__age)) xiaomei = Women("小美") # xiaomei.__secret() # 私有方法不能被外界的實例對象直接訪問 xiaomei._Women__secret() # 私有方法可以通過_類名__方法名來間接訪問 xiaomei.__dict__ # {'name': '小美', '_Women__age': 18} dir(xiaomei)[0:3] # ['_Women__age', '_Women__secret', '__class__']
3.刪除方法del語句,語法: del 類名.方法名
4.類名.方法查找順序: 先從本類空間找,如果找不到,再從父類找...
5.可以通過對象的__dict__方法查看對象所有屬性所在的字典
6.可以通過dir函數查看對象的所有屬性和方法
8.類方法與靜態方法和靜態屬性(@classmethod, @staticmethod, @property)
1.類方法 classmethod
類方法概述:
1.類方法是操作類的方法,類方法屬於類,不屬於該類創建的對象
2.類方法需要使用 @classmethod 裝飾器定義,第一個參數用來綁定類,約定寫為cls
3.類和對象實例都可以調用類方法,類方法不能訪問此類創建的對象的屬性
4.類方法不用對象命名空間中的內容,而是用到了類命名空間中的變量(靜態屬性),或者類方法,靜態方法

class A: v = 0 # 類變量 @classmethod def get_v(cls): # 此方法是類方法 return cls.v a = A() a.get_v() # 0 A.get_v() # 0 a.__dict__ # {}

class Goods: __discount = 0.8 def __init__(self, price): self.__price = price @property def price(self): return self.__price * Goods.__discount @classmethod def change_discount(cls, num): cls.__discount = num # 商場的程序 apple = Goods(10) banana = Goods(15) print(apple.price,banana.price) Goods.change_discount(1) print(apple.price,banana.price)
2.靜態方法 staticmethod
靜態方法概述:
1.靜態方法是定義在類的內部的函數,此函數作用域是類的內部
2.靜態方法需要使用@staticmethod 裝飾器定義,定義上與普通函數的定義相同,不需要傳入self和cls
3.靜態方法只能憑借該類和實例來調用,不能訪問類變量和實例變量
4.如果一個類里面的方法既不需要用到self中的資源,也不用cls中的資源,就將這個方法定義成一個靜態方法

class A: @staticmethod def myadd(a, b): # 此方法是靜態方法 return a + b print(A.myadd(100, 200)) # 300 a = A() print(a.myadd(300, 400)) # 300

# 裝飾登錄功能 class Person: @staticmethod def login(): # 動詞 動作 屬於某一個對象 pass class Student(Person): pass class Manager(Person): pass class Course: pass class Classes: pass
3.靜態屬性 property
靜態屬性概述:
1.將方法偽裝成一個靜態屬性,代碼上沒有什么提升只是更合理,靜態屬性本質就是實現了 get,set,delete 三種方法
2.靜態屬性需要使用 @property 裝飾器定義,只和實例對象綁定既可以訪問實例屬性也可以訪問類的屬性
3.property的構造方法中有個四個參數
1.第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
2.第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
3.第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法
4.第四個參數是字符串,調用 對象.屬性.__doc__ 時此參數是該屬性的描述信息
4.@方法名.setter裝飾器:
修改property裝飾的屬性時會被調用這個裝飾器裝飾的方法
除了self外還有一個參數是被修改的值
5.@方法名.deleter裝飾器:
當要刪除被property裝飾的屬性時會被調用這個裝飾器的方法
實際不會刪除被property裝飾的方法,而是在執行'del 類名.屬性'刪除操作時執行這個裝飾器的方法
6.定義property屬性有兩種方式: 在方法上應用裝飾器和在類中定義值為property對象的類屬性

# property屬性定義方式: 類中定義值為property對象的類屬性 class Foo: def get_bar(self): return 'Tom' # 必須兩個參數 def set_bar(self, value): return return 'set value' + value def del_bar(self): return 'Tom' BAR = property(get_bar, set_bar, del_bar, 'description') obj = Foo() obj.BAR # 自動調用第一個參數中定義的方法: get_bar obj.BAR = "Cat" # 自動調用第二個參數中定義的方法: set_bar方法,並將"Cat"當作參數傳入 del Foo.BAR # 自動調用第三個參數中定義的方法: del_bar方法 obj.BAE.__doc__ # 自動獲取第四個參數中設置的值: description

# property屬性定義方式: 在方法上應用裝飾器 class Person: def __init__(self, name, age): self.name = name if type(age) is int: self.__age = age else: print('你輸入的年齡的類型有誤,請輸入數字') @property def age(self): return self.__age @age.setter def age(self, a1): '''判斷,你修改的年齡必須是數字''' if type(a1) is int: self.__age = a1 else: print('你輸入的年齡的類型有誤,請輸入數字') @age.deleter def age(self): del self.__age p1 = Person('帥哥', 20) print(p1.age) # 20 print(p1.__dict__) # {'name': '帥哥', '_Person__age': 20} p1.age = 23 print(p1.age) # 23 del p1.age

# property屬性定義方式: 在方法上應用裝飾器 class Money(object): def __init__(self): self.__money = 0 # 使用裝飾器對money進行裝飾,那么會自動添加一個叫money的屬性,當調用獲取money的值時調用裝飾的方法 @property def money(self): return self.__money # 使用裝飾器對money進行裝飾,當對money設置值時,調用裝飾的方法 @money.setter def money(self, value): if isinstance(value, int): self.__money = value else: print("error:不是整型數字") a = Money() a.money = 100 print(a.money) # property屬性定義方式: 在方法上應用裝飾器 class Money(object): def __init__(self): self.__money = 0 def getMoney(self): return self.__money def setMoney(self, value): if isinstance(value, int): self.__money = value else: print("error:不是整型數字") # 定義一個屬性,當對這個money設置值時調用setMoney,當獲取值時調用getMoney money = property(getMoney, setMoney) a = Money() a.money = 100 # 調用setMoney方法 print(a.money) # 調用getMoney方法

# property屬性定義方式: 在方法上應用裝飾器,實現類型檢測功能 class People: def __init__(self, name): self.name = name # 實例化就觸發property @property def name(self): # return self.name # 無限遞歸 print('get------>') return self.DouNiWan @name.setter def name(self, value): print('set------>') if not isinstance(value, str): raise TypeError('必須是字符串類型') self.DouNiWan = value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1 = People('echo') # self.name實際是存放到self.DouNiWan里 p1.name = 1

# property屬性定義方式: 在方法上應用裝飾器,驗證@deleter不會刪除被property裝飾的方法 class A: def __init__(self, name): self.__name = name @property def name(self): return self.__name @name.setter def name(self, new_name): if type(new_name) is str: self.__name = new_name @name.deleter def name(self): def self.__name a = A("Ccoc") print(a.name) a.name = "echo" del a.name # 看似是刪除了name屬性,實際是刪除了__name私有屬性 # print(a)

# property屬性定義方式: 在方法上應用裝飾器,讓調用看起來更加合理 class Pager: def __init__(self, current_page): # 用戶當前請求的頁碼(第一頁,第二頁...) self.current_page = current_page # 每頁默認顯示10條數據 self.per_items = 10 @property def start(self): val = (self.current_page - 1) * self.per_items return val @property def end(self): val = self.current_page * self.per_items return val p = Pager(1) p.start # 就是起始值,即: m p.end # 就是結束值,即: n

# property屬性定義方式: 在方法上應用裝飾器,取代getater和seatter以及delattr方法 class Goods(object): def __init__(self): # 原價 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 實際價格 = 原價 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 獲取商品價格 obj.price = 200 # 修改商品原價 del obj.price # 刪除商品原價

# property屬性定義方式: 使用類屬性的方式創建property屬性改寫示例7 class Goods(object): def __init__(self): # 原價 self.original_price = 100 # 折扣 self.discount = 0.8 def get_price(self): # 實際價格 = 原價 * 折扣 new_price = self.original_price * self.discount return new_price def set_price(self, value): self.original_price = value def del_price(self): del self.original_price PRICE = property(get_price, set_price, del_price, '價格屬性描述...') obj = Goods() obj.PRICE # 獲取商品價格 obj.PRICE = 200 # 修改商品原價 del obj.PRICE # 刪除商品原價
4.靜態方法,類方法,靜態屬性綜合示例

class Room(object): tag = 1 def __init__(self, wight, length): self.wight = wight self.length = length @property def car_area(self): return self.length * self.wight + self.tag @classmethod def tell_info(cls): print(cls) print("-->", cls.tag) @staticmethod def run(a, b): print("%s %s 正在跑步" % (a, b)) # 靜態屬性: 將類的函數屬性@property裝飾后,是對象以數據屬性的方式調用 # 只和實例對象綁定-->即可以訪問實例屬性也可以訪問類的屬性 p1 = Room(10, 20) print("類屬性", p1.tag) print("實例對象調用靜態屬性", p1.car_area) # 類方法: 將類的函數屬性@classmethod裝飾后,不用創建實例對象,調用類的函數屬性 # 只和類綁定-->可以訪問類屬性,不能訪問實例屬性 Room.tell_info() # 靜態方法: 將類的函數屬性@staticmethod裝飾后,不創建和創建實例對象,都可以調用類的函數屬性 # 即不和類綁定也不和實例對象綁定-->不能訪問類屬性,也不能訪問實例屬性,是類的工具包 Room.run("co1", "co2") p2 = Room(20, 20) p2.run("co3", "co4")
5.Django框架中應用了property屬性

class WSGIRequest(http.HttpRequest): def __init__(self, environ): script_name = get_script_name(environ) path_info = get_path_info(environ) if not path_info: # Sometimes PATH_INFO exists, but is empty (e.g. accessing # the SCRIPT_NAME URL without a trailing slash). We really need to # operate as if they'd requested '/'. Not amazingly nice to force # the path like this, but should be harmless. path_info = '/' self.environ = environ self.path_info = path_info self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/')) self.META = environ self.META['PATH_INFO'] = path_info self.META['SCRIPT_NAME'] = script_name self.method = environ['REQUEST_METHOD'].upper() _, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', '')) if 'charset' in content_params: try: codecs.lookup(content_params['charset']) except LookupError: pass else: self.encoding = content_params['charset'] self._post_parse_error = False try: content_length = int(environ.get('CONTENT_LENGTH')) except (ValueError, TypeError): content_length = 0 self._stream = LimitedStream(self.environ['wsgi.input'], content_length) self._read_started = False self.resolver_match = None def _get_scheme(self): return self.environ.get('wsgi.url_scheme') def _get_request(self): warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or ' '`request.POST` instead.', RemovedInDjango19Warning, 2) if not hasattr(self, '_request'): self._request = datastructures.MergeDict(self.POST, self.GET) return self._request @cached_property def GET(self): # The WSGI spec says 'QUERY_STRING' may be absent. raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '') return http.QueryDict(raw_query_string, encoding=self._encoding) # ############### 看這里看這里 ############### def _get_post(self): if not hasattr(self, '_post'): self._load_post_and_files() return self._post # ############### 看這里看這里 ############### def _set_post(self, post): self._post = post @cached_property def COOKIES(self): raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '') return http.parse_cookie(raw_cookie) def _get_files(self): if not hasattr(self, '_files'): self._load_post_and_files() return self._files # ############### 看這里看這里 ############### POST = property(_get_post, _set_post) FILES = property(_get_files) REQUEST = property(_get_request)
9.作用於類的內置函數(isinstance issubclass object super vars type)
1.isinstance函數
isinstance(obj, class_or_tuple):
返回這個對象是否是某個類的對象,或者某些類中的一個類的對象,如果是則返回True,否則返回False

class Foo(object): pass obj = Foo() isinstance(obj, Foo) # True isinstance(1, int) # True isinstance("", (int, str, list)) # # True
2.issubclass函數
issubclass(cls, class_or_tuple):
判斷一個類是否是繼承自其它的類,如果此類cls是class或tuple中的一個派生子類則返回True,否則返回False

class A: pass class B(A): pass class C(B): pass class D(B): pass issubclass(B, A) # True issubclass(C, B) # True issubclass(D, C) # False issubclass(C, (int, str)) # False
3.object函數
object函數概述:
1.object類是Python中所有類的基類,如果定義一個類時沒有指定繼承哪個類,則默認繼承object類
2.object沒有定義__dict__所以不能對object類實例對象嘗試設置屬性
3.object函數返回一個新的無特征對象,obj = object()

class A: pass # 默認繼承object類 print(issubclass(A,object)) # True # object類定義了所有類的一些公共方法 print(dir(object)[0:5]) # ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__'] # object函數返回一個新的無特征對象 obj = object() type(obj) # object print(obj) # <object object at 0x0000000003E89EF0>

# 定義一個類A class A: pass a = A() a.name = 'li' # 能設置屬性 b = object() b.name = 'wang' # 不能設置屬性 """執行結果 AttributeError Traceback (most recent call last) <ipython-input-23-fae4aa8bc37b> in <module> 7 8 b = object() ----> 9 b.name = 'wang' # 不能設置屬性 AttributeError: 'object' object has no attribute 'name' """
4.super函數
super函數概述:
1.返回超類的實例,用超類實例來調用其自身的方法,super()就是使用super類創建出來的對象
2.當子類實現了__init__方法后,父類的__init__方法將被覆蓋,不再會主動調用父類的__init__方法
3.當子類實現了__init__方法后,會引起父類的屬性得不到初始化,需要父類初始化屬性必須要在子類中調用super函數
4.super(type, obj): 返回綁定超類的實例(要求obj必須為type類型的實例)
5.super(): 返回綁定超類的實例,等同於super(__class__, 實例的第一個參數), 且必須在方法內調用

# 此示例示意用super函數訪問父類的覆蓋方法 class A: def work(self): print("A類的work方法被調用") class B(A): def work(self): print("B類的work方法被調用") def doworks(self): # self.work() # 調用B類的方法 super(B, self).work() # 調用超類的方法 super().work() # Python推薦的調用超類的調用方法 # super(__class__, self).work() # 一樣會調用超類的方法 b = B() b.work() # B類的work方法被調用 print("-----以下用b調用覆蓋版本的方法----") # A.work(b) # A類的work方法被調用 super(B, b).work() b.doworks()

# 此示例示意顯式調用基類的初始化方法 class Human: def __init__(self, n, a): self.name = n self.age = a print("Human類的 __init__被調用") def show_info(self): print("姓名:", self.name) print("年齡:", self.age) class Student(Human): """學生類""" def __init__(self, n, a, s=0): super().__init__(n, a) # 顯式調用基類的初始化方法 self.score = s print("Student類的 __init__被調用") def show_info(self): super().show_info() print("成績:", self.score) s1 = Student('coco', 20) s1.show_info()
5.vars函數
vars() 函數返回對象object的屬性和屬性值的字典對象

print(vars()) """ {'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None} """

class Runoob: a = 1 print(vars(Runoob)) # {'a': 1, '__module__': '__main__', '__doc__': None} runoob = Runoob() print(vars(runoob)) # {}
6.type函數(type元類)
type元類概述:
1.type元類是獲取該對象從屬於的類
2.Python原則是: 一切皆對象,其實類也可以理解為'對象',而type元類又稱作構建類
3.Python中大多數內置的類(包括object)以及自己定義的類,都是由type元類創造的
4.而type類與object類之間的關系比較獨特:
1.object是type類的實例,而type類是object類的子類
2.這種關系比較神奇無法使用python的代碼表述,因為定義其中一個之前另一個必須存在

# type(obj)返回對象的類 class A: pass a = A() type(a) # __main__.A
10.屬性管理內置函數(反射函數)-getattr, hasattr, setattr, delattr
反射概述: 從某個指定的命名空間中,用字符串數據類型的變量名來獲取變量的值
getattr(obj, name[, default]):
從一個對象得到對象的屬性;getattr(x, 'y') 等同於x.y
當屬性不存在時,如果給出default參數,則返回default,如果沒有給出default 則產生一個AttributeError錯誤
hasattr(obj, name):
用給定的name返回對象obj是否有此屬性,此種做法可以避免在getattr(obj, name)時引發錯誤
setattr(obj, name, value):
給對象obj的名為name的屬性設置相應的值value, setattr(x, 'y', v) 等同於 x.y = v
delattr(obj, name):
刪除對象obj中的name屬性, delattr(x, 'y') 等同於 del x.y

# 對類的反射-拿靜態屬性,類方法,靜態方法 class Student: ROLE = 'STUDENT' @classmethod def check_course(cls): print('查看課程了') @staticmethod def login(): print('登錄') # 反射查看屬性 print(Student.ROLE) # STUDENT print(getattr(Student, 'ROLE')) # STUDENT # 反射調用方法 getattr(Student, 'check_course')() # 查看課程了 getattr(Student, 'login')() # 登錄 num = input('>>>') # login if hasattr(Student, num): getattr(Student, num)() # 登錄

# 對實例化對象的反射-拿屬性,方法 class Foo: f = '類的靜態變量' def __init__(self, name, age): self.name = name self.age = age def say_hi(self): print('hi,%s' % self.name) obj = Foo('egon', 73) # 檢測是否含有某屬性 print(hasattr(obj, 'name')) print(hasattr(obj, 'say_hi')) # 獲取屬性 n = getattr(obj, 'name') print(n) func = getattr(obj, 'say_hi') func() print(getattr(obj, 'aaaaaaaa', '不存在')) # 報錯 # 設置屬性 setattr(obj, 'sb', True) setattr(obj, 'show_name', lambda self: self.name + 'sb') print(obj.__dict__) print(obj.show_name(obj)) # 刪除屬性 delattr(obj, 'age') delattr(obj, 'show_name') delattr(obj, 'show_name111') # 不存在,則報錯 print(obj.__dict__)

# 反射當前模塊成員-拿自己的變量名 import sys def s1(): print('s1') def s2(): print('s2') this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2')

# 導入其他模塊,利用反射-拿模塊中的方法 """程序目錄 module_test.py index.py """ # module_test.py中的代碼 def test(): print('from the test') # index.py中的代碼 import module_test as obj # obj.test() print(hasattr(obj, 'test')) getattr(obj, 'test')()

# 反射的應用 class User: def login(self): print('歡迎來到登錄頁面') def register(self): print('歡迎來到注冊頁面') def save(self): print('歡迎來到存儲頁面') user = User() while 1: choose = input('>>>').strip() if hasattr(user, choose): func = getattr(user, choose) func() else: print('輸入錯誤...')

# 四個反射函數綜合運用 class BlackMedium: feature = 'Ugly' def __init__(self, name, addr): self.name = name self.addr = addr def sell_house(self): print('%s 賣房子' %self.name) def rent_house(self): print('%s 出租' %self.name) b1 = BlackMedium('桃園', 'xx區xx路') # 檢測是否含有某屬性 print(hasattr(b1, 'name')) print(hasattr(b1, 'sell_house')) # 獲取屬性 n = getattr(b1, 'name') print(n) func = getattr(b1, 'rent_house') func() # getattr(b1, 'a') # 報錯 print(getattr(b1, 'a', '不存在')) # 設置屬性 setattr(b1, 'sb', True) setattr(b1, 'show_name', lambda self: '景秀' + self.name) print(b1.__dict__) print(b1.show_name(b1)) # 刪除屬性 delattr(b1, 'addr') delattr(b1, 'show_name') # delattr(b1, 'show_name111') # 不存在則報錯 print(b1.__dict__)
11.函數與方法的區別
1.函數的是顯式傳遞數據的,是操作參數傳入的數據,與對象無關
2.方法中的數據則是隱式傳遞的,方法可以操作類內部的數據,和對象有關聯
3.打印對象名稱可以確定對象是函數還是方法

def func(): pass print(func) # <function func at 0x10f65c170> class A: def func(self): pass print(A.func) # <function A.func at 0x10f67c0e0> obj = A() print(obj.func) # <bound method A.func of <__main__.A object at 0x10f692250>>
4.模塊驗證對象是函數還是方法

from types import FunctionType from types import MethodType def func(): pass class A: def func(self): pass obj = A() print(isinstance(func, FunctionType)) # True print(isinstance(A.func, FunctionType)) # True print(isinstance(obj.func, FunctionType)) # False print(isinstance(obj.func, MethodType)) # True
5.靜態方法其實是函數

from types import FunctionType from types import MethodType class A: def func(self): pass @classmethod def func1(cls): pass @staticmethod def func2(): pass obj = A() # 靜態方法其實是函數 print(isinstance(A.func2, FunctionType)) # True print(isinstance(obj.func2, FunctionType)) # True print(isinstance(obj.func1, FunctionType)) # False
12.雙下屬性
1.類的文檔字符串屬性 __doc__
__doc__屬性: 是類的描述信息,無法被子類繼承

class Demo: """測試類""" pass Demo.__doc__ # '測試類'

class Foo: """描述信息""" pass class Bar(Foo): pass print(Bar.__doc__) # 該屬性無法繼承給子類
2.類的字典屬性 __dict__
__dict__屬性: 可以查詢類中的所有屬性和方法

class A: a = 1 def add(): pass A.__dict__ """執行結果 mappingproxy({'__module__': '__main__', 'a': 1, 'add': <function __main__.A.add()>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}) """
3.類的名稱屬性 __name__

class Demo: """測試類""" pass Demo.__name__ # 'Demo'
4.類的記錄基類屬性 __base__, __bases__
__base__屬性: 用來記錄此類的基類,返回一個字符串
__bases__屬性: 用來記錄此類的基類,返回一個元祖

class A: pass class B(A): pass B.__base__ # __main__.A B.__bases__ # (__main__.A,) A.__base__ # object A.__bases__ # (object,) type.__base__ # object str.__bases__ # (object,)
5.類的限定實例化對象的屬性 __slots__
__slots__屬性概述
1.是一個類變量,變量值可以是列表,元祖,或者可迭代對象,也可以是一個字符串(意味着所有實例只有一個固定的屬性)
2.使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每個實例的是獨立的)
3.字典會占用大量內存,如果類的屬性很少但卻有很多實例,為了節省內存可以使用__slots__取代實例的__dict__
4.含有 __slots__ 屬性的類所創建的對象沒有__dict__字典,統一歸__slots__管理,有利於節省內存

class Student: # 限定此的類創建的對象只能有name和age兩個屬性 __slots__ = ['name', 'age'] def __init__(self, n, a): self.name = n self.age = a s1 = Student("小張", 20) # s1.Age = 21 # 此時是錯寫了age為Age, 會報錯 # print(s1.__dict__) # 出錯,因為沒有__dict__字典

class Foo2: """文檔描述""" # __slost__取代了類的__dict__使實例化除的屬性沒有__dict__方法 __slots__ = ["name", "age"] class Foo3(Foo2): def __del__(self): print("回收實例時觸發") def __call__(self, *args, **kwargs): print("__call__方法讓實例對象可以加()執行") f3 = Foo2 print("f3", f3.__slots__) # __doc__記錄類的文檔描述信息 print(Foo2.__doc__) # 文檔描述 # __doc__無法被子類繼承,因為子類一旦定義就有屬於自己的__doc__ print(Foo3.__doc__) # None print(Foo3.__dict__) # __module__查看f3來自於哪個模塊 print(f3.__module__) # __main__ # __class__查看f3來自於哪個類 print(f3.__class__) # <class 'type'> f3 = Foo3() # <class 'type'> f3() # __call__方法讓實例對象可以加()執行 # __del__析構函數,會在回收實例時觸發 del f3 # 回收實例時觸發
6.實例的預置屬性 __dict__, __class__
__dict__屬性: 用於綁定一個存儲此實例自身變量的字典
__class__ 屬性: 用於綁定創建此實例的類,表示當前操作的對象的類是什么

class Dog: pass dog1 = Dog() print(dog1.__dict__) # {} dog1.color = "白色" print(dog1.__dict__) # {'color': '白色'} print(dog1.__class__) # <class '__main__.Dog'>
7.實例的隸屬屬性 __module__
__module__屬性: 表示當前操作的對象在那個模塊

# 模塊 My_Module 中的代碼 class C: def __init__(self): self.name = "Cat" # 主模塊中的代碼 from My_Module import C obj = C() # 輸出obj對象所在的模塊 print(obj.__module__) # My_Module # 即輸出obj對象所在的類 print(obj.__class__) # My_Module.C
13.雙下方法
1.構造方法 __new__
構造方式概述:
1.過程: 開辟一個屬於對象的空間,把對象的空間傳給self后執行init,最后將這個對象的空間返回給調用者
2.new方法在實例化之后,__init__ 之前先執行new來創建一塊空間
3.當創建的類沒有new方法時,會調用object的new方法

class Single: def __new__(cls, *args, **kwargs): # print('在new方法里') obj = object.__new__(cls) print('在new方法里', obj) return obj def __init__(self): print('在init方法里', self) # 實例化時,__new__構造方法先調用,接着__init__初始化方法調用 obj = Single() """執行結果 在new方法里 <__main__.Single object at 0x000000000525EDD8> 在init方法里 <__main__.Single object at 0x000000000525EDD8> """
2.初始化方法 __init__
初始化方法概述:
1.初始化方法名必須為 __init__ 不可改變,是對新創建的對象添加屬性等必須的資源
2.初始化方法會在構造函數創建實例后自動調用, 且將實例自身通過第一個參數 self 傳入 __init__ 方法
3.構造函數的實參將通過 __init__ 方法的參數列表傳到 __init__ 方法中
4.初始化方法內如果需要 return 語句返回則必須返回 None
5.語法形式:
class 類名:
def __init__(self[, 參數列表]): # []代表其中內容可省略
語句塊

# 此示例示意初始化方法的定義方法和調用過程 class Car: """小汽車類""" def __init__(self, c, b, m): self.color = c # 顏色 self.brand = b # 品牌 self.model = m # 型號 self.wheel = 4 print("__init__方法被調用") def run(self, speed): print(self.color, '的', self.brand, self.model, '正在以', speed, '公里/小時的速度行駛') def change_color(self, c): self.color = c a4 = Car('紅色', '奧迪', 'A4') a4.run(199) a4.change_color("白色") a4.run(280) x5 = Car('藍色', '寶馬', 'x5') x5.run(188)
3.析構方法 __del__
析構方法概述:
1.在對象被銷毀之前被調用,主要負責清理對象所占用的資源
2.Python建議盡可能少的在析構方法內做事情,因為銷毀時間難以確定
3.對象借用了操作系統的資源還要通過析構方法歸還資源,如文件資源,網絡資源
4.語法形式:
class 類名:
def __del__(self):
語句塊

class FileManage: """定義一個文件管理員類""" def __init__(self, filename='a.txt'): self.file = open(filename, 'w') def writeline(self, string): self.file.write(string) self.file.write('\n') def __del__(self): """析構方法會在對象銷毀前自動調用""" self.file.close() print("文件已關閉") fm = FileManage() fm.writeline("hello world") fm.writeline("這是中文寫的第二行") del fm # 文件已關閉 # 死循環永遠不退出,del不調用一直不關閉文件 while True: pass print("程序結束")
4.調用方法 __call__
在創建類型的時候定義了__call__方法,這個對象就類似函數一樣是可調用的,相當於 對象()
在寫類的裝飾器時會用到__call__方法

class A: def __call__(self, *args, **kwargs): print('執行call方法了') def call(self): print('執行call方法了') class B: def __init__(self, cls): print('在實例化A之前做一些事情') self.a = cls() self.a() print('在實例化A之后做一些事情') a = A() # 對象() 相當於調用__call__方法 a() # 執行call方法了 a.call() # 執行call方法了 # 類名()()相當於先實例化得到一個對象,再對對象(),和上面的結果一樣,相當於調用__call__方法 A()() # 執行call方法了 B(A) """B(A)執行結果 在實例化A之前做一些事情 執行call方法了 在實例化A之后做一些事情 Out[11]: <__main__.B at 0x550a9b0> # 這行是調用B類的基類object的__call__方法執行的結果 """
5.item系列方法(__getitem__, __setitem__, __delitem__)
item方法概述:
1.item系列方法和對象使用[]訪問值有聯系,用於索引切片操作,常用語字典和列表對象
2.Python3中的切片方法不再是Python2中的__getslice__(), __setslice__()和__delslice__()
3.Python3中借助slice類把分片整合到了__getitem__(),__setitem__()和 __delitem__()中

obj = {'k':'v'} # 字典的對象 print(obj) # {'k': 'v'} print(obj['k']) # v class B: def __getitem__(self, item): return getattr(self, item) def __setitem__(self, key, value): setattr(self, key, value*2) def __delitem__(self, key): delattr(self, key) b = B() # b.k2 = 'v2' # print(b.k2) # __setitem__ b['k1'] = 'v1' # __getitem__ print(b['k1']) # v1v1 # __delitem__ del b['k1'] # print(b['k1']) # 會報錯

# 字典的對象 class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): print('del obj[key]時,我執行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key時,我執行') self.__dict__.pop(item) f1 = Foo('sb') f1['age'] = 18 f1['age1'] = 19 del f1.age1 # del obj.key時執行 del f1['age'] # del obj[key]時執行 f1['name'] = 'coco' print(f1.__dict__) # {'name': 'coco'}

# 字典的對象 class Foo(): def __getattr__(self, item): print("執行了getattr查找%s" % item) # 無論是否找到屬性都會執行getattribute def __getattribute__(self, item): print("執行了getattribute查找%s" % item) raise AttributeError("拋出異常") f1 = Foo() # isinstance(obj, cls)判斷對象obj是否是類cls實例化出來的對象 print(isinstance(f1, Foo)) # True f1.x """f1.x執行結果 執行了 __getattribute__ 查找x 執行了 __getattr__ 查找x """ class Bar(): # 只針對字典方式操作觸發 def __getitem__(self, item): print("%s getitem" % item) return self.__dict__[item] # 只針對字典方式操作觸發 def __setitem__(self, key, value): print("%s:%s setitem" % (key, value)) self.__dict__[key] = value # 只針對字典方式操作觸發 def __delitem__(self, key): print("%s delitem" % key) self.__dict__.pop(key) # issubclass(sub, super)判斷子類sub是否是由父類super繼承來的派生類 print(issubclass(Bar, Foo)) # False f2 = Bar() f2["name"] = "coco" # name:coco setitem print(f2["name"]) """print(f2["name"])執行結果 name getitem coco """ f2["age"] = "18" # age:18 setitem print(f2.__dict__) # {'name': 'coco', 'age': '18'} del f2["age"] # age delitem print(f2.__dict__) # {'name': 'coco'}

# 列表的對象 class Foo(object): def __getitem__(self, index): if isinstance(index, slice): print("Get slice---------> start: %s, stop: %s, step: %s." \ % (str(index.start), str(index.stop), str(index.step))) def __setitem__(self, index, value): if isinstance(index, slice): print("Set slice---------> start: %s, stop: %s, step: %s." \ % (str(index.start), str(index.stop), str(index.step))) print("\tThe value is:", value) def __delitem__(self, index): if isinstance(index, slice): print("Delete slice------> start: %s, stop: %s, step: %s." \ % (str(index.start), str(index.stop), str(index.step))) if __name__ == "__main__": obj = Foo() obj[-1:10] # Get slice---------> start: -1, stop: 10, step: None. obj[-1:10:1] = [2, 3, 4, 5] """obj[-1:10:1] = [2, 3, 4, 5]執行結果 Set slice---------> start: -1, stop: 10, step: 1. The value is: [2, 3, 4, 5] """ del obj[-1:10:2] # Delete slice------> start: -1, stop: 10, step: 2.

# Python2中的分片(切片)操作 __getslice__ __setslice__ __delslice__ class Foo(object): def __getslice__(self, i, j): print('__getslice__', i, j) def __setslice__(self, i, j, sequence): print('__setslice__', i, j) def __delslice__(self, i, j): print('__delslice__', i, j) obj = Foo() obj[-1:1] # 自動觸發執行 __getslice__ obj[0:1] = [11, 22, 33, 44] # 自動觸發執行 __setslice__ del obj[0:2] # 自動觸發執行 __delslice__
6.__eq__ 方法
__eq__方法定義了類的等號(==)行為

class A: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if self.name == other.name and self.age == other.age: return True a = A('gogo', 83) aa = A('gogo', 83) aa2 = A('gogo', 83) aa3 = A('gogo', 83) aa4 = A('gogo', 83) print(a, aa) # <__main__.A object at 0x0000000004F94FD0> <__main__.A object at 0x0000000004F94748> # ==這個語法 是完全和__eq__ 相同 print(aa3 == aa == aa4) # True
7.__str__ 方法和 __repr__ 方法
str函數和repr函數概述:
str函數返回對象的描述信息print函數輸出使用
str函數或者print函數 實際調用--->obj.__str__(), print("%s", 變量名)
repr函數返回一個能代表次對象的字符串表達式(這個字符串是給Python解釋器識別用的),通常用於遠程通信數據校驗
repr函數用於遠程通信數據校驗示例:eval(repr(obj)) == obj
repr函數或者交互式解釋器 實際調用--->obj.__repr__(), print("%r", 變量名)
__str__ 和 __repr__ 方法概述:
如果__str__沒有被定義,那么原本調用__str__方法的語法就會使用__repr__來代替輸出
__str__ 和 __repr__ 方法的返回值必須是字符串,否則拋出異常
查找順序:
1.在子類中使用__str__,先找子類的__str__,沒有的話要向上找,只要父類不是object,就執行父類的__str__
2.但是如果除了object之外的父類都沒有__str__方法,就執行子類的__repr__方法
3.如果子類沒有__repr__方法,還要向上找父類中的__repr__方法,一直找不到再執行object類中的__str__方法

class Foo1(): # 運用在print打印時觸發 def __str__(self): return "自定義打印信息" # 運用在解釋器時觸發 def __repr__(self): return "自定義打印信息2" obj = Foo1() print(obj) # 自定義打印信息 # __str__和 __repr__共存,print先找str沒有再找repr print(obj) # 自定義打印信息

class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b=B() print('%s' % b) print('%r' % b)
8.格式化字符串方法 __format__

# __format__ 格式化方法示例 format_dic = { "ymd": "{0.year}{0.mon}{0.day}", "m-d-y": "{0.mon}-{0.day}-{0.year}", } class Date: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day def __format__(self, format_spec): print("-->", format_spec) if not format_spec or format_spec not in format_dic: format_spec = "ymd" fm = format_dic[format_spec] return fm.format(self) d1 = Date(2019, 4, 13) print("{0.year}{0.mon}{0.day}".format(d1)) # 2019413 print(format(d1, "ymd")) # 2019413 print(format(d1, "m-d-y")) # 4-13-2019
9.描述符方法(__get__, __set__, __delete__)
1.描述符概述(描述符協議):
描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個
__get__(): 調用一個屬性時,觸發
__set__(): 為一個屬性賦值時,觸發
__delete__(): 使用del刪除屬性時,觸發
數據描述符: 至少實現了__get__()和__set__()
非數據描述符: 沒有實現: __set__()
2.描述符應用示例

class Foo4(): def __get__(self, instance, owner): print("get") def __set__(self, instance, value): print("set") def __delete__(self, instance): print("delete") class Foo5(Foo4): name = Foo4() f5 = Foo5() f5.name # get f5.name = 1 # set del f5.name # delete

class Typed: def __init__(self, key, expected_type): self.key = key self.expected_type = expected_type def __get__(self, instance, owner): print('get方法') # print('instance參數【%s】' %instance) # print('owner參數【%s】' %owner) return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') # print('instance參數【%s】' % instance) # print('value參數【%s】' % value) # print('====>',self) if not isinstance(value,self.expected_type): # print('你傳入的類型不是字符串,錯誤') # return raise TypeError('%s 傳入的類型不是%s' %(self.key, self.expected_type)) instance.__dict__[self.key] = value def __delete__(self, instance): print('delete方法') # print('instance參數【%s】' % instance) instance.__dict__.pop(self.key) class People: name = Typed('name', str) # t1.__set__() self.__set__() age = Typed('age', int) # t1.__set__() self.__set__() def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People("coco", 13, 13.3) print(p1.__dict__) # {'name': 'coco', 'age': 13, 'salary': 13.3}
3.描述符的優先級
類屬性 > 數據描述符 > 實例屬性 > 非數據描述符 > 找不到的屬性觸發__getattr__()
1.類屬性 > 數據描述符

#描述符Str class Str: def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') class People: name = Str() def __init__(self, name, age): # name被Str類代理,age被Int類代理 self.name = name self.age = age # 在一個類中定義描述符它就是一個類屬性,存在於類的屬性字典中,而不是實例的屬性字典 # 描述符被定義成了一個類屬性,直接通過類名也可以調用類屬性name,本質就是在調用描述符Str,觸發了__get__() People.name # Str調用 # 賦值並沒有觸發__set__() People.name = 'coco' # del也沒有觸發__delete__() del People.name '''原因: 描述符在使用時被定義成另外一個類的類屬性,因而類屬性比二次加工的描述符偽裝而來的類屬性有更高的優先級 People.name # 調用類屬性name,找不到就去找描述符偽裝的類屬性name,觸發了__get__() People.name = 'coco' # 直接賦值了一個類屬性,它擁有更高的優先級,相當於覆蓋了描述符不會觸發描述符的__set__() del People.name # 直接刪除一個類屬性,它擁有更高的優先級,相當於覆蓋了描述符不會觸發描述符的__delete__() '''
2.數據描述符 > 實例屬性

#描述符Str class Str: def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') class People: name=Str() def __init__(self, name, age): # name被Str類代理,age被Int類代理 self.name = name self.age = age p1 = People('angels', 18) # Str設置... # 如果描述符是一個數據描述符(即有__get__又有__set__) # 那么p1.name的調用與賦值都是觸發描述符的操作,與p1本身無關了相當於覆蓋了實例的屬性 p1.name ='coco' # # Str設置... p1.name # Str調用 # 實例的屬性字典中沒有name,因為name是一個數據描述符,優先級高於實例屬性 # 查看/賦值/刪除都是跟描述符有關,與實例無關 print(p1.__dict__) # {'age': 18} del p1.name # Str刪除...
3.實例屬性 > 非數據描述符

class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get') class Room: name = Foo() def __init__(self, name, width, length): self.name = name self.width = width self.length = length # name是一個數據描述符,因為name = Foo()而Foo實現了get和set方法,因而比實例屬性有更高的優先級 # 對實例的屬性操作,觸發的都是描述符的方法 r1 = Room('廁所', 1, 1) # set r1.name # get r1.name = '廚房' # set class Foo: def __get__(self, instance, owner): print('get') class Room: name = Foo() def __init__(self, name, width, length): self.name = name self.width = width self.length = length # name是一個非數據描述符,因為name=Foo()而Foo沒有實現set方法,因而比實例屬性有更低的優先級 # 對實例的屬性操作,觸發的都是實例自己的方法 r1 = Room('廁所', 1, 1) r1.name r1.name = '廚房'
4.非數據描述符 > 找不到

class Foo: def func(self): print('func被調用') def __getattr__(self, item): print('找不到就調用getattr', item) f1=Foo() f1.xxx # 找不到就調用getattr xxx
4.用描述符定制 靜態屬性@property, 類方法@classmethod, 靜態方法@staticmethod
1.定制靜態屬性@property

class Lazyproperty: def __init__(self, func): print('==========>', func) self.func = func def __get__(self, instance, owner): print('get') # print(instance) # print(owner) if instance is None: return self res = self.func(instance) setattr(instance, self.func.__name__, res) return res class Room: def __init__(self, name, width, length): self.name = name self.width = width self.length = length # @property #area = property(area) @Lazyproperty #area = Lazypropery(area) def area(self): return self.width * self.length @property #test = property(test) def area1(self): return self.width * self.length r1 = Room("coco", 10, 10) # ======> <function Room.area at 0x0000000006AC4C80> print(r1.area1) # 100 # 先從自己的屬性字典找,沒有再去類的中找,然后觸發了area的__get__方法 print(r1.area) # 先打印get, 再打印100 # 先從自己的屬性字典找,找到了,是上次計算的結果,這樣就不用每執行一次都去計算 print(r1.area) # 先打印100, 再打印get print(Room.area) # <__main__.Lazyproperty object at 0x0000000005AE52B0> print(Room.area1) # <property object at 0x00000000052F1458>
2.定制類方法@classmethod

class ClassMethod: def __init__(self, func): self.func = func # 類來調用,instance為None,owner為類本身,實例來調用,instance為實例,owner為類本身 def __get__(self, instance, owner): def feedback(): print('在這里可以加功能啊...') return self.func(owner) return feedback class People: name = 'coco' @ClassMethod # say_hi = ClassMethod(say_hi) def say_hi(cls): print('你好啊,world %s' % cls.name) People.say_hi() p1 = People() p1.say_hi() class ClassMethod: def __init__(self, func): self.func = func # 類來調用,instance為None,owner為類本身,實例來調用,instance為實例,owner為類本身 def __get__(self, instance, owner): def feedback(*args, **kwargs): print('在這里可以加功能啊...') return self.func(owner, *args, **kwargs) return feedback class People: name = 'linhaifeng' @ClassMethod # say_hi = ClassMethod(say_hi) def say_hi(cls, msg): print('你好啊,world %s %s' %(cls.name, msg)) People.say_hi('每天都是充滿希望的一天') p1 = People() p1.say_hi('每天都是充滿希望的一天')
3.定制靜態方法@staticmethod

class StaticMethod: def __init__(self, func): self.func = func # 類來調用,instance為None,owner為類本身,實例來調用,instance為實例,owner為類本身 def __get__(self, instance, owner): def feedback(*args, **kwargs): print('在這里可以加功能啊...') return self.func(*args, **kwargs) return feedback class People: @StaticMethod # say_hi = StaticMethod(say_hi) def say_hi(x, y, z): print('------>', x, y, z) People.say_hi(1, 2, 3) p1 = People() p1.say_hi(4, 5, 6)
10.上下文管理器方法(__enter__, __exit__)
上下文管理器內部必須實現 __enter__ __exit__ 方法

class Open(): def __init__(self, name): self.name = name def __enter__(self): print("執行了enter") return self def __exit__(self, exc_type, exc_val, exc_tb): print("執行了exit") print(exc_type) # 異常類型 print(exc_val) # 異常值 print(exc_tb) # 異常追蹤信息 with Open("a.txt") as f: print(f) # <__main__.Foo6 object at 0x000000000358E0F0> print(f.name) # a.txt print("結束") # 結束

class A: def __enter__(self): print('before') def __exit__(self, exc_type, exc_val, exc_tb): print('after') with A() as a: print('123')

class A: def __init__(self): print('init') def __enter__(self): print('before') def __exit__(self, exc_type, exc_val, exc_tb): print('after') with A() as a: print('123')

class Myfile: def __init__(self, path, mode='r', encoding='utf-8'): self.path = path self.mode = mode self.encoding = encoding def __enter__(self): self.f = open(self.path, mode=self.mode, encoding=self.encoding) return self.f def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() with Myfile('file',mode='w') as f: f.write('wahaha')

import pickle class MyPickledump: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='ab') return self def dump(self, content): pickle.dump(content, self.f) def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() class Mypickleload: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='rb') return self def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() def load(self): return pickle.load(self.f) def loaditer(self): while True: try: yield self.load() except EOFError: break # with MyPickledump('file') as f: # f.dump({1, 2, 3, 4}) with Mypickleload('file') as f: for item in f.loaditer(): print(item)

import pickle class MyPickledump: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='ab') return self def dump(self, content): pickle.dump(content, self.f) def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() class Mypickleload: def __init__(self, path): self.path = path def __enter__(self): self.f = open(self.path, mode='rb') return self def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() def __iter__(self): while True: try: yield pickle.load(self.f) except EOFError: break # with MyPickledump('file') as f: # f.dump({1, 2, 3, 4}) with Mypickleload('file') as f: for item in f: print(item)
11.哈希方法 __hash__
哈希方法概述:
1.Python底層數據結構是基於hash值尋址的優化操作
2.hash是一個算法能夠把某一個要存在內存里的值通過一系列計算,保證不同值的hash結果是不一樣的

class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a) + str(self.b)) a = A() print(hash(a)) # -7282248652925619321 hash("12") # -7282248652925619321
12.__len__ 方法
__len__ 方法用於獲取一個對象的元素個數

class B: def __len__(self): print("len函數觸發__len__方法") return 0 b = B() # len一個對象就會觸發 __len__方法 len(b) """len(b)執行結果 len函數觸發__len__方法 0 """

class A: def __init__(self): self.a = 1 self.b = 2 def __len__(self): print(self.__dict__) # {'a': 1, 'b': 2} return len(self.__dict__) # 2 a = A() print(len(a)) # 2
13.實現迭代器協議的 __next__ 和 __iter__ 方法
方法概述:
__iter__ 方法用於迭代器,列表字典元組可以進行for循環,也是因為類型內部定義了__iter__ 方法
__next__ 方法訪問可迭代對象的數據,如果沒有數據則觸發 StopIteration 異常來通知調用者停止迭代即迭代器協議

class Foo(object): def __init__(self, sq): self.sq = sq def __iter__(self): return iter(self.sq) obj = Foo([11, 22, 33, 44]) for i in obj: print(i, end=" ") # 11 22 33 44

# 模擬range函數加上步長 class Range: def __init__(self, start, stop, step): self.start = start self.stop = stop self.step = step def __next__(self): if self.start >= self.stop: raise StopIteration x = self.start self.start += self.step return x def __iter__(self): return self for i in Range(1, 7, 3): print(i)

# 斐波拉契數列 class Fib: def __init__(self): self._a = 0 self._b = 1 def __iter__(self): return self def __next__(self): self._a, self._b = self._b, self._a + self._b return self._a f1=Fib() print(f1.__next__()) print(next(f1)) print(next(f1)) for i in f1: if i > 100: break print('%s ' % i, end=' ')
14.__getattribute__ 方法
方法概述:
1.當 __getattribute__ 與 __getattr__ 同時存在,只會執行 __getattribute__
2.當 __getattribute__ 在執行過程中拋出異常 AttributeError 時才會同時執行 __getattr__

class Foo: def __init__(self, name): self.name = name def __getattr__(self, item): print('__getattr__執行') # return self.__dict__[item] def __getattribute__(self, item): print('無論如何都會執行') raise AttributeError('發生了異常') f1 = Foo("Coco") f1.x f1.xx
14.單例的實現
單例概述: 單例類從頭到尾只能有一個實例且從頭到尾之開辟了一塊兒屬於對象的空間

class Single: __ISINCTANCE = None def __new__(cls, *args, **kwargs): if not cls.__ISINCTANCE: cls.__ISINCTANCE = object.__new__(cls) return cls.__ISINCTANCE def __init__(self, name, age): # self.name = name # self.age = age print(self) s1 = Single('coco', 23) # <__main__.Single object at 0x10f67fad0> s2 = Single('gogo', 40) # <__main__.Single object at 0x10f67fad0>

class MusicPlayer(object): # 記錄第一個被創建對象的引用 instance = None # 記錄是否執行過初始化動作 init_flag = False def __new__(cls, *args, **kwargs): # 1.判斷類屬性是否是空對象 if cls.instance is None: # 2.調用父類方法,為第一個對象分配空間 cls.instance = super().__new__(cls) # 3.返回類屬性保存的對象引用 return cls.instance def __init__(self): # 1.判斷是否執行過初始化動作 if MusicPlayer.init_flag: return # 2.沒有執行過,再執行初始化動作 print("初始化播放器") # 3.修改類屬性標記 MusicPlayer.init_flag = True player1 = MusicPlayer() # 初始化播放器 print(player1) # <__main__.MusicPlayer object at 0x0000000006AA28D0> player2 = MusicPlayer() print(player2) # <__main__.MusicPlayer object at 0x0000000006AA28D0>
15.函數模擬面向對象設計與類的面向對象設計對比
1.函數模擬面向對象設計

def dogs(name, gender, type): # 狗的屬性 def init(name, gender, type): dog = { "name": name, "gender": gender, "type": type, "run": run } return dog # 狗的方法 def run(dog): print("一條狗%s正在跑" % dog["name"]) return init(name, gender, type) d1 = dogs("wangcai", "公", "中華田園犬") d2 = dogs("xiaoqiang", "公", "哈士奇") print("面向對象設計", d1) d1["run"](d1) print(d2["name"])
2.類實現面向對象設計

# 面向對象編程-->用面向對象獨有的class語法實現面向對象設計 class Dog: """定義一個狗類""" def __init__(self,name, gender, type): self.name = name self.gender = gender self.type = type def run(self): print("一條狗%s正在跑" % self.name) dog1 = Dog("wangcai", "公", "中華田園犬") dog2 = Dog("xiaoqiang", "公", "哈士奇") print("面向對象編程", dog1.__dict__) # __dict__ 查看屬性字典 print(dog1.__dict__["type"]) # 中華田園犬 # 創建出的實例只有數據屬性,函數屬性實際是調的類的函數屬性 dog1.run() print(dog2.name) # 查看類的名字 print(Dog.__name__) # Dog # 查看文檔說明 print(Dog.__doc__) # 定義一個狗類 # 查看第一個父類 print(Dog.__base__) # <class 'object'> # 查看所有父類,構成元組 print(Dog.__bases__) # (<class 'object'>,) # 查看類的屬性,返回一個字典 print(Dog.__dict__) # 查看類定義所在的模塊 print(Dog.__module__) # __main__ # 查看實例對應的類 print(Dog.__class__) # <class 'type'>
16.組合
組合概述: 給一個類的對象封裝一個屬性,這個屬性是另一個類的對象

class GameRole: """英雄人物類""" def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def attack(self,p): p.hp = p.hp - self.ad print('%s 攻擊 %s,%s 掉了%s血,還剩%s血' %(self.name, p.name, p.name, self.ad, p.hp)) def armament_weapon(self, wea): self.wea = wea class Weapon: """武器屬性類""" def __init__(self, name, ad): self.name = name self.ad = ad def fight(self, p1, p2): p2.hp = p2.hp - self.ad print('%s 用%s打了%s\t%s 掉了%s血,還剩%s血'\ % (p1.name, self.name, p2.name, p2.name, self.ad, p2.hp)) p1 = GameRole('coco', 20, 500) p2 = GameRole('cat', 50, 200) axe = Weapon('三板斧', 60) broadsword = Weapon('屠龍寶刀', 100) p1.armament_weapon(axe) # 給coco裝備了三板斧這個對象. p1.wea.fight(p1, p2) # coco 用三板斧打了cat cat 掉了60血,還剩140血

# 組合實現與類的關聯 class School: def __init__(self, name, addr): self.name = name self.addr = addr class Course: def __init__(self, name, price, school): self.name = name self.price = price self.school = school s1 = School("coco", "成都") s2 = School("coco", "廣州") msg = """ 1 coco 成都 2 coco 廣州 """ while True: print(msg) menu = { "1": s1, "2": s2 } choice = input("請選擇學校>>>:") school_obj = menu[choice] name = input("課程名: ") price = input("課程費用: ") new_course = Course(name, price, school_obj) print("課程 %s 屬於 %s 學校" % (new_course.name, new_course.school.name))
17.封裝 enclosure
封裝概述:
1.封裝是指隱藏類的實現細節讓使用者不關心這些細節,目的是讓使用者通過盡可能少的使用實例屬性操作對象
2.私有成員有兩種: 私有屬性和私有方法,python類中以雙下划線('__')開頭且不以雙下划線結尾的標識符為私有成員
3.私有成員只能被方法調用,不能在子類或其它地方使用
4.廣義的封裝: 把屬性和方法封裝在類中,定義一個規范來描述一類事物
5.狹義的封裝: 私有化,只能在類的內部訪問

class A: def __init__(self): self.__p1 = 100 # 創建私有屬性 def __m1(self): print("A類的私有方法被調用!") def test(self): print(self.__p1) # 可以訪問 self.__m1() # 可以訪問 a = A() # print(a.__p1) # 出錯,不可以訪問 # a.__m1() # 出錯, 在類外部不能調用類的私有方法 a.test() # 用方法來操作私有屬性和私有方法 a.__dict__ # {'_A__p1': 100} a._A__p1 # 100 a._A__m1() # A類的私有方法被調用!

class Parent: def __func(self): print('in Parent func') def __init__(self): self.__func() class Son(Parent): def __func(self): print('in Son func') son1 = Son() # in Parent func
18.繼承 inheritance 和派生 derived
1.繼承和派生概述:
1.繼承是從已有類中派生出新類,新類具有原類的數據屬性和行為,並能擴展新的能力
2.派生就是從一個已有的類衍生出新類,在新的類上添加新的屬性和行為
3.任何類都直接或間接的繼承自object類,object類是一切類的超類
4.用繼承派生機制,可以將一些共有功能加在基類中實現代碼的共享,在不改變超類的代碼的基礎上改變原有的功能
5.繼承和派生流程圖: https://www.processon.com/view/link/5ee0d5cc07912929cb38f831
6.術語名詞: 基類(base class) 超類(super class) 父類(father class) 派生類(derived class) 子類(child class)
2.單繼承:
語法:
class 類名(超類名):
語句塊

class Human: # 人 def say(self, what): # 說話的行為 print("說: ", what) def walk(self, distance): # 走路的行為 print("走了", distance, "公里") h1 = Human() h1.say("今天天氣不錯!") h1.walk(5) class Student(Human): # def say(self, what): # 說話的行為 # print("說: ", what) # def walk(self, distance): # 走路的行為 # print("走了", distance, "公里") def study(self, subject): print("正在學習", subject) s1 = Student() s1.say("今天晚飯吃什么?") s1.walk(3) s1.study("python")
3.多繼承 multiple inheritance
多繼承概述:
1.多繼承是指一個子類繼承自兩個或兩個以上的基類
2.多繼承的問題(缺陷): 標識符(名字空間)沖突的問題,要謹慎使用多繼承
語法:
class 類名(超類名1, 超類名2, ...):
pass

class Car: def run(self, speed): print("汽車以", speed, "km/h的速度行駛") class Plane: def fly(self, height): print("飛機以海拔", height, "米的高度飛行") class PlaneCar(Car, Plane): """飛行汽車類, 同時繼承 自Car和 Plane""" pass p1 = PlaneCar() p1.fly(10000) # 飛機以海拔 10000 米的高度飛行 p1.run(299) # 汽車以 299 km/h的速度行駛

# 多繼承缺陷示例 # 小張寫了一個類A class A: def m(self): print("A.m()被調用") # 小李寫了一個類B: class B: def m(self): print("B.m() 被調用") # 小王感覺小張和小李寫的兩個類自己可以用 class AB(A, B): pass ab = AB() ab.m() # 調用A類中的m方法
4.多繼承的 MRO (Method Resolution Order)問題
MRO 方法搜索順序問題
子類繼承父類時MRO查找順序為C3算法
MRO順序-廣度優先和深度優先流程圖: https://www.processon.com/view/link/5ee103c907912929cb39509a
python 3 廣度優先: 一條路走到倒數第二級,判斷如果其他路能走到終點,則返回走另一條路,如果不能則走到終點
python 2 深度優先: 一條路走到底

class E: pass class F: pass class A(E): pass class B(E): pass class C(F): pass class D(F): pass class AB(A, B): pass class CD(C, D): pass class ABCD(AB, CD): pass print(ABCD.__mro__) """Python3運行結果 (__main__.ABCD, __main__.AB, __main__.A, __main__.B, __main__.E, __main__.CD, __main__.C, __main__.D, __main__.F, object) """
5.子類調用父類方法

print("******多繼承使用類名.__init__ 發生的狀態******") class Parent(object): def __init__(self, name): print('parent的init開始被調用') self.name = name print('parent的init結束被調用') class Son1(Parent): def __init__(self, name, age): print('Son1的init開始被調用') self.age = age Parent.__init__(self, name) print('Son1的init結束被調用') class Son2(Parent): def __init__(self, name, gender): print('Son2的init開始被調用') self.gender = gender Parent.__init__(self, name) print('Son2的init結束被調用') class Grandson(Son1, Son2): def __init__(self, name, age, gender): print('Grandson的init開始被調用') Son1.__init__(self, name, age) # 單獨調用父類的初始化方法 Son2.__init__(self, name, gender) print('Grandson的init結束被調用') gs = Grandson('grandson', 12, '男') print('姓名: ', gs.name) print('年齡: ', gs.age) print('性別: ', gs.gender) print("******多繼承使用類名.__init__ 發生的狀態******\n\n")

print("******單繼承使用super().__init__ 發生的狀態******") class Parent(object): def __init__(self, name): print('parent的init開始被調用') self.name = name print('parent的init結束被調用') class Son1(Parent): def __init__(self, name, age): print('Son1的init開始被調用') self.age = age super().__init__(name) # 單繼承不能提供全部參數 print('Son1的init結束被調用') class Grandson(Son1): def __init__(self, name, age, gender): print('Grandson的init開始被調用') super().__init__(name, age) # 單繼承不能提供全部參數 print('Grandson的init結束被調用') gs = Grandson('grandson', 12, '男') print('姓名: ', gs.name) print('年齡: ', gs.age) #print('性別: ', gs.gender) print("******單繼承使用super().__init__ 發生的狀態******\n\n")

print("******多繼承使用super().__init__ 發生的狀態******") class Parent(object): def __init__(self, name, *args, **kwargs): # 為避免多繼承報錯,使用不定長參數,接受參數 print('parent的init開始被調用') self.name = name print('parent的init結束被調用') class Son1(Parent): # 為避免多繼承報錯,使用不定長參數,接受參數 def __init__(self, name, age, *args, **kwargs): print('Son1的init開始被調用') self.age = age super().__init__(name, *args, **kwargs) # 為避免多繼承報錯,使用不定長參數,接受參數 print('Son1的init結束被調用') class Son2(Parent): # 為避免多繼承報錯,使用不定長參數,接受參數 def __init__(self, name, gender, *args, **kwargs): print('Son2的init開始被調用') self.gender = gender super().__init__(name, *args, **kwargs) # 為避免多繼承報錯,使用不定長參數,接受參數 print('Son2的init結束被調用') class Grandson(Son1, Son2): def __init__(self, name, age, gender): print('Grandson的init開始被調用') # 多繼承時,相對於使用類名.__init__方法,要把每個父類全部寫一遍 # 而super只用一句話,執行了全部父類的方法,這也是為何多繼承需要全部傳參的一個原因 # super(Grandson, self).__init__(name, age, gender) super().__init__(name, age, gender) print('Grandson的init結束被調用') print(Grandson.__mro__) gs = Grandson('grandson', 12, '男') print('姓名: ', gs.name) print('年齡: ', gs.age) print('性別: ', gs.gender) print("******多繼承使用super().__init__ 發生的狀態******\n\n")
19.多態 polymorphic
多態概述:
1.多態是指在有繼承/派生關系的類中,調用基類對象的方法,實際能調用子類的覆蓋方法的現象叫多態
2.多態是不同的子類對象調用相同的父類方法產生不同的執行結果,即多態要以繼承和重寫父類方法為前提
3.多態是一種類型的多種形態,多個子類去繼承父類,那么每一個子類都是父類的一種形態
4.Python的全部對象只有"運行時狀態(動態)"沒有"C++/Java"里的"編譯時狀態(靜態)"
5.Python中沒有多態但處處是多態,因為Python中所有的類都繼承自object基類
6.Python要實現靜態就只能在防范命名時不要覆蓋基類的方法

class Shape: def draw(self): pass 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) # 正在畫一個圓 my_draw(s2) # 正在畫一個點

class MiniOS(object): """MiniOS 操作系統類 """ def __init__(self, name): self.name = name self.apps = [] # 安裝的應用程序名稱列表 def __str__(self): return "%s 安裝的軟件列表為 %s" % (self.name, str(self.apps)) def install_app(self, app): # 判斷是否已經安裝了軟件 if app.name in self.apps: print("已經安裝了 %s,無需再次安裝" % app.name) else: app.install() self.apps.append(app.name) class App(object): def __init__(self, name, version, desc): self.name = name self.version = version self.desc = desc def __str__(self): return "%s 的當前版本是 %s - %s" % (self.name, self.version, self.desc) def install(self): print("將 %s [%s] 的執行程序復制到程序目錄..." % (self.name, self.version)) class PyCharm(App): pass class Chrome(App): def install(self): print("正在解壓縮安裝程序...") super().install() linux = MiniOS("Linux") print(linux) pycharm = PyCharm("PyCharm", "1.0", "python 開發的 IDE 環境") chrome = Chrome("Chrome", "2.0", "谷歌瀏覽器") linux.install_app(pycharm) linux.install_app(chrome) linux.install_app(chrome) print(linux) """執行結果 Linux 安裝的軟件列表為 [] 將 PyCharm [1.0] 的執行程序復制到程序目錄... 正在解壓縮安裝程序... 將 Chrome [2.0] 的執行程序復制到程序目錄... 已經安裝了 Chrome,無需再次安裝 Linux 安裝的軟件列表為 ['PyCharm', 'Chrome'] """
20.鴨子類型 Duck typing
鴨子類型概述:
1.一個對象有效的語義,不是由繼承自特定的類或實現特定的接口,而是由"當前方法和屬性的集合"決定
2.鴨子類型是動態類型的一種風格,看着像鴨子,它就是鴨子
3.鴨子類型源自鴨子測試: 當看到一只鳥走起來像鴨子,游泳起來像鴨子,叫起來也像鴨子,那么這只鳥就可以被稱為鴨子
4.鴨子類型中的規范全憑自覺,沒有強制規定,我們不需要關心對象是什么類型到底是不是鴨子,只需要關心對象的行為

# Srt,List,Tuple這些類都有相同的方法,那么這些類互稱鴨子 class Str: def index(self): pass class List: def index(self): pass class Tuple: def index(self): pass s = Str() s.index() l = List() l.index() t = Tuple() t.index() # Str類,List類和Tuple類完全沒有耦合性,但是在某種意義上他們卻統一了一個標准 # 對相同的功能設定了相同的名字,這樣方便開發,這個方法就可以互成為鴨子類型

# 用鴨子類型實現多態 class Duck: def quack(self): print "Duck imitate duck" class Bird: def quack(self): print "bird imitate duck" class Chicken: def quack(self): print "chicken imitate duck" def in_the_forest(duck): duck.quack() duck = Duck() bird = Bird() chicken = Chicken() for x in [duck, bird, chicken]: in_the_forest(x)
21.包裝(二次加工標准類型)
包裝概述: 基於標准數據類型來定制我們自己的數據類型,新增/改寫方法

# 包裝 = 繼承 + 派生 class List(list): # 定義一個方法取列表中間值 def show_midlle(self): min_index = int(len(self) / 2) return self[min_index] # 重寫列表的append方法,限制只允許添加字符串 def append(self, p_object): if type(p_object) is str: super().append(p_object) else: print("類型錯誤,只允許添加字符串") l1 = List("helloworld") print(l1, type(l1)) # ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'] <class '__main__.List'> print("列表%s的中間值是%s" %(l1, l1.show_midlle())) # 列表['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']的中間值是w l1.append("coco") l1.append(123) # 類型錯誤,只允許添加字符串 print(l1) # ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'coco']

# 給clear加權限限制 class List(list): def __init__(self, item, tag=False): super().__init__(item) self.tag = tag def append(self, p_object): if not isinstance(p_object, str): raise TypeError super().append(p_object) def clear(self): if not self.tag: raise PermissionError super().clear() l=List([1, 2, 3], False) print(l) print(l.tag) l.append('saf') print(l) # l.clear() #異常 l.tag = True l.clear()
22.授權
授權概述:
1.授權是包裝的一個特性,包裝一個類型通常是對已存在的類型的一些定制
2.授權的這種做法可以新建修改或刪除原有產品的功能,其它的則保持原樣
3.授權的過程,即所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權給對象的默認屬性
4.實現授權的關鍵點就是覆蓋__getattr__方法

# 授權-->是組合的運用 import time class Open: def __init__(self, filename, mode="r", encoding="utf-8"): self.filename = filename self.file = open(filename, mode, encoding=encoding) self.mode = mode self.encoding = encoding def write(self, line): t = time.strftime("%Y-%m-%d %X") self.file.write("%s %s" % (t, line)) def __getattr__(self, item): return getattr(self.file, item, "沒有找到%s" % item) f1 = Open("a.txt", "r+") print(f1.file) # <_io.TextIOWrapper name='a.txt' mode='r+' encoding='utf-8'> print(f1.reads) # 沒有找到reads print(f1.read) # <built-in method read of _io.TextIOWrapper object at 0x00000000056F51F8> f1.write("hello world\n") f1.seek(0) # 移動文件指針到文件開頭 print("讀文件%s內容是%s" % (f1.filename, f1.read())) # 讀文件a.txt內容是2020-07-31 10:54:27 hello world

# 加上b模式支持 import time class FileHandle: def __init__(self, filename, mode='r', encoding='utf-8'): if 'b' in mode: self.file = open(filename, mode) else: self.file = open(filename, mode, encoding=encoding) self.filename = filename self.mode = mode self.encoding = encoding def write(self, line): if 'b' in self.mode: if not isinstance(line, bytes): raise TypeError('must be bytes') self.file.write(line) def __getattr__(self, item): return getattr(self.file, item) def __str__(self): if 'b' in self.mode: res = "<_io.BufferedReader name='%s'>" %self.filename else: res = ("<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" % (self.filename, self.mode, self.encoding)) return res f1 = FileHandle('b.txt','wb') # f1.write('你好啊') # 自定制的write,不用在進行encode轉成二進制去寫了 f1.write('你好啊'.encode('utf-8')) print(f1) # <_io.BufferedReader name='b.txt'> f1.close()

class List: def __init__(self, seq): self.seq = seq def append(self, p_object): '派生自己的append加上類型檢查,覆蓋原有的append' if not isinstance(p_object, int): raise TypeError('must be int') self.seq.append(p_object) @property def mid(self): '新增自己的方法' index = len(self.seq) // 2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l = List([1, 2, 3]) print(l) # [1, 2, 3] l.append(4) print(l) # [1, 2, 3, 4] # l.append('33') # 會報錯,必須為int類型 print(l.mid) # 3 # 基於授權,獲得insert方法 l.insert(0, -123) print(l) # [-123, 1, 2, 3, 4]

class List: def __init__(self, seq, permission=False): self.seq = seq self.permission = permission def clear(self): if not self.permission: raise PermissionError('not allow the operation') self.seq.clear() def __getattr__(self, item): return getattr(self.seq, item) def __str__(self): return str(self.seq) l = List([1, 2, 3]) # l.clear() # 此時沒有權限,拋出異常 l.permission = True print(l) # [1, 2, 3] l.clear() # l.clear() print(l) # [] #基於授權,獲得insert方法 l.insert(0, -123) print(l) # [-123]
23.覆蓋 override
覆蓋概述:
1.覆蓋是指在有繼承派生關系的類中,子類中實現了與基類(超類)同名的方法
2.在子類實例調用方法時,實際調用的是子類中的覆蓋版本,這種現象叫做覆蓋

class A: def work(self): print("A類的work方法被調用") class B(A): def work(self): print("B類的work方法被調用") b = B() b.work() # 子類已經覆蓋了父類的方法
24.函數重寫 overwrite
函數重寫概述: 在自定義的類中,通過添加特定的方法,讓自定義的類實例化的對象能像內建對象一樣進行內建函數操作
1.字符串顯示函數重寫
repr(obj) 返回一個能代表此對象的字符串的表達式(這個字符串是給Python解釋器識別用的)
repr(obj) 函數用於遠程通信數據校驗 例如: eval(repr(obj)) == obj
str(obj) 通過給定的對象返回一個字符串(這個字符串通常是給人閱讀的)
repr(obj) 函數的重寫方法 def __repr__(self)
str(obj) 函數的重寫方法 def __str__(self)
當對象沒有 __str__方法時, 則返回__repr__(self)的值

class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b=B() print('%s'%b) # str : class B print('%r'%b) # repr : class B
2.內建函數重寫
obj.__abs__() 對應 abs(obj)
obj.__len__() 對應 len(obj)
obj.__reversed__() 對應 reversed(obj)
obj.__round__() 對應 round(obj)

# 此示例示意 abs(obj) 函數的重寫方法 obj.__abs__() 方法的使用 class MyInteger: def __init__(self, value): self.data = value def __repr__(self): return 'MyInteger(%d)' % self.data def __abs__(self): if self.data < 0: return MyInteger(-self.data) # 創建一個新的以對象並返回 return MyInteger(self.data) def __len__(self): '''len(x)函數規定只能返回整數值,因此此方法不能返回字符串等其它類型的值''' return 100 I1 = MyInteger(-10) print(I1) # <-- 此處等同於print(str(I1)) I2 = abs(I1) # I2 = MyInteger(10) print(I2) # MyInteger(10) print(len(I1)) # I1.__len__()
3.數值轉換函數重寫
obj.__complex__() 對應 complex(obj)
obj.__int__() 對應 int(obj)
obj.__float__() 對應 float(obj)
obj.__bool__() 對應 bool(obj)
4.布爾測試函數的重寫
布爾測試函數概述:
1.用於bool(obj)函數取值,用於if語句真值表達式中,用於while語句真值表達式中
2.布測試式方法的查找順序是 __bool__方法,其次是__len__方法,如果都沒有則返回True
3.語法:
def __bool__(self):
pass

# 此示例示意 bool 真值測試函數的重寫 class MyList: '''定義一個容器類,用於存儲任意類型的數據其內部的存儲方式用list實現''' def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%s)' % self.data def __len__(self): print("__len__ 方法被調用!") return len(self.data) # 返回列表的長度 def __bool__(self): print('__bool__方法被調用') '''此方法用於bool(obj) 函數取值,優先取此函數的返回值,此方法用於定義bool(obj)的取值規則''' # 規則,所有元素的和為0,則返回False否則返回True return sum(self.data) != 0 myl = MyList([1, -2, 5, -4]) print(myl) # MyList([1, -2, 5, -4]) print('bool(myl) =', bool(myl)) if myl: print('myl 的布爾值為True') else: print('myl 的布爾值為False')
5.自定制格式化字符串重寫 __format__

format_dict = { 'nat':'{obj.name}-{obj.addr}-{obj.type}', # 學校名-學校地址-學校類型 'tna':'{obj.type}:{obj.name}:{obj.addr}', # 學校類型:學校名:學校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}', # 學校類型/學校地址/學校名 } class School: def __init__(self, name, addr, type): self.name = name self.addr = addr self.type = type def __repr__(self): return 'School(%s,%s)' %(self.name, self.addr) def __str__(self): return '(%s,%s)' %(self.name, self.addr) def __format__(self, format_spec): # if format_spec if not format_spec or format_spec not in format_dict: format_spec = 'nat' fmt = format_dict[format_spec] return fmt.format(obj=self) s1 = School('oldboy1', '北京', '私立') print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1) ''' str函數或者print函數--->obj.__str__();repr或者交互式解釋器--->obj.__repr__() 如果__str__沒有被定義,那么就會使用__repr__來代替輸出,這倆方法的返回值必須是字符串,否則拋出異常 ''' print(format(s1, 'nat')) print(format(s1, 'tna')) print(format(s1, 'tan')) print(format(s1, 'asfdasdffd'))
6.反射函數重寫

class Foo(): x = 1 def __init__(self): self.y = 1 def __getattr__(self, item): print("你要找的%s屬性不存在" % item) # 重寫內置刪除方法,限制刪除條件 def __delattr__(self, item): print("執行了刪除%s屬性的操作" % item) self.__dict__.pop(item) # 重寫內置設置方法,限制設置條件 def __setattr__(self, key, value): print("你執行了設置%s屬性值為%s的操作" %(key, value)) self.__dict__[key] = value f1 = Foo() f1.x = 10 f1.name = "coco" # 設置屬性時會觸發__setattr__ print(f1.z) # 屬性不存在時,會自動觸發__getattr__ del f1.y # 刪除屬性時,會觸發__delattr__ print(f1.__dict__)
25.運算符重載
運算符重載概述:
1.讓自定義的類實例化的對像能夠使用運算符進行操作,像數學表達式一樣進行運算操作讓程序簡潔易讀
2.運算符重載方法的參數已經有固定的含義,不建議改變原有的含義
1.算術運算符
方法名 運算符
__add__ 加法 +
__sub__ 減法 -
__mul__ 乘法 *
__truediv__ 除法 /
__floordiv__ 地板除 //
__mod__ 取模(求余) %
__pow__ 冪 **
二元運算符重載方法格式:
def __xxx__(self, other):
pass

# 此程序示意運算符重載 class MyNumber: def __init__(self, v): self.data = v def __repr__(self): return "MyNumber(%d)" % self.data def __add__(self, other): print("__add__方法被調用") obj = MyNumber(self.data + other.data) return obj def __sub__(self, other): return MyNumber(self.data - other.data) n1 = MyNumber(100) n2 = MyNumber(200) # n3 = n1.__add__(n2) n3 = n1 + n2 # 等同於 n1.__add__(n2) print(n3) n4 = n2 - n1 print('n4 =', n4) # MyNumber(100)

# 自定義對象的加法運算符重載規則 class MyType: """實現對象中*號求和""" def __init__(self, s): self.s = s def __add__(self, other): return self.s.count('*') + other.s.count('*') obj1 = MyType('ccasdd***2343*****') obj2 = MyType('vvsvvndk***3423**6517***') print(obj1 + obj2) print(obj1.__add__(obj2))
2.反向算術運算符
方法名 運算符
__radd__ 加法 +
__rsub__ 減法 -
__rmul__ 乘法 *
__rtruediv__ 除法 /
__rfloordiv__ 地板除 //
__rmod__ 取模(求余) %
__rpow__ 冪 **

# 此示例示意返向算術運算符的重載 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): # %r 等同與用repr函數取值--> repr(self.data) return 'MyList(%r)' % self.data def __mul__(self, rhs): return MyList(self.data * rhs) def __rmul__(self, lhs): print("__rmul__被調用, lhs=", lhs) return MyList(self.data * lhs) # lhs (left hand side) L1 = MyList([1, 2, 3]) L2 = MyList(range(4, 7)) L4 = L1 * 2 # 實現乘法運算調用的是自定義的__mul__方法 print('L4 =', L4) # MyList([1,2,3,1,2,3]) L5 = 2 * L1 # 此時調用的是自定義的反向乘法運算符重載__rmul__方法 print(L5)
3.復合賦值運算符重載
方法名 運算符
__iadd__ 加法 +=
__isub__ 減法 -=
__imul__ 乘法 *=
__itruediv__ 除法 /=
__ifloordiv__ 地板除 //=
__imod__ 取模(求余) %=
__ipow__ 冪 **=
示例:

# 此示例示意復合賦值算術運算符的重載 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%r)' % self.data def __add__(self, rhs): print("__add__方法被調用") return MyList(self.data + rhs.data) def __iadd__(self, rhs): print("__iadd__方法被調用") self.data.extend(rhs.data) return self L1 = MyList([1, 2, 3]) L2 = MyList(range(4, 7)) print("id(L1) =", id(L1)) # +=優先調用__iadd__方法,沒有再調用__add__方法 L1 += L2 # 相當於 L1 = L1 + L2 print('L1 =', L1) print("id(L1) =", id(L1))
4.比較的運算符的重載
__lt__ < 小於
__le__ <= 小於等於
__gt__ > 大於
__ge__ >= 大於等於
__eq__ == 等於
__ne__ != 不等於
比較運算符通常返回True或False
5.位運算符重載
__inert__ ~ 取反(一元運算符)
__and__ & 按位與(交集)
__or__ | 按位或(並集)
__xor__ ^ 按位異或(對稱補集)
__lshift__ << 左移
__rshift__ >> 右移
6.反向位運算符重載
__rand__ & 按位與(交集)
__ror__ | 按位或(並集)
__rxor__ ^ 按位異或(對稱補集)
__rlshift__ << 左移
__rrshift__ >> 右移
7.復合賦值運算符重載
__iand__ &= 按位與(交集)
__ior__ |= 按位或(並集)
__ixor__ ^= 按位異或(對稱補集)
__ilshift__ <<= 左移
__irshift__ >>= 右移
8.一元運算符的重載
__neg__ - 負號
__pos__ + 正號
__invert__ ~ 按位取反
格式:
def __xxx__(self):
pass

# 此示例示意一元運算符的重載 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%r)' % self.data def __neg__(self): print("__neg__方法被調用!") L = (-x for x in self.data) return MyList(L) L1 = MyList([1, -2, 3, -4, 5]) print("L1 =", L1) L2 = -L1 print("L2 =", L2)
9.成員運算符的重載
in
not in 相當於 in 取反,所有只需要重載 in 即可
格式:
def __contains__(self, e): # e代表元素
pass

# 此示例示意 in / not in 運算符的重載 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%r)' % self.data def __contains__(self, e): # e 代表測試元素 print("__contains__被調用") for x in self.data: if e == x: # 如果相同,則說明e在列表中 return True return False L1 = MyList([1, -2, 3, -4, 5]) if 2 in L1: # 需要重載 __contains__ 方法 print("2在L1中") else: print("2不在L1中")
10.索引和切片運算符的重載
重載方法: 讓自定義的類型的對象能夠支持索引和切片操作
__getitem__(self, i) 用於索引/切片取值
__setitem__(self, i) 用於索引/切片賦值
__delitem__(self, i) 用於del語句刪除索引操作

# 此示例示意索引 index 和切片 slice 運算符的重載 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return "MyList(%r)" % self.data def __getitem__(self, i): print("__getitem__被調用", i) if type(i) is slice: print("正在進行切片操作") elif type(i) is int: print("正在進行索引操作") return self.data[i] def __setitem__(self, i, v): print("__setitem__被調用", i, v) self.data[i] = v def __delitem__(self, i): print("__delitem__被調用", i) del self.data[i] L1 = MyList([1, -2, 3, -4, 5]) # 切片操作調用的是__getitem__方法,L1[0:2]等同於L1[slice(0, 2, None)] print(L1[0:2]) # [1, -2] print(L1[2]) # 3 L1[1] = 2 # MyList([1, 2, 3, -4, 5]) del L1[:2] print(L1) # MyList([3, -4, 5])
26.類裝飾器
1.類的裝飾器無參數示例

def decorate(cls): print('類的裝飾器開始運行啦------>') return cls @decorate # 無參:People=decorate(People) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1=People('egon', 18, 3.3)
2.類的裝飾器有參數示例

def typeassert(**kwargs): def decorate(cls): print('類的裝飾器運行:', kwargs) return cls return decorate # 有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs # 有參:2.People=decorate(People) @typeassert(name=str, age=int, salary=float) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1=People('egon', 18, 3.3) # 類的裝飾器運行:{'name': <class 'str'>, 'age': <class 'int'>, 'salar': <class 'float'>}

class Test(object): def __init__(self, func): print("---初始化---") print("func name is %s" % func.__name__) self.__func = func def __call__(self): print("---裝飾器中的功能---") self.__func() """說明: 1.用Test來裝作裝飾器對test函數進行裝飾的時,先創建Test的實例對象,再把test這個函數名當做參數傳遞到__init__方法中 即在__init__方法中的屬性__func指向了test指向的函數,而test則指向了用Test創建出來的實例對象 2.當在使用test()進行調用時,就相當於讓這個對象(),因此會調用這個對象的__call__方法 3.為了能夠在__call__方法中調用原來test指向的函數體,在__init__方法中需要一個實例屬性來保存這個函數體的引用 所以才有了self.__func = func這句代碼,從而在調用__call__方法中能夠調用到test之前的函數體 """ @Test def test(): print("----test---") test() """執行結果 ---初始化--- func name is test ---裝飾器中的功能--- ----test--- """
3.描述符裝飾器示例

class Typed: def __init__(self, key, expected_type): self.key = key self.expected_type = expected_type def __get__(self, instance, owner): print('get方法') # print('instance參數【%s】' % instance) # print('owner參數【%s】' % owner) return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') # print('instance參數【%s】' % instance) # print('value參數【%s】' % value) # print('====>', self) if not isinstance(value, self.expected_type): # print('你傳入的類型不是字符串,錯誤') # return raise TypeError('%s 傳入的類型不是%s' % (self.key, self.expected_type)) instance.__dict__[self.key] = value def __delete__(self, instance): print('delete方法') # print('instance參數【%s】' % instance) instance.__dict__.pop(self.key) def deco(**kwargs): def wrapper(obj): for key,val in kwargs.items(): print("-->", key, val) setattr(obj, key, Typed(key, val)) return obj return wrapper @deco(name=str, age=int) class People: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary p1 = People('coco', 13, 13.3) print(p1.__dict__) """執行結果 --> name <class 'str'> --> age <class 'int'> set方法 set方法 {'name': 'coco', 'age': 13, 'salary': 13.3} """
4.類裝飾函數示例

class Test: def __init__(self, func): # 要使__call__方法中調用原來get_str指向的函數體,需要在__init__方法中就需要一個實例屬性來保存這個函數體的引用 self.__func = func def __call__(self): print("Test類裝飾的內容") return self.__func() # 用Test來裝作裝飾器對get_str函數進行裝飾的時候,首先會創建Test的實例對象 # 然后把get_str這個函數名當做參數傳遞到__init__方法中的屬性__func指向了get_str指向的函數 @Test # 等同於get_str = Test(get_str) def get_str(): print("this is get_str function") return "ok" # get_str指向了用Test創建出來的實例對象,當在使用get_str()進行調用時 # 就相當於讓這個對象(),因此會調用這個對象的__call__方法 ret = get_str() print(ret) """執行結果 Test類裝飾的內容 this is get_str function ok """
27.類的約束-抽象類(接口類)
1.抽象類概述:
1.抽象類的主要功能就是定制一個規范,讓其子類必須按照此規范實現,抽象類本身不能被實例化
2.制定了一個規范,讓繼承父類的子類必須實現父類的抽象方法,如果子類沒有實現父類的抽象方法則子類實例化對象時報錯
3.抽象類和接口類做的事情: 制定一個類的metaclass是ABCMeta那么這個類就變成了一個抽象類
4.Python中有原生實現抽象類的方法,但沒有原生實現接口類的方法
5.在Java來區分抽象類和接口類
1.抽象類: 只能被子類單繼承,抽象類的方法可以實現
2.接口類: 可以被子類多繼承,接口類的所有方法都不應該實現,只定義方法后用pass占位
3.抽象類和接口類只需要知道區分,不用按照Java規定去寫Python中的代碼

from abc import ABCMeta from abc import abstractmethod class Payment(metaclass=ABCMeta): # 抽象類規范和約束,metaclass指定的是一個元類 @abstractmethod def pay(self): # 抽象方法 pass class Alipay(Payment): def __init__(self,money): self.money = money def pay(self): print('使用支付寶支付了%s' %self.money) class Jdpay(Payment): def __init__(self, money): self.money = money def pay(self): print('使用京東支付了%s' % self.money) class Wechatpay(Payment): def __init__(self,money): self.money = money def pay(self): print('使用微信支付了%s' % self.money) def pay(obj): obj.pay() # 子類中沒有實現pay方法時,子類實例化對象時會報錯 w1 = Wechatpay(200) a1 = Alipay(200) j1 = Jdpay(100) # 統一化接口設計,也叫歸一化設計: 不管是哪一個類的對象,都調用同一個函數去完成相似的功能 pay(a1) # 使用支付寶支付了200 pay(j1) # 使用京東支付了100

# 用元類的思想來實現約束時子類必須實現pay方法 class Payment: """此類什么都不做,就是制定一個標准,誰繼承我就必須定義我里面的方法""" def pay(self, money): raise Exception("你沒有實現pay方法") class QQpay(Payment): def pay(self, money): print('使用qq支付%s元' % money) class Alipay(Payment): def pay(self, money): print('使用阿里支付%s元' % money) class Wechatpay(Payment): def fuqian(self, money): print('使用微信支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() c = Wechatpay() pay(a, 100) # 使用阿里支付100元 pay(b, 200) # 使用qq支付200元 pay(c, 300) # Exception: 你沒有實現pay方法
28.類的約束-元類
1.元類概述:
1.元類就是用來創建這些類(對象)的,元類就是類的類,type就是Python在背后用來創建所有類的元類
2.自定義元類目的是攔截類的創建, 修改類, 返回修改之后的類
3.類中有一個屬性 __metaclass__ 其用來表示該類由誰來實例化創建,可以用該屬性設置一個type類的派生類,從而查看類創建的過程
4.type可以動態創建類,type(類名, 由父類名稱組成的元組(針對繼承的情況,可以為空), 包含屬性的字典(名稱和值))
5.元類創建類,元類創建元類驗證

class A: pass a = A() a.__class__ # __main__.A a.__class__.__class__ # type a.__class__.__class__.__class__ # type
2.type動態創建類

Test = type("Test", (), {"num1": 100, "num2": 200}) Test.__dict__ obj = Test() obj.num1 obj.num2 """執行結果 mappingproxy({'num1': 100, 'num2': 200, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}) 100 200 """
3.較為完整的使用type創建類的方式

class A(object): num = 100 def print_b(self): print("實例方法調用", self.num) @staticmethod def print_static(): print("----靜態方法調用-----") @classmethod def print_class(cls): print("類方法調用", cls.num) B = type("B", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class}) b = B() b.print_b() # 實例方法調用 100 b.print_static() # ----靜態方法調用---- b.print_class() # 類方法調用 100
4.自定義元類的 __init__ 和 __call__ 方法

class MyType(type): def __init__(self, a, b, c): print("a:", a) # a: Foo print("b:", b) # b: () print("c:", c) # c: {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x102ead560>} print("元類初始化執行完成") # 元類初始化執行完成 def __call__(self, *args, **kwargs): print(args, kwargs) # ('coco', 18) {} obj = object.__new__(self) # object.__new__(Foo)-->f1 self.__init__(obj, *args, **kwargs) # Foo.__init__(f1, *args, **kwargs) return obj class Foo(object, metaclass=MyType): # Foo=MyType(Foo, "Foo", (), {})-->__init__ def __init__(self, name, age): self.name = name self.age = age f1 = Foo("coco", 18) print(f1) # <__main__.Foo object at 0x00000000055BF6A0> print(f1.__dict__) # {'name': 'coco', 'age': 18}
5.自定義元類的 __new__ 方法

class UpperAttrMetaClass(type): # __new__ 是在__init__之前被調用的特殊方法,很少用到__new__,除非希望能夠控制對象的創建 # __new__是用來創建對象並返回的方法,而__init__只是用來將傳入的參數初始化給對象 def __new__(cls, class_name, class_parents, class_attr): # 遍歷屬性字典,把不是__開頭的屬性名字變為大寫 new_attr = {} for name, value in class_attr.items(): if not name.startswith("__"): new_attr[name.upper()] = value # 方法1: 通過'type'來做類對象的創建 return type(class_name, class_parents, new_attr) # 方法2: 復用type.__new__方法,這就是基本的OOP編程,沒什么魔法 # return type.__new__(cls, class_name, class_parents, new_attr) # python3的用法 class Foo(object, metaclass=UpperAttrMetaClass): bar = 'bip' # python2的用法 # class Foo(object): # __metaclass__ = UpperAttrMetaClass # bar = 'bip' print(hasattr(Foo, 'bar')) # False print(hasattr(Foo, 'BAR')) # True f = Foo() print(f.BAR) # bip
29.元類的應用-ORM
1.ORM概述:
1.ORM是python編程語言后端web框架Django的核心思想"Object Relational Mapping",即對象-關系映射,簡稱ORM
2.創建一個實例對象,用創建它的類名當做數據表名,用創建它的類屬性對應數據表的字段,當對這個實例對象操作時,能夠對應MySQL語句
3.ORM就是讓開發者在操作數據庫的時候,能夠像操作對象時通過xxxx.屬性=yyyy一樣簡單
2.通過元類簡單實現ORM中的insert功能

class ModelMetaclass(type): def __new__(cls, name, bases, attrs): mappings = dict() # 判斷是否需要保存 for k, v in attrs.items(): # 判斷是否是指定的StringField或者IntegerField的實例對象 if isinstance(v, tuple): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v # 刪除這些已經在字典中存儲的屬性 for k in mappings.keys(): attrs.pop(k) # 將之前的uid/name/email/password以及對應的對象引用,類名字 attrs['__mappings__'] = mappings # 保存屬性和列的映射關系 attrs['__table__'] = name # 假設表名和類名一致 return type.__new__(cls, name, bases, attrs) class Model(object, metaclass=ModelMetaclass): def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value) def save(self): fields = [] args = [] for k, v in self.__mappings__.items(): fields.append(v[0]) args.append(getattr(self, k, None)) args_temp = list() for temp in args: # 判斷入如果是數字類型 if isinstance(temp, int): args_temp.append(str(temp)) elif isinstance(temp, str): args_temp.append("""'%s'""" % temp) sql = ('insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))) print('SQL: %s' % sql) class User(Model): uid = ('uid', "int unsigned") name = ('username', "varchar(30)") email = ('email', "varchar(30)") password = ('password', "varchar(30)") # 實例化對象首先會調用元類ModelMetaclass的__new__構造方法,再調用父類Model的__init__初始化方法 u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd') # print(u.__dict__) u.save()
30.即時通訊和微信面向對象設計示例
1.騰訊即時通信模塊,初級封裝

#! /usr/bin/env python # coding: utf-8 import random import time class Message(object): def __init__(self, msgarr=[], toacc=''): self.msgbody = msgarr # 此處為MsgDict對象實例的列表或者空列表 self.toacc = toacc # toacc為字符串(單發)或者列表(批量發) self.msgrandom = random.randint(1, 1000000000) self.msgrequest = { 'To_Account': toacc, # 消息接收方賬號 'MsgRandom': self.msgrandom, # 消息隨機數,由隨機函數產生 'MsgBody': [t.msg for t in msgarr] } def del_option(self, option): if option in (set(self.msgrequest) - set(['To_Account', 'MsgRandom', 'MsgBody'])): self.__dict__.pop(option) self.msgrequest.pop(option) def append_msg(self, msg): self.msgbody.append(msg) self.msgrequest['MsgBody'].append(msg.msg) def insert_msg(self, index, msg): self.msgbody.insert(index, msg) self.msgrequest['MsgBody'].insert(msg.msg) def del_msg(self, index): if index in range(len(self.msgbody)): del self.msgbody[index] del sel.msgrequest['MsgBody'][index] def set_from(self, fromacc): # 指定消息的發送方,默認為服務器發送 self.fromacc = fromacc self.msgrequest['From_Account'] = fromacc def set_to(self, toacc): # 指定消息的接收方,可以為String(單發),可以為List(批量發送) self.toacc = toacc self.msgrequest['To_Account'] = toacc def refresh_random(self): self.msgrandom = random.randint(1, 1000000000) self.msgrequest['MsgRandom'] = self.msgrandom, # 消息隨機數,由隨機函數產生 def set_sync(self, sync): # 同步選項: 1, 把消息同步到From_Account在線終端和漫游上 # 2, 消息不同步至From_Account # 若不填寫,默認情況下會將消息同步 # 僅在單發單聊消息中可調用 self.sync = sync self.msgrequest['SyncOtherMachine'] = sync def set_timestamp(self): # 設置消息時間戳,unix時間, 僅在單發單聊消息中可以調用 self.timestamp = int(time.time()) self.msgrequest['MsgTimeStamp'] = self.timestamp def set_offlinepush(self, pushflag=0, desc='', ext='', sound=''): # 僅適用於APNa推送,不適用於安卓廠商推送 self.msgrequest['OfflinePushInfo'] = { 'PushFlag': pushflag, 'Desc': desc, 'Ext': ext, 'Sound': sound } class MsgDict(object): def __init__(self, msgtype='', msgcontent={}): self.msgtype = msgtype self.msgcontent = msgcontent @property def msg(self): return { 'MsgType': self.msgtype, 'MsgContent': self.msgcontent } def set_content(self, content): self.msgcontent = content class TextMsg(MsgDict): def __init__(self, text='', msgtype='TIMTextElem'): self.text = text content = {'Text': text} super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_text(self, text): self.text = text self.msgcontent['Text'] = text class LocationMsg(MsgDict): def __init__(self, desc='', latitude=0, longitude=0, msgtype='TIMLocationElem'): self.desc = desc self.latitude = latitude self.longitude = longitude content = { 'Desc': desc, # 地理位置描述信息, String 'Latitude': latitude, # 緯度, Number 'Longitude': longitude # 經度, Number } super(LocationMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_desc(self, desc): self.desc = desc self.msgcontent['Desc'] = desc def set_location(self, latitude, longitude): self.latitude = latitude self.longitude = longitude self.msgcontent['Latitude'] = latitude self.msgcontent['Longitude'] = longitude def set_latitude(self, latitude): self.latitude = latitude self.msgcontent['Latitude'] = latitude def set_longitude(self, longitude): self.longitude = longitude self.msgcontent['Longitude'] = longitude class FaceMsg(MsgDict): def __init__(self, index=1, data='', msgtype='TIMFaceElem'): self.index = index self.data = data content = { 'Index': index, # 表情索引,用戶自定義, Number 'Data': data # 額外數據, String } super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_index(self, index): self.index = index self.msgcontent['Index'] = index def set_data(self, data): self.data = data self.msgcontent['Data'] = data class CustomMsg(MsgDict): def __init__(self, data='', desc='', ext='', sound='', msgtype='TIMCustomElem'): self.data = data self.desc = desc self.ext = ext self.sound = sound content = { 'Data': data, # 自定義消息數據,不作為APNS的payload中字段下發,故從payload中無法獲取Data字段, String 'Desc': desc, # 自定義消息描述,當接收方為iphone后台在線時,做ios離線Push時文本展示 'Ext': ext, # 擴展字段,當接收方為ios系統且應用處在后台時,此字段作為APNS請求包Payloads中的ext鍵值下發,Ext的協議格式由業務方確定,APNS只做透傳 'Sound': sound # 自定義APNS推送鈴聲 } super(CustomMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_data(self, data): self.data = data self.msgcontent['Data'] = data def set_desc(self, desc): self.desc = desc self.msgcontent['Desc'] = desc def set_ext(self, ext): self.ext = ext self.msgcontent['Ext'] = ext def set_sound(self, sound): self.sound = sound self.msgcontent['Sound'] = sound class SoundMsg(MsgDict): def __init__(self, uuid='', size=0, second=0, msgtype='TIMSoundElem'): self.uuid = uuid self.size = size self.second = second content = { 'UUID': uuid, # 語音序列號,后台用於索引語音的鍵值,String 'Size': size, # 語音數據大小, Number 'Second': second # 語音時長,單位秒 Number } super(SoundMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_uuid(self, uuid): self.uuid = uuid self.msgcontent['UUID'] = uuid def set_size(self, size): self.size = size self.msgcontent['Size'] = size def set_second(self, second): self.second = second self.msgcontent['Second'] = second class ImageMsg(MsgDict): def __init__(self, uuid='', imgformat=0, imginfo=[], msgtype='TIMImageElem'): self.uuid = uuid self.imgformat = imgformat self.imginfo = imginfo content = { 'UUID': uuid, # 圖片序列號,后台用於索引語音的鍵值,String 'ImageFormat': imgformat, # 圖片格式, BMP=1, JPG=2, GIF=3, 其他=0, Number 'ImageInfoArray': [t.info for t in imginfo] # 原圖,縮略圖或者大圖下載信息, Array } super(ImageMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_uuid(self, uuid): self.uuid = uuid self.msgcontent['UUID'] = uuid def set_format(self, imgformat): self.imgformat = imgformat self.msgcontent['ImageFormat'] = imgformat def append_info(self, info): # info 為ImageInfo對象實例 self.imginfo.append(info) self.msgcontnet['ImageInfoArray'].append(info.info) def insert_info(self, index, info): self.imginfo.insert(index, info) self.msgcontent['ImageInfoArray'].insert(index, info.info) def del_info(self, index): del self.imginfo[index] del self.msgcontent['ImageInfoArray'][index] class FileMsg(MsgDict): def __init__(self, uuid='', size=0, name='', msgtype='TIMFileElem'): self.uuid = uuid self.size = size self.name = name content = { 'UUID': uuid, # 文件序列號,后台用於索引語音的鍵值,String 'FileSize': size, # 文件數據大小, Number 'FileName': name # 文件名稱/路徑, String } super(FileMsg, self).__init__(msgtype=msgtype, msgcontent=content) def set_uuid(self, uuid): self.uuid = uuid self.msgcontent['UUID'] = UUID def set_size(self, size): self.size = size self.msgcontent['FileSize'] = size def set_name(self, name): self.name = name self.msgcontent['FileName'] = name class ImageInfo(object): def __init__(self, itype=1, size=0, width=0, height=0, url=''): # 圖片類型, 1-原圖, 2-大圖, 3-縮略圖, 0-其他 self.itype = itype # 圖片數據大小,Number self.size = size # 圖片寬度,Number self.width = width # 圖片高度, Number self.height = height # 圖片下載地址,String self.url = url @property def info(self): return { 'Type': self.itype, 'Size': self.size, 'Width': self.width, 'Height': self.height, 'URL': self.url } def set_type(self, itype): self.itype = itype def set_size(self, size): self.size = size def set_width(self, width): self.width = width def set_height(self, height): self.height = height def set_url(self, url): self.url = url
2.微信開發包,python實現wechat_sdk開發

from __future__ import unicode_literals import time from wechat_sdk.exceptions import NeedParamError from wechat_sdk.lib.crypto import BasicCrypto from wechat_sdk.lib.request import WechatRequest from wechat_sdk.utils import disable_urllib3_warning class WechatConf(object): """ WechatConf 配置類 該類將會存儲所有和微信開發相關的配置信息, 同時也會維護配置信息的有效性. """ def __init__(self, **kwargs): """ :param kwargs: 配置信息字典, 可用字典 key 值及對應解釋如下: 'token': 微信 Token 'appid': App ID 'appsecret': App Secret 'encrypt_mode': 加解密模式 ('normal': 明文模式, 'compatible': 兼容模式, 'safe': 安全模式(默認)) 'encoding_aes_key': EncodingAESKey 值 (傳入此值必須保證同時傳入 token, appid, 否則拋出異常) 'access_token_getfunc': access token 獲取函數 (用於單機及分布式環境下, 具體格式參見文檔) 'access_token_setfunc': access token 寫入函數 (用於單機及分布式環境下, 具體格式參見文檔) 'access_token_refreshfunc': access token 刷新函數 (用於單機及分布式環境下, 具體格式參見文檔) 'access_token': 直接導入的 access token 值, 該值需要在上一次該類實例化之后手動進行緩存並在此處傳入, 如果不 傳入, 將會在需要時自動重新獲取 (傳入 access_token_getfunc 和 access_token_setfunc 函數 后將會自動忽略此處的傳入值) 'access_token_expires_at': 直接導入的 access token 的過期日期, 該值需要在上一次該類實例化之后手動進行緩存 並在此處傳入, 如果不傳入, 將會在需要時自動重新獲取 (傳入 access_token_getfunc 和 access_token_setfunc 函數后將會自動忽略此處的傳入值) 'jsapi_ticket_getfunc': jsapi ticket 獲取函數 (用於單機及分布式環境下, 具體格式參見文檔) 'jsapi_ticket_setfunc': jsapi ticket 寫入函數 (用於單機及分布式環境下, 具體格式參見文檔) 'jsapi_ticket_refreshfunc': jsapi ticket 刷新函數 (用於單機及分布式環境下, 具體格式參見文檔) 'jsapi_ticket': 直接導入的 jsapi ticket 值, 該值需要在上一次該類實例化之后手動進行緩存並在此處傳入, 如果不 傳入, 將會在需要時自動重新獲取 (傳入 jsapi_ticket_getfunc 和 jsapi_ticket_setfunc 函數 后將會自動忽略此處的傳入值) 'jsapi_ticket_expires_at': 直接導入的 jsapi ticket 的過期日期, 該值需要在上一次該類實例化之后手動進行緩存 並在此處傳入, 如果不傳入, 將會在需要時自動重新獲取 (傳入 jsapi_ticket_getfunc 和 jsapi_ticket_setfunc 函數后將會自動忽略此處的傳入值) 'partnerid': 財付通商戶身份標識, 支付權限專用 'partnerkey': 財付通商戶權限密鑰 Key, 支付權限專用 'paysignkey': 商戶簽名密鑰 Key, 支付權限專用 'checkssl': 是否檢查 SSL, 默認不檢查 (False), 可避免 urllib3 的 InsecurePlatformWarning 警告 :return: """ self.__request = WechatRequest() if kwargs.get('checkssl') is not True: disable_urllib3_warning() # 可解決 InsecurePlatformWarning 警告 self.__token = kwargs.get('token') self.__appid = kwargs.get('appid') self.__appsecret = kwargs.get('appsecret') self.__encrypt_mode = kwargs.get('encrypt_mode', 'safe') self.__encoding_aes_key = kwargs.get('encoding_aes_key') self.__crypto = None self._update_crypto() self.__access_token_getfunc = kwargs.get('access_token_getfunc') self.__access_token_setfunc = kwargs.get('access_token_setfunc') self.__access_token_refreshfunc = kwargs.get('access_token_refreshfunc') self.__access_token = kwargs.get('access_token') self.__access_token_expires_at = kwargs.get('access_token_expires_at') self.__jsapi_ticket_getfunc = kwargs.get('jsapi_ticket_getfunc') self.__jsapi_ticket_setfunc = kwargs.get('jsapi_ticket_setfunc') self.__jsapi_ticket_refreshfunc = kwargs.get('jsapi_ticket_refreshfunc') self.__jsapi_ticket = kwargs.get('jsapi_ticket') self.__jsapi_ticket_expires_at = kwargs.get('jsapi_ticket_expires_at') self.__partnerid = kwargs.get('partnerid') self.__partnerkey = kwargs.get('partnerkey') self.__paysignkey = kwargs.get('paysignkey') @property def token(self): """ 獲取當前 Token """ self._check_token() return self.__token @token.setter def token(self, token): """ 設置當前 Token """ self.__token = token self._update_crypto() # 改動 Token 需要重新更新 Crypto @property def appid(self): """ 獲取當前 App ID """ return self.__appid @property def appsecret(self): """ 獲取當前 App Secret """ return self.__appsecret def set_appid_appsecret(self, appid, appsecret): """ 設置當前 App ID 及 App Secret""" self.__appid = appid self.__appsecret = appsecret self._update_crypto() # 改動 App ID 后需要重新更新 Crypto @property def encoding_aes_key(self): """ 獲取當前 EncodingAESKey """ return self.__encoding_aes_key @encoding_aes_key.setter def encoding_aes_key(self, encoding_aes_key): """ 設置當前 EncodingAESKey """ self.__encoding_aes_key = encoding_aes_key self._update_crypto() # 改動 EncodingAESKey 需要重新更新 Crypto @property def encrypt_mode(self): return self.__encrypt_mode @encrypt_mode.setter def encrypt_mode(self, encrypt_mode): """ 設置當前加密模式 """ self.__encrypt_mode = encrypt_mode self._update_crypto() @property def crypto(self): """ 獲取當前 Crypto 實例 """ return self.__crypto @property def access_token(self): """ 獲取當前 access token 值, 本方法會自行維護 access token 有效性 """ self._check_appid_appsecret() if callable(self.__access_token_getfunc): self.__access_token, self.__access_token_expires_at = self.__access_token_getfunc() if self.__access_token: now = time.time() if self.__access_token_expires_at - now > 60: return self.__access_token self.grant_access_token() # 從騰訊服務器獲取 access token 並更新 return self.__access_token @property def jsapi_ticket(self): """ 獲取當前 jsapi ticket 值, 本方法會自行維護 jsapi ticket 有效性 """ self._check_appid_appsecret() if callable(self.__jsapi_ticket_getfunc): self.__jsapi_ticket, self.__jsapi_ticket_expires_at = self.__jsapi_ticket_getfunc() if self.__jsapi_ticket: now = time.time() if self.__jsapi_ticket_expires_at - now > 60: return self.__jsapi_ticket self.grant_jsapi_ticket() # 從騰訊服務器獲取 jsapi ticket 並更新 return self.__jsapi_ticket @property def partnerid(self): """ 獲取當前財付通商戶身份標識 """ return self.__partnerid @property def partnerkey(self): """ 獲取當前財付通商戶權限密鑰 Key """ return self.__partnerkey @property def paysignkey(self): """ 獲取商戶簽名密鑰 Key """ return self.__paysignkey def grant_access_token(self): """ 獲取 access token 並更新當前配置 :return: 返回的 JSON 數據包 (傳入 access_token_refreshfunc 參數后返回 None) """ self._check_appid_appsecret() if callable(self.__access_token_refreshfunc): self.__access_token, self.__access_token_expires_at = self.__access_token_refreshfunc() return response_json = self.__request.get( url="https://api.weixin.qq.com/cgi-bin/token", params={ "grant_type": "client_credential", "appid": self.__appid, "secret": self.__appsecret, }, access_token=self.__access_token ) self.__access_token = response_json['access_token'] self.__access_token_expires_at = int(time.time()) + response_json['expires_in'] if callable(self.__access_token_setfunc): self.__access_token_setfunc(self.__access_token, self.__access_token_expires_at) return response_json def grant_jsapi_ticket(self): """ 獲取 jsapi ticket 並更新當前配置 :return: 返回的 JSON 數據包 (傳入 jsapi_ticket_refreshfunc 參數后返回 None) """ self._check_appid_appsecret() if callable(self.__jsapi_ticket_refreshfunc): self.__jsapi_ticket, self.__jsapi_ticket_expires_at = self.__jsapi_ticket_refreshfunc() return response_json = self.__request.get( url="https://api.weixin.qq.com/cgi-bin/ticket/getticket", params={ "type": "jsapi", }, access_token=self.access_token, ) self.__jsapi_ticket = response_json['ticket'] self.__jsapi_ticket_expires_at = int(time.time()) + response_json['expires_in'] if callable(self.__jsapi_ticket_setfunc): self.__jsapi_ticket_setfunc(self.__jsapi_ticket, self.__jsapi_ticket_expires_at) return response_json def get_access_token(self): """ 獲取 Access Token 及 Access Token 過期日期, 僅供緩存使用, 如果希望得到原生的 Access Token 請求數據請使用 :func:`grant_token` **僅為兼容 v0.6.0 以前版本使用, 自行維護 access_token 請使用 access_token_setfunc 和 access_token_getfunc 進行操作** :return: dict 對象, key 包括 `access_token` 及 `access_token_expires_at` """ self._check_appid_appsecret() return { 'access_token': self.access_token, 'access_token_expires_at': self.__access_token_expires_at, } def get_jsapi_ticket(self): """ 獲取 Jsapi Ticket 及 Jsapi Ticket 過期日期, 僅供緩存使用, 如果希望得到原生的 Jsapi Ticket 請求數據請使用 :func:`grant_jsapi_ticket` **僅為兼容 v0.6.0 以前版本使用, 自行維護 jsapi_ticket 請使用 jsapi_ticket_setfunc 和 jsapi_ticket_getfunc 進行操作** :return: dict 對象, key 包括 `jsapi_ticket` 及 `jsapi_ticket_expires_at` """ self._check_appid_appsecret() return { 'jsapi_ticket': self.jsapi_ticket, 'jsapi_ticket_expires_at': self.__jsapi_ticket_expires_at, } def _check_token(self): """ 檢查 Token 是否存在 :raises NeedParamError: Token 參數沒有在初始化的時候提供 """ if not self.__token: raise NeedParamError('Please provide Token parameter in the construction of class.') def _check_appid_appsecret(self): """ 檢查 AppID 和 AppSecret 是否存在 :raises NeedParamError: AppID 或 AppSecret 參數沒有在初始化的時候完整提供 """ if not self.__appid or not self.__appsecret: raise NeedParamError('Please provide app_id and app_secret parameters in the construction of class.') def _update_crypto(self): """ 根據當前配置內容更新 Crypto 類 """ if self.__encrypt_mode in ['compatible', 'safe'] and self.__encoding_aes_key is not None: if self.__token is None or self.__appid is None: raise NeedParamError('Please provide token and appid parameters in the construction of class.') self.__crypto = BasicCrypto(self.__token, self.__encoding_aes_key, self.__appid) else: self.__crypto = None