封裝、繼承、多態、反射

面向對象(OOP)

#什麼是對象
	面向對象是一種編程思想,是指導程序員如何編寫出更好的程序的思想
    核心是對象,程序就是一系列對象的集合,咱們程序員只須要調度、控制這些對象來交互完成工做
    
#案例1,把對象裝進冰箱
	面向過程:
    	1.打開冰箱
        2.裝入冰箱
		3.關閉冰箱
    面向對象:
    	找個具有裝大象的技能的對象
    
在面向對象中,程序員的角度發生改變,從具體的操做者變成了指揮者
強調:對象不是憑空產生的,須要咱們本身設計
    
#案例2,西天取經
	面向過程:
    	如來找5我的,經歷九九八十一難來取經,在把經書送回去
    面向對象:
    	如來只須要找5個對象,本身負責調度便可,若是某個對象發生變化,也不會影響其餘對象的擴展性
    
#案例3,
	面向過程:
        曹操吟詩
        喝酒吃肉,人生真爽	後改成
        喝酒吃肉,人生幾何	後改成
        對酒當歌,人生幾何
	面向對象:
    	把字拆成一個個對象,用的時候組裝
    	優勢:
        	1.擴展性強
    		2.靈活性強
    		3.重用性強
    	缺點:
        	1.程序的複雜度提升了
    		2.沒法準確預知結果
    
#使用場景
    1.對擴展性要求較高的程序,一般是直接面向用戶的(QQ、微信)
    
#不是全部的程序都要面向對象,要分析具體的需求

面向過程

#面向過程
	面向過程關注的是過程,就是先幹什麼、後幹什麼
    
#優勢
	邏輯清晰,能夠將複雜的問題簡單化、流程化
    
#缺點
	擴展性差,可維護性差,重用性低
    
#使用場景
    1.對擴展性要求較低的程序,好比系統內核、git、計算器

類和對象

#類和對象
	類和對象是面向對象編程的最核心的兩個概念

#類	
	類就是類型、類別,類就是一個抽象的概念
    類是一系列具有相同特徵和相同行爲的對象的集合

對象

#對象
	對象就是具體存在的某個事物,有本身的特徵和行爲
    
#類和對象的關係
	類包含一系列對象
    一個對象屬於某個類
    
#先有類,仍是先有對象
	生活中,是先有對象,而後有類
    程序中,先有類,纔能有對象,咱們必須先告訴計算機這類對象有什麼特徵、有什麼行爲(也就是說要先建立類)
    因此說,在使用面向對象編程時,第一步就是考慮須要使用什麼對象,再考慮使用什麼類,而後,先建立類,再建立對象

建立類和對象

#建立類
#語法
	class 類的名字:
        #註釋
        #類中的內容,描述屬性和技能
        #描述屬性用變量
        #描述行爲用函數
        
#類的名字
	1.見名知意
    2.大駝峯(單詞的首字母大寫)
        
#建立類
	#一個類,使用類名+()能夠建立多個不一樣的對象
class Student:
    pass

#建立對象(類名加括號)
print(Student)		#<class類 '__main__.Student'>
res = Student()
print(res)  		#<__main__.Student object對象 at 0x000001CBB06AFA90>
print(res.__class__.__name__)   #Student
print(res.__dict__)				#{}
        
#類中的屬性能夠被任何一個對象訪問,因此類中存放的應該是對象的公共屬性  
class Person:
    name = '張三丰'
    age = 2000
    sex = 'male'
res = Person()
print(res)
print(res.name)
print(res.age)
print(res.sex)

<__main__.Person object at 0x00000138EAA9A940>
張三丰
2000
male

#能夠爲對象單獨設置屬性	
	#屬性的查找順序爲,先查找對象,再查找類
class Person:
    eat = '會吃飯'
    run = '會跑路'
zhangsan = Person()
zhangsan.eat = '不會吃飯'
lisi = Person()
lisi.run = '不會跑路'
print(zhangsan.eat)
print(zhangsan.run)
print(lisi.eat)
print(lisi.run)

不會吃飯
會跑路
會吃飯
不會跑路

#對象的精髓就在於將數據和處理數據的函數整合到一塊兒,這樣以來,當拿到一個對象,就同時拿處處理的數據和處理數據的函數
#在類和對象中,屬性(數據)用變量表示,方法用函數表示
	#屬性、方法也能夠統一稱爲屬性,分爲數據屬性、函數屬性
    
class Student:
    school = '清華'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def study(self):
        print('hello i am a student, my name is:%s'%self.school)

t1 = Student('syy',18)
print(type(t1.study))
print(type(Student.study))

<class 'method'>		#方法
<class 'function'>		#函數

屬性的增刪改查

#類
#增長類的屬性
	新建一個類(覆蓋)
    
#對象
#增長對象的屬性
對象變量名.屬性名稱 = 屬性值

#刪除對象的屬性
del 對象變量名.屬性名稱

#修改對象的屬性
對象變量名.屬性名稱 = 新的屬性值

#查看對象的屬性(專屬屬性)
	#不管是查看類的屬性仍是查找對象的屬性,都是使用__dict__
    #查看對象的類,使用__class__
class Person:
    eat = '會吃飯'
    run = '會跑路'
print(Person.__dict__)		#類的屬性
res = Person()		
print(res)					#沒有專屬屬性的對象
print(res.__dict__)
print(res.__class__)		#對象的類信息
zhangsan = Person()
zhangsan.eat = '不會吃飯'
print(zhangsan.__dict__)	#有專屬屬性的對象
print(zhangsan.__class__)		#對象的類信息

{'__module__模塊名稱': '__main__', 'eat': '會吃飯', 'run': '會跑路', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__註釋': None}
<__main__.Person object at 0x000001C11D14A9E8>
{}
<class '__main__.Person'>
{'eat': '不會吃飯'}
<class '__main__.Person'>

初始化對象的屬性

#使用類產生對象1
class Teacher:
    school = '清華'

t1 = Teacher()
t1.name = 'tom'
t1.age = 19

t2 = Teacher()
t2.name = 'jerry'
t2.age = 20

t3 = Teacher()
t3.name = 'syy'
t3.age = 21

print(t1.name)
print(t2.name)
print(t3.name)

#使用類產生對象2,使用函數封裝重複代碼
class Teacher:
    school = '清華'

def init(obj,name,age):
    obj.name = name
    obj.age = age

t1 = Teacher()
t2 = Teacher()
t3 = Teacher()

init(t1,'tom',19)
init(t2,'jerry',19)
init(t3,'syy',19)

print(t1.name)
print(t2.name)
print(t3.name)
#使用類產生對象3,使用__init__方法
	#self就是生成的對應的對象
    #使用__init__定義的變量,就是對象的專屬屬性(__init__的做用就是給對象附初始值)
    #函數名必須是__init__,__init__函數不能使用return返回值
    #調用函數的時候,注意變量的個數要對應類體內的變量的個數(少一個),緣由在於,調用函數的時候會將對象自己做爲第一個參數
    #當需求是在建立對象時還須要作掉別的事,那就應該想到對象的初始化

class Teacher:
    school = '清華'
    def __init__(self):
        print(self)
t1 = Teacher()
print(t1)

<__main__.Teacher object at 0x000002C04478F908>
<__main__.Teacher object at 0x000002C04478F908>

class Teacher:
    school = '清華'
    def __init__(self,name,age):
        self.name = name
        self.age = age
t1 = Teacher('tom',19)
t2 = Teacher('jerry',20)
t3 = Teacher('syy',21)
print(t1,t2,t3)
print(t1.name,t2.name,t3.name)

<__main__.Teacher object at 0x0000022248F15A90> <__main__.Teacher object at 0x0000022248F15BA8> <__main__.Teacher object at 0x0000022248F159B0>
tom jerry syy
#使用locals()初始化對象
	#locals(),是一個字典,表示當前名稱空間中全部的變量名

class Hero:
    def __init__(self,name,level,blood,attach,q_hurt,w_hurt,e_hurt):
        # self.name = name
        # self.level = level
        # self.blood = blood
        # self.attach = attach
        # self.q_hurt = q_hurt
        # self.w_hurt = w_hurt
        # self.e_hurt = e_hurt
        lcs = locals()
        lcs.pop('self')
        self.__dict__.update(lcs)
s1 = Hero(1,2,3,4,5,6,7)
print(s1.__dict__)

{'e_hurt': 7, 'w_hurt': 6, 'q_hurt': 5, 'attach': 4, 'blood': 3, 'level': 2, 'name': 1}

對象的綁定方法

#對象綁定方法
	#不使用裝飾器的前提下,類中的方法(函數),都是對象綁定方法
	#使用類生成對象,經過對象能夠調用別的方法(函數體代碼)
    
    
#一個類裏面能夠有多個函數,可是一個類同時只能生成一個對象
	#只要一輩子成對象,類中的代碼就會運行,可是不會運行除了__init__別的函數體代碼
    #別的函數體代碼只會在調用的時候運行
    
class Student:
    school = '清華'
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print(self)
    def study(self):
        print('hello i am a student, my name is:%s'%self.name)

t1 = Student('syy',18)
t2 = Student('tom',19)
print(t1.study())		#不須要傳參,自動將該對象當作第一個參數傳入
print(t2.study())

<__main__.Student object at 0x000002842EECAA20>
<__main__.Student object at 0x000002842EEDE978>
hello i am a student, my name is:syy
None
hello i am a student, my name is:tom
None

#使用對象調用方法和使用類調用方法的行爲不一樣
	#使用對象調用方法,有幾個參數就傳幾個參數
    #使用類名調用方法,該方法就是一個普通函數
class Student:
    school = '清華'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def study(self):
        print('hello i am a student, my name is:%s'%self.name)

t1 = Student('syy',18)
t1.study()						#使用對象調用方法
Student.study(t1)				#使用類名調用方法

hello i am a student, my name is:syy
hello i am a student, my name is:syy
    
#練習
	#寫一個學生類,具有一個打招呼的技能,要能輸出本身的名字信息
class Student:
    def __init__(self,name):
        self.name = name
    def say_hi(self):
        print('my name is %s'%self.name)
s1 = Student('syy')
s1.say_hi()

my name is syy

類的綁定方法

#
class Student:
    school = '清華'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def study(self):
        print(self.school)

t1 = Student('syy',18)
print(Student.school)		#訪問類
print(t1.school)			#訪問對象	
Student.study(t1)			#訪問類中的函數(須要傳入一個對象)
t1.study()

#類的綁定方法
	#使用裝飾器@classmethod裝飾
    #無論是用類仍是對象調用,都會自動傳入類自己,做爲第一個參數
class Student:
    school = '清華'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @classmethod
    def study(cls):
        print(cls.school)

t1 = Student('syy',18)
print(Student.school)
print(t1.school)
Student.study()			#訪問類中的函數(不須要傳參數)
t1.study()

#何時綁定給對象
	#當函數邏輯須要訪問對象中的數據時
#何時綁定給類
	#當函數邏輯須要訪問類中的數據時

非綁定方法

#非綁定方法或叫作靜態方法
	#使用裝飾器@staticmethod
	#即類既不綁定對象self,也不綁定類cls
    #該方法定義函數與類體外定義函數結果相同
    
class Student:
    school = '清華'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @staticmethod
    def study():
        print('hello')

t1 = Student('syy',18)
Student.study()			#使用類訪問方法
t1.study()				#使用對象訪問方法

#實例
	#爲學生類添加一個save方法,一個get方法
    #save是將對象存儲到文件中(對象綁定方法)
    #get是從你文件中獲取對象(非綁定方法)
    
import pickle
class Student:
    def __init__(self,name):
        self.name = name
    def save(self):
        with open(self.name,'wb') as f:
            pickle.dump(self,f)
    @staticmethod
    def get(name):
        with open(name,'rb') as f:
            obj = pickle.load(f)
            return obj
s1 = Student('syy')
s1.save()
obj = Student.get('syy')
print(obj.name) 

#當類中的函數須要使用self或者__init__中定義的變量,那麼這個函數就是對象綁定
#當類中的函數須要使用類中定義的變量,那麼這個函數就是類綁定方法,可使用裝飾器classmethod修飾
#當類中的函數既不須要使用類中定義的變量,又不須要使用__init__中定義的變量,那麼這個對象就是非綁定方法

實例 (對象交互練習)

參考網站html

import random
import time

class Hero:
    def __init__(self,name,level,blood,att,Q_hurt,W_hurt,E_hurt):
        lcs = locals()
        lcs.pop('self')
        self.__dict__.update(lcs)
    def attack(self,enemy):
        enemy.blood -= self.att
        print('%s對%s釋放了普通攻擊,形成了%s的傷害,敵人剩餘血量%s'%(self.name,enemy.name,self.att,enemy.blood))
        if enemy.blood <= 0:
            print('%s被%s使用普通攻擊擊殺!'%(enemy.name,self.name))
    def Q(self,enemy):
        enemy.blood -= self.Q_hurt
        print('%s對%s釋放了Q技能,形成了%s的傷害,敵人剩餘血量%s'%(self.name,enemy.name,self.Q_hurt,enemy.blood))
        if enemy.blood <= 0:
            print('%s被%s使用Q技能擊殺!'%(enemy.name,self.name))
    def W(self,enemy):
        enemy.blood -= self.W_hurt
        print('%s對%s釋放了W技能,形成了%s的傷害,敵人剩餘血量%s'%(self.name,enemy.name,self.W_hurt,enemy.blood))
        if enemy.blood <= 0:
            print("%s被%s使用W技能擊殺!" % (enemy.name, self.name))
    def E(self,enemy):
        enemy.blood -= self.E_hurt
        print('%s對%s釋放了E技能,形成了%s的傷害,敵人剩餘血量%s'%(self.name,enemy.name,self.E_hurt,enemy.blood))
        if enemy.blood <= 0:
            print('%s被%s使用E技能擊殺!'%(enemy.name,self.name))

#定義英雄
h1 = Hero('亞索',level=1,blood=50000,att=500,Q_hurt=600,W_hurt=1000,E_hurt=2000)
h2 = Hero('妲己',level=2,blood=3000,att=600,Q_hurt=700,W_hurt=1000,E_hurt=2000)
h3 = Hero('魯班',level=3,blood=3000,att=700,Q_hurt=800,W_hurt=1500,E_hurt=2000)
h4 = Hero('蔡文姬',level=4,blood=5000,att=100,Q_hurt=200,W_hurt=300,E_hurt=400)
# h1.attack(h2)
# h2.Q(h1)
# h2.W(h1)
# h2.E(h1)

while True:
    #把全部的攻擊方法裝到字典,爲了隨機取出一個
    funcs = {1:Hero.Q,2:Hero.W,3:Hero.E,4:Hero.attack}
    func_index = random.randint(1,len(funcs))
    func = funcs[func_index]
    #把全部英雄裝到字典,爲了隨機取出一個
    heros = {1:h1,2:h2,3:h3,4:h4}
    hero_index = random.randint(1,len(heros))
    hero = heros[hero_index]
    #剩餘的英雄們
    other_heros = {}
    new_index = 1
    for k,v in heros.items():
        if k != hero_index:
            other_heros[new_index] = v
            new_index +=1
    #從剩餘的英雄中隨機跳出一個英雄來捱打
    enemy_index = random.randint(1,len(other_heros))
    enemy = other_heros[enemy_index]
    #打他
    func(hero,enemy)
    if enemy.blood <=0:
        break
    time.sleep(1)

面向對象(OOP)的三大特徵

繼承

#什麼是繼承
	繼承是一種關係,描述兩個類之間的關係
    在程序中,繼承描述的是類和類之間的關係(父類、基類、子類)
    在python中,一個子類能夠同時繼承多個父類,使用逗號分開

#爲何要使用繼承
	繼承的一方能夠直接使用被繼承一方已經有的東西
    繼承的做用就是重用已經有的代碼,提升重用性
    
#如何使用繼承
#語法
class 類名稱(父類的名稱):
    pass

#例
class Base:
    desc = '這是一個基類'
    def show_info(self):
        print(self.desc)
    def make_money(self):
        print('一天賺它一個億')
class Subclass(Base):
    pass
    # def make_money(self):
    #     print('一天賺它一百塊')
obj = Subclass()
obj.make_money()
print(obj.desc)

一天賺它一個億
這是一個基類

抽象與繼承

#原始代碼
class Teacher:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
    def say_hi(self):
        print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
class Student:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
    def say_hi(self):
        print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))

t1 = Teacher('syy','male',20)
t1.say_hi()
stu1 = Student('dog','female',2)
stu1.say_hi()

#抽象
	#生活中,抽象是指不具體、不清晰、很模糊、看不懂,形容詞
    #程序中,抽象是動詞
    #將多個不一樣的子類中相同的部分進行抽取,造成一個新的類,這個過程叫抽象
    #先抽象再繼承
    #繼承一個已經存在的類,能夠擴展或是修改原始的功能
    
class Person:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
    def say_hi(self):
        print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
class Teacher(Person):
    def teacher(self):
        print('老師專屬...')
class Student(Person):
    pass

t1 = Teacher('syy','male',20)
t1.say_hi()
stu1 = Student('dog','female',2)
stu1.say_hi()

屬性查找順序

#屬性查找順序
	對象 > 子類 > 父類
    
class A:
    a = 1
class B(A):
    b = 2
    def __init__(self,c):
        self.c = c
class C(B):
    pass
class D(C):
    pass
t = B(3)
print(t.b)		#2
print(t.a)		#1
print(t.c)		#3
print(D.mro())	#全部父類名,按繼承順序排列,[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

派生與覆蓋

#派生
	當一個子類中出現了與父類中不一樣的內容時,這個子類就稱之爲派生類
    一般子類都會寫一些新的代碼,不可能和父類徹底同樣,因此,子類一般都是派生類
    
#覆蓋
    覆蓋也稱之爲overwrite
    當子類出現了與父類徹底一致(名字一致)的屬性和方法時,父類中的屬性和方法被子類覆蓋
    
#派生與覆蓋
class Person:
    def say_hi(self):
        print('hello person')
class Student(Person):
    def say_hi(self):
        print('hello student')
stu = Student()
print(stu.say_hi())

#例
	實現一個能夠限制元素類型的列表
	#子類中訪問父類的內容使用關鍵字super
class Mylist(list):
    def __init__(self,element_type):
        #先繼承父類,後自定義子類的初始化
        super().__init__()
        #element_type: 指定的數據類型
        self.element_type = element_type
    def append(self,object):
        #object: 要存儲的元素
        if type(object) == self.element_type:
            #在這裏訪問父類的append函數
            super(Mylist,self).append(object)
        else:
            print('sorry,you element type not is %s'%self.element_type)
m = Mylist(str)
m.append('壹')
print(m[0])

super

參考網站python

#python2中的supper
class Parent:
    test = 'abc'
    def say_something(self):
        print('anything')
class Sub(Parent):
    def show_info(self):
        print(super(Sub,self).test)
        super(Sub, self).say_something()
sub = Sub()
sub.show_info()

#python3中的supper
class Parent:
    test = 'abc'
    def say_something(self):
        print('anything')
class Sub(Parent):
    def show_info(self):
        print(super().test)
        super().say_something()
sub = Sub()
sub.show_info()

#使用類名稱,與繼承無關
class Parent:
    test = 'abc'
    def say_something(self):
        print('anything')
class Sub(Parent):
    def show_info(self):
        print(Parent.test)
        Parent.say_something(self)
sub = Sub()
sub.show_info()

#參考
class Animal:  				# 定義一個父類
    def __init__(self):  	# 父類的初始化
        self.name = 'animal'
        self.role = 'parent'
        print('I am father')
 
class Dog(Animal):				# 定一個繼承Animal的子類
    def __init__(self):  		# 子類的初始化函數,此時會覆蓋父類Animal類的初始化函數
        super(Dog, self).__init__()  # 在子類進行初始化時,也想繼承父類的__init__()就經過super()實現,此時會定義self.name= 'animal'等
        print('I am son')
        self.name = 'dog'	  # 定義子類的name屬性,而且會把剛纔的self.name= 'animal'更新爲'dog'
animal = Animal()			#I am father
xbai = Dog()				#I am son(沒有繼承父類的print?)
print(xbai.name)			#'dog'

#使用super繼承
	#子類在初始化的時候,調用父類的初始化
class Teacher:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
    def say_hi(self):
        print('name: %s,age: %s,gender: %s'%(self.name,self.age,self.gender))
class Student(Teacher):
    def __init__(self,name,age,gender,number):
        super(Student, self).__init__(name,age,gender)
        self.number = number
    def say_hi(self):
        super().say_hi()
        print('number: %s'%self.number)
stu = Student('syy',18,'male',2333333)
stu.say_hi()

name: syy,age: 18,gender: male
number: 2333333
    
#繼承方法
class Teacher:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
        self.aa()
    def say_hi(self):
        print('name: %s,age: %s,gender: %s'%(self.name,self.age,self.gender))
    def aa(self):
        print('my name si aa')
class Student(Teacher):
    def __init__(self,name,age,gender,number):
        super().__init__(name,age,gender)
        self.number = number
    def say_hi(self):
        super().say_hi()
        print('number: %s'%self.number)
stu = Student('syy',18,'male',2333333)
stu.say_hi()

#當繼承一個現有的類,而且覆蓋了父類的__init__方法,那麼,必須在初始化方法的第一行調用父類的初始化方法,並傳入父類所需的參數,再根據狀況自定義屬性

組合

#組合
	組合描述的是對象與對象之間的關係
    將一個對象做爲另外一個對象的屬性
    組合也能夠理解成繼承的一種
    組合對比繼承,類的耦合度更低
    
#組合的做用
	重用代碼,減小代碼冗餘
    
#何時使用繼承
	兩個類之間存在父子關係,同類
    
#何時使用組合
	兩個類之間毫無關係,異類
    
#組合的代碼實現
	#人不是收集,可是人可使用收集的功能
class Phone:
    def __init__(self,price,kind,color):
        self.price = price
        self.kind = kind
        self.color = color
    def call(self):
        print('呼叫...')
    def send_message(self):
        print('發短信...')
class Student:
    def __init__(self,name,age,phone):
        self.name = name
        self.age = age
        self.phone = phone
    def show_info(self):
        print('name: %s,age: %s'%(self.name,self.age))
phone1 = Phone(10000,'apple','red')
stu1 = Student('syy',18,phone1)
stu1.phone.call()

菱形繼承

#python支持多繼承
	存在類的繼承順序問題

#通常的繼承順序
	#按Sub的書寫順序繼承
class A:
    pass
class B:
    pass
class C:
    pass
class Sub(A,B,C):
    pass
print(Sub.mro())

[<class '__main__.Sub'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]

#python3中,任何類都是直接或間接的繼承Object
class A:
    pass
print(A.mro())		#[<class '__main__.A'>, <class 'object'>]

#新式類與經典類
	#新式類
    	任何顯示或隱式的繼承自Object的類,就叫作新式類
        python3中,所有都是新式類
    #隱式類
    	沒有繼承Object的類,就叫作經典類,python2中才會存在經典類
        因此類的建立通常標識繼承自object,這樣代碼既能夠兼容python2有能夠兼容python3
        
#菱形繼承
	當一個類有多個父類,且多個父類有共同的基類,那麼這個類的繼承就叫作菱形繼承
    
class A:
    j = 1
    pass
class B(A):
    # j = 2
    pass
class C(A):
    j = 3
    pass
class D(B,C):
    # j =4
    pass
test = D.j
print(test)		#3
    
#菱形繼承中新式類與經典類的區別
	#新式類
    	有共同父類就廣度(廣度沒有再深度),沒有共同父類就深度找
    #經典類
    	有共同父類是深度(深度沒有再廣度),沒有共同父類是且僅是深度找
class A:
    num = 1
    pass
class B():
    num = 2
    pass
class C(A):
    num = 3
    pass
class D(A):
    num = 4
    pass
class E(B):
    # num = 5
    pass
class F(C):
    num = 6
    pass
class G(D):
    num = 7
    pass
class H(E,F,G):
    # num = 8
    pass
print(H.num)	#2
print(H.mro())	#[<class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.C'>, <class '__main__.G'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]

繼承的使用

#原始數據
	程序員,姓名、性別、年齡、工資、編程技能
    項目經理,姓名、性別、年齡、工資、編程技能、獎金、管理技能
    
#分析關係
	初始化變量較多的時候,可使用locals
	項目經理來自程序員,可使用super繼承,沒有必要使用抽象
    不管是程序員仍是項目經理,都要使用存和取,單獨抽取代碼更加簡潔,有利於代碼擴展

import pickle
import os
class Baseclass:
    '''
    將存儲數據的操做單獨抽取,這樣能夠下降耦合度,減小代碼冗餘
    '''
    def save(self):
        #判斷類名對應的文件夾是否存在
        cls_name = self.__class__.__name__
        if not os.path.exists(cls_name):
            os.makedirs(cls_name)
        path = os.path.join(cls_name,self.name)
        with open(path,'wb') as f:
            pickle.dump(self,f)
    @classmethod
    def get_obj(cls,name):
        #拼接路徑
        path = os.path.join(cls.__name__,name)
        if os.path.exists(path):
            with open(path,'rb') as f:
                return pickle.load(f)

class Coder(Baseclass):
    def __init__(self,name,age,gender,salary):
        self.name = name
        self.age = age
        self.gender = gender
        self.salary = salary
    def programming(self):
        print('程序員正在開發項目...')
class Manager(Coder):
    def __init__(self,name,age,gender,salary,bonus):
        super().__init__(name,age,gender,salary)
        self.bonus = bonus
    def manage(self):
        print('經理正在毆打程序員...')
if __name__ == '__main__':
    # cod1 = Coder('syy',18,'男神',10000)
    # cod1.programming()
    # cod1.save()
    # man1 = Manager('ji',88,'糟老頭子',100,1)
    # man1.programming()
    # man1.save()

    cod1 = Coder.get_obj('syy')  #type: Coder
    cod1.programming()
    man1 = Manager.get_obj('ji')    #type: Manager
    man1.manage()
    
程序員正在開發項目...
經理正在毆打程序員...

封裝

#什麼是封裝
	封裝就是將複雜的細節隱藏到內部,對外提供簡單的使用接口
    
#爲何要使用封裝
	爲了保證關鍵數據的安全性
    對外隱藏細節,隔離複雜度
    
#何時使用封裝
	當有一些數據不但願外界直接修改
    當有一些數據不但願外界使用時
    
#怎麼使用封裝
class Person:
    def __init__(self,id_number,name,age):
        self.id_number = id_number
        self.name =name
        self.age = age
    def show_id(self):
        print(self.id_number)
p = Person('111','syy',18)
p.id_number = '222'
p.show_id()				#222
print(p.id_number)		#222

class Person:
    def __init__(self,id_number,name,age):
        self.__id_number = id_number
        self.name =name
        self.age = age
    def show_id(self):
        print(self.__id_number)
p = Person('111','syy',18)
p.__id_number = '222'
p.show_id()				#111
print(p.__id_number)	#222

class Person:
    def __init__(self,id_number,name,age):
        self.__id_number = id_number
        self.name =name
        self.age = age
    def show_id(self):
        print(self.__id_number)
p = Person('111','syy',18)
p.id_number = '222'
p.show_id()				#111,此時外界沒法修改類體(可使用_Person__id_number修改)
print(p.id_number)		#222


#例
class PC:
    def __init__(self,price,kind,color):
        self.price = price
        self.kind = kind
        self.color = color
    def open(self):
        print('接通電源')
        self.__check_device()
        print('載入內核')
        print('初始化內核')
        self.__start_service()
        print('啓動GUI')
        self.__login()
    def __check_device(self):
        print('硬件檢測1.')
        print('硬件檢測2..')
        print('硬件檢測3...')
    def __start_service(self):
        print('啓動服務1.')
        print('啓動服務2..')
        print('啓動服務3...')
    def __login(self):
        print('登陸1.')
        print('登陸2..')
        print('登陸3...')
p = PC(10000,'蘋果','白色')
p.open()
# p.login(),外界沒法修改類體,沒法使用類體內部的數據

#被封裝的函數的特色
	1.外界不能直接訪問
    2.內部依然可使用函數名調用
    
#學習了封裝以後就能夠控制屬性的權限
	1.公開權限,默認全部屬性和方法都是公開的
    2.私有權限,當前類才能使用的屬性和方法
    
#如何訪問被封裝的屬性或方法
    #屬性雖然被封裝了,可是仍是須要訪問或修改的
    #經過定義函數,完成對私有屬性的訪問或修改
    	#這樣以來,能夠在外界訪問或修改私有屬性或方法時,添加額外的邏輯
    
class Downloader:
    def __init__(self,filename,url,buffer_size):
        self.filename = filename
        self.url = url
        self.__buffer_size = buffer_size
    def start_download(self):
        if self.__buffer_size <= 1024*1024:
            print('當前緩衝大小爲: %s'%self.__buffer_size)
            print('開始下載...')
        else:
            print('內存炸了!!!')
    #定義接口函數
    def set_buffer_size(self,size):
        #能夠在接口函數中添加額外的邏輯
        if type(size) == int:
            print('緩衝大小修改爲功,大小爲: %s'%size)
            self.__buffer_size = size
        else:
            print('緩衝大小必須是整型!!!')
    # 經過函數訪問內部封裝的屬性
    def get__buffer_size(self):
        return self.__buffer_size
#生成對象
d = Downloader('奧特曼全集','www.aoteman.com',1024*1024)
#下載
d.start_download()
#修改函數內部封裝的屬性
d.set_buffer_size(1024*512)
#繼續下載
d.start_download()

property、key.setter、key.deleter裝飾器

#裝飾器是用在函數上的
#property裝飾器
	該裝飾器爲了解決封裝的屬性和方法的'訪問'方法的不一樣
    使用了該裝飾器,類中隱藏方法的'訪問',直接使用點,再也不使用括號
    
#key.setter裝飾器
	該裝飾器爲了解決封裝先後的屬性'修改’的不一樣
    使用了該裝飾器,類中隱藏屬性的修改,直接使用點,再也不使用括號
    
#key.deleter裝飾器
	該裝飾器爲了解決不能刪除封裝的屬性的問題,相似於觸發器
    使用了該裝飾器,類中隱藏屬性的刪除,能夠直接刪除,也能夠設置觸發條件
    
#原始代碼
class A:
    def __init__(self,name,key):
        self.name = name
        self.__key = key
    def get_key(self):
        return self.__key
    def set_key(self,new_key):
        self.__key = new_key
a = A('syy',123)
print(a.name)			#訪問屬性不加括號
print(a.get_key())		#訪問方法加括號

#使用了property後的代碼
class A:
    def __init__(self,name,key):
        self.name = name
        self.__key = key
    @property
    def get_key(self):
        return self.__key
    def set_key(self,new_key):
        self.__key = new_key
a = A('syy',123)
print(a.name)			#訪問屬性不加括號
print(a.get_key)		#訪問方法再也不須要加括號,隱藏屬性與普通屬性訪問方式相同
a.set_key(456)			#屬性的修改須要加括號
print(a.get_key)

#使用property、key.setter、key.deleter後的代碼
class A:
    def __init__(self,name,key):
        self.name = name
        self.__key = key
    @property
    def key(self):
        return self.__key
    @key.setter
    def key(self,new_key):
        self.__key = new_key
    @key.deleter
    def key(self):
        print('不容許刪除該屬性...')
        #del self.__key		#能夠在這裏刪除隱藏屬性
a = A('syy',123)
print(a.key)
a.key = 456			#屬性的修改不須要再使用括號,直接點,隱藏屬性與普通屬性修改方式相同
print(a.key)
del a.key			#隱藏屬性不能直接刪除,可使用裝飾器刪除,隱藏屬性與普通屬性刪除方式相同
print(a.key)

123
456
不容許刪除該屬性...
456

#注意
	key是property裝飾器裝飾的方法的名字,在使用setter和deleter時,裝飾器的函數名要保持一致

封裝的原理

#封裝的原理
	#python中,類中的屬性的封裝很是簡單,僅僅是把類體中的屬性的'__變量名'改成'_類名__變量名',這樣外界將不能直接使用變量名或者__變量名訪問隱藏屬性(可是外界可使用_類名__變量名訪問類的隱藏屬性)
    #字符串的修改,對於python解釋器的是很簡單的
    #這種字符串的修改只會在加載類的時候,在類體中運行(屬性隱藏和訪問的時候,'添加都會運行')

class A:
    def __init__(self,key):
        self.__key = key
    @property
    def key(self):
        return self.__key
    @key.deleter
    def key(self):
        del self.__key
a = A(123)
print(a.key)
del a.key
print(a.key)	#AttributeError: 'A' object has no attribute '_A__key'

#
class A:
    def __init__(self,key):
        self.__key = key
    @property
    def key(self):
        return self.__key
    @key.deleter
    def key(self):
        del self.__key
a = A(123)
print(a.key)			#123
print(a.__dict__)		#{'_A__key': 123}
print(a._A__key)		#123

計算屬性

#使用property裝飾器,能夠用來實現計算屬性
	#計算屬性指的是:屬性的值不能直接獲取,必須經過計算才能獲取
    #這裏的property裝飾器,僅僅只是爲了讓方法的調用不加括號,與屬性的調用相同而已
    
#例
	求正方形的面積
class Square:
    def __init__(self,width):
        self.width = width
    @property
    def area(self):
        return self.width * self.width
s = Square(10)
print(s.area)		#100
s.width = 20
print(s.area) 		#400

#例2
	求BMI,BMI = 體重/身高的平方
class Bmi:
    def __init__(self,height,weight):
        self.height = height
        self.weight = weight
    @property
    def bmi(self):
        return self.weight/self.height/self.height
b = Bmi(170,165)
print(b.bmi)		#0.005709342560553633

接口

#什麼是接口
	接口是一組功能的集合,可是接口中只包含功能的名字,不包含具體的實現代碼
    接口本質上是一套協議標準,遵循這個標準的對象就能被調用
    
#接口的做用
	爲了提升代碼的擴展性
    
#例
	例如電腦上的USB接口協議,只要你遵循該協議,相關的設備就能被電腦使用,不須要關心設備的種類
    #PC的代碼一旦完成,後期不管什麼樣的設備,只要遵循了USB接口協議,都能被電腦調用
    #接口主要是方便了對象的使用者,下降了使用者的學習難度,由於使用者只要學習了一套使用方法,就能夠操做各種USB設備
    
class USB:
    def open(self):
        pass
    def close(self):
        pass
    def read(self):
        pass
    def write(self):
        pass
class Mouse(USB):
    def open(self):
        print('鼠標開機')
    def close(self):
        print('鼠標關機')
    def read(self):
        print('獲取光標位置')
    def write(self):
        print('鼠標不支持寫入')
class Keyboard(USB):
    def open(self):
        print('鍵盤開機')
    def close(self):
        print('鍵盤關機')
    def read(self):
        print('獲取鍵盤字符')
    def write(self):
        print('鍵盤寫入燈光顏色')
def pc(usb_device):
    usb_device.open()
    usb_device.read()
    usb_device.write()
    usb_device.close()
m = Mouse()
k = Keyboard()
pc(m)
pc(k)
    
鼠標開機
獲取光標位置
鼠標不支持寫入
鼠標關機
鍵盤開機
獲取鍵盤字符
鍵盤寫入燈光顏色
鍵盤關機
    
#問題
	若是子類沒有按照你的協議來進行代碼的編寫,將致使代碼沒法運行

抽象類

#abc模塊,是單詞absract class的縮寫,意爲'抽象類' 
#抽象類
	一個類中沒有包含函數體(或者通過裝飾器裝飾的函數體),那麼這個類就是抽象類
    #做用
    	能夠限制子類必須遵循父類中定義的抽象方法

import abc
class USB(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def open(self):
        pass
    @abc.abstractmethod
    def close(self):
        pass
    @abc.abstractmethod
    def read(self):
        pass
    @abc.abstractmethod
    def write(self):
        pass

class Mouse(USB):
    def open(self):
        print('鼠標開機')

    def close(self):
        print('鼠標關機')

    def read(self):
        print('獲取光標位置')

    def write(self):
        print('鼠標不支持寫入')

class Keyboard(USB):
    def open(self):
        print('鍵盤開機')

    def close(self):
        print('鍵盤關機')

    def read(self):
        print('獲取鍵盤字符')

    def write(self):
        print('鍵盤寫入燈光顏色')

def pc(usb_device):
    usb_device.open()
    usb_device.read()
    usb_device.write()
    usb_device.close()

m = Mouse()
k = Keyboard()
pc(m)
pc(k)

鴨子類型

#python通常不會限制你必須怎麼寫,做爲一個優秀的程序員,就應該遵照相關協議,因此有了鴨子類型的說法
#鴨子類型
	若是一個對象長得像鴨子,走路像鴨子,那他就是鴨子
    #由此類推,只要保證你的類,按照相關的協議編寫,就能夠達到提升擴展性的目的
    
class Mouse:
    def open(self):
        print('鼠標開機')
    def close(self):
        print('鼠標關機')
    def read(self):
        print('獲取光標位置')
    def write(self):
        print('鼠標不支持寫入')
class Keyboard:
    def open(self):
        print('鍵盤開機')
    def close(self):
        print('鍵盤關機')
    def read(self):
        print('獲取鍵盤字符')
    def write(self):
        print('鍵盤寫入燈光顏色')
def pc(usb_device):
    usb_device.open()
    usb_device.read()
    usb_device.write()
    usb_device.close()

m = Mouse()
k = Keyboard()
pc(m)
pc(k)

多態性

#多態
	生活中,一種事物有多種不一樣的形態
	官網:多個對象能夠響應同一個方法,產生不一樣的結果
    多態不是一種特殊的語法,而是一種狀態、特性
    接口、抽象類、鴨子類型,均可以寫出具有多態的代碼,其中最簡單的就是鴨子類型
    #優勢
    	對於使用者而言,大大的下降了使用難度,例如USB接口程序就有多態性

#例1
class Ji:
    def bark(self):
        print('咯咯咯...')
    def spawn(self):
        print('下雞蛋')
class Ya:
    def bark(self):
        print('嘎嘎嘎...')
    def spawn(self):
        print('下鴨蛋')
class E:
    def bark(self):
        print('eee...')
    def spawn(self):
        print('下鵝蛋')
j = Ji()
y = Ya()
e = E()
	#多個對象使用相同的方法,能夠獲得不一樣的結果
def manage(obj):
    obj.spawn()
manage(j)
manage(y)
manage(e)
    
下雞蛋
下鴨蛋
下鵝蛋
    
#例2
a = 10
b = '10'
c = [10]
print(type(a))		#多個變量名使用相同的方法,獲得不一樣的結果
print(type(b))
print(type(c))

OOP相關的內置函數

#該內置函數不管在不在類中,都能執行
#代碼1
def add_num(a,b):
    if type(a) == int and type(b) == int:
        return a+b
    return None
print(add_num(1,2))
print(add_num(1,'2'))

#isinstance()
	使用isinstance()函數,判斷數據類型
    isinstance(對象,數據類型)
def add_num(a,b):
    if isinstance(a,int) and isinstance(b,int):
        return a+b
    return None
print(add_num(1,2))
print(add_num(1,'2'))

#issubclass()
	使用issubclass()函數,能夠判斷一個類是否爲某個類的子類/子孫類
    issubclass(子類,父類)
class Animal:
    def eat(self):
        print('動物要吃東西...')
class Pig(Animal):
    def eat(self):
        print('豬要吃豬食')
class Tree:
    def light(self):
        print('植物進行光和做用')
def manage(obj):
    if issubclass(type(obj),Animal):
        obj.eat()
    else:
        print('不是動物!')
p = Pig()
t = Tree()
manage(p)
manage(t)

類中的魔法函數

#__str__
	只要是雙下,就會在某個時候自動執行
    __str__會在對象被轉換爲字符串時執行,轉換的結果就是這個函數的返回值

#打印對象,獲得內存地址
class Person:
    pass
p = Person()
print(p)

<__main__.Person object at 0x0000028E1471FA20>

#打印對象,獲得返回值
class Person:
    def __str__(self):
        return 'abc'
p = Person()
print(p) 		#abc
    
#python解釋器中的內置變量、內置函數、內置類,在程序運行結束會自動清理,可是python解釋器不會清理不屬於解釋器的資源,例如打開的文件等,這個時候就須要手動close關閉代碼
	#__del__,吸構函數,刪除對象/類,獲得返回值 
	#__del__函數只會執行一次
    #執行時機:
    	手動刪除對象時執行
        程序運行結束,就會執行類中的__del__函數
    #使用場景
    	當你的對象在使用過程當中,打開了不屬於python解釋器的資源,例如文件、網絡端口,在程序運行結束以後,就須要使用__del__函數來完成資源的釋放工做
        
import time
class Person:
    def __del__(self):
        print('del run')
p = Person()
print(p)
# del p
time.sleep(2)
print('end')
    
<__main__.Person object at 0x0000025385DEF400>
end
del run
    
class Filetool:
    '''該類用於文件的讀取操做'''
    def __init__(self,path):
        self.file = open(path,'rt',encoding='utf-8')
    def read(self):
        return self.file.read()
    #在這裏能夠肯定生成的對象再也不使用了,因此能夠放心的關閉文件了
    def __del__(self):
        tool.file.close()
tool = Filetool(r'E:\python_test\a.txt')
print(tool.read())		#a.txt 
    
#__init__,初始化函數、構造函數
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
p = Person('syy',18)
print(p.name)
    
#__call__函數,在調用對象的時候執行(對象加括號)
class A:
    def __call__(self, *args, **kwargs):
        print('call run')
        print(args)
        print(kwargs)
a = A()
a(1,'壹',z=233)
    
#python是一門動態的語言,能夠在代碼運行期間動態的修改對象的屬性所佔用的空間
	#若是代碼運行內存不足,那麼python解釋器就會開啓更大的內存區域,將原始的屬性遷移過去
    #這裏存在一個問題,若是python解釋器開啓內存太大,那麼將會形成內存的浪費
    #解決方法是,在建立對象時,告訴python解釋器要開啓多大的內存,而不是讓python解釋器被動的開啓內存
	#__slots__函數,該屬性是一個類屬性,用於優化對象內存,將本來不固定的屬性數量變得固定,這樣python解釋器就不會爲這個對象建立名稱空間,因此也沒了__dict__方法
    	#使用了該函數,對象將不能再添加、刪除屬性

#代碼1
import sys
class Person:
    def __init__(self,name):
        self.name = name
p = Person('syy')
print(sys.getsizeof(p))		#56

#代碼2
import sys
class Person:
    __slots__ = ['name']
    def __init__(self,name):
        self.name = name
p = Person('syy')
print(sys.getsizeof(p))		#48
# p.age = 18				#AttributeError: 'Person' object has no attribute 'age'
# print(p.__dict__)			#AttributeError: 'Person' object has no attribute 'age'

屬性的getattr、setattr、delattr函數

#這幾個函數體現了python解釋器如何使用點來設置、訪問、刪除屬性的
#使用點設置屬性的時候,執行__setattr__函數
#使用點訪問屬性(屬性不存在)的時候,執行___getattr__函數

class A:
    def __setattr__(self, key, value):
        print('__setattr__')
    def __getattr__(self, item):
        print('__getattr__')
    def __delattr__(self, item):
        print('__delattr__')
a = A()
a.name = 'syy'
print(a.name)

__setattr__
__getattr__
None

class A:
    def __setattr__(self, key, value):
        super().__setattr__(key,value)
        print('__setattr__')
    def __getattr__(self, item):
        print('__getattr__')
    def __delattr__(self, item):
        print('__delattr__')
a = A()
a.name = 'syy'			#經過點語法操做對象的屬性(原理是使用__dict__)
print(a.name)			#syy,變量設置成功,因此能夠打印出syy

__setattr__
syy

a = A()
a.__dict__['name'] = 'syy'		#經過__dict__設置屬性
print(a.name)					#syy

class A:
    def __setattr__(self, key, value):
        self.__dict__[key] = value
        print('__setattr__')
    def __getattr__(self, item):
        print('__getattr__')
    def __delattr__(self, item):
        print('__delattr__')
a = A()
print(a.xxx)					#__getattr__

#刪除屬性的時候,執行__delattr__函數
class A:
    def __setattr__(self, key, value):
        self.__dict__[key] = value
        print('__setattr__')
    def __getattr__(self, item):
        print('__getattr__')
    def __delattr__(self, item):
        print('__delattr__')
a = A()
a.name = 'syy'
del a.name
print(a.name)		#刪除不掉,緣由是由於__delattr__對應的函數沒有刪除的操做

__setattr__
__delattr__
syy

class A:
    def __setattr__(self, key, value):
        self.__dict__[key] = value
        print('__setattr__')
    def __getattr__(self, item):
        print('__getattr__')
    def __delattr__(self, item):
        self.__dict__.pop(item)
        print('__delattr__')
a = A()
a.name = 'syy'
del a.name
print(a.name)		#刪除成功

__setattr__
__delattr__
__getattr__
None

#訪問屬性的時候,不管有沒有該屬性都會執行__getattribute__函數,
class A:
    def __getattr__(self, item):
        print('__getattr__')
    def __getattribute__(self, item):
        print('__getattribute__')
a = A()
a.name = 'syy'
print(a.name)
print(a.xxx)

__getattribute__
None
__getattribute__
None

#__dict__方法的原理就是使用了__getattribute__函數
class A:
    def __getattr__(self, item):
        print('__getattr__')
    def __getattribute__(self, item):
        print('__getattribute__')
        return self.__dict__[item]
a = A()
a.name = 'syy'
print(a.name)
print(a.xxx)		#RecursionError: maximum recursion depth

#實際上python內部是先使用__getattribute__取值,若是取不到,再使用__getattr__
class A:
    def __getattr__(self, item):
        print('__getattr__')
    def __getattribute__(self, item):
        print('__getattribute__')
        return super(A, self).__getattribute__(item)
a = A()
a.name = 'syy'
print(a.name)
print(a.xxx)

__getattribute__
syy
__getattribute__
__getattr__
None

屬性的setitem、getitem、delitem

#這幾個函數體現了python解釋器如何使用[]來設置、訪問、刪除屬性的
#設置屬性的時候執行__setitem__
class A:
    def __setitem__(self, key, value):
        print('__setitem__')
    def __getitem__(self, item):
        print('__getitem__')
    def __delitem__(self, key):
        print('__delitem__')
a = A()
a['name'] = 'syy'		#__setitem__
print(a.name)			#AttributeError: 屬性不存在

#訪問屬性的時候執行__getitem__
class A:
    def __setitem__(self, key, value):
        print('__setitem__')
        self.__dict__[key] = value
    def __getitem__(self, item):
        print('__getitem__')
        return self.__dict__[item]
    def __delitem__(self, key):
        print('__delitem__')
        del self.__dict__[key]
a = A()
a['name'] = 'syy'
print(a['name'])

__setitem__
__getitem__
syy

#刪除屬性的時候執行__delitem__
class A:
    def __setitem__(self, key, value):
        print('__setitem__')
        self.__dict__[key] = value
    def __getitem__(self, item):
        print('__getitem__')
        return self.__dict__[item]
    def __delitem__(self, key):
        print('__delitem__')
        del self.__dict__[key]
a = A()
a['name'] = 'syy'
print(a['name'])
del a['name']
print(a['name'])		#KeyError: 'name'

__setitem__
__getitem__
syy
__delitem__
__getitem__

#例
	如何讓一個對象既支持點語法取值,也支持括號取值
    #對象自己就支持括號[]設置、訪問、刪除值,因此只須要添加點語法便可

class Mydict(dict):
    def __setattr__(self, key, value):
        self[key] = value
    def __getattr__(self, key):
        return self.get(key)
    def __delattr__(self, item):
        del self[item]
m = Mydict()
m['name'] = 'syy'
print(m['name'])		#syy
del m['name']
# print(m['name'])
m.name = 'zyy'
print(m.name)			#zyy
del m.name
# print(m.name)

運算符重載

#當咱們在使用某個符號時,python會爲這個符號定義一個含義,同時調用對應的處理函數,當咱們須要自定義對象的比較規則時,就能夠在子類中覆蓋 >、<、=、!=等方法
	#self和other指的是兩個對象
    #相似於大於、小於,咱們只須要實現一個便可,若是符號不一樣,python解釋器會自動交換兩個對象的位置

class Student:
    def __init__(self,name,age,height):
        self.name = name
        self.age = age
        self.height = height
    def __gt__(self, other):
        if self.height > other.height:
            return True
        else:
            return False
    def __lt__(self, other):
        if self.height < other.height:
            return True
        else:
            return False
    def __eq__(self, other):
        if self.name == other.name and self.age == other.age and self.height == other.height:
            return True
        else:
            return False
    def __ne__(self, other):
        if self.name == other.name or self.age == other.age or self.height == other.height:
            return True
        else:
            return False
s1 = Student('syy',18,180)
s2 = Student('zyy',80,140)
print(s1 > s2)
print(s1 < s2)
print(s1 == s2)
print(s1 != s2)

True
False
False
True

#代碼簡寫
class Student:
    def __init__(self,name,age,height):
        self.name = name
        self.age = age
        self.height = height
    def __gt__(self, other):
        return self.height > other.height
    def __lt__(self, other):
        return self.height < other.height
    def __eq__(self, other):
        return self.name == other.name and self.age == other.age and self.height == other.height
    def __ne__(self, other):
        return self.name != other.name or self.age != other.age or self.height != other.height
s1 = Student('syy',18,180)
s2 = Student('zyy',80,140)
print(s1 > s2)
print(s1 < s2)
print(s1 == s2)
print(s1 != s2)

True
False
False
True

迭代器協議

#迭代器的斷定方法
	1.內置有__iter__方法
	2.內置有__next__方法

#迭代器的做用
	迭代器就是一種取值的工具
    節省空間

#迭代器原理
class Myiter:
    def __init__(self,num):
        self.num = num
        self.count = 0
    def __iter__(self):
        return self
    def __next__(self):
        self.count += 1
        if self.count <= self.num:
            return '哈哈'
        else:
            raise StopIteration
for i in Myiter(10):
    print(i)

#生成器
class Myrange:
    def __init__(self,start,end,step):
        self.start = start
        self.end = end
        self.step = step
    def __iter__(self):
        return self
    def __next__(self):
        a = self.start
        self.start += self.step
        if a < self.end:
            return a
        else:
            raise StopIteration
for i in Myrange(1,10,2):
    print(i)

上下文管理

#上下文comtext
	指的是一段話的意義,要參考當前的場景,即參考上下文
	在python中,上下文能夠理解爲是一個代碼區間、一個範圍,例如with open,打開的文件盡在這個上下文中有效
	涉及到的兩個方法:
		enter:
            表示進入上下文,進入某個場景
		exit:
            表示退出上下文,退出某個場景
        #使用open類的with,先執行__enter__函數,open內代碼運行結束,最後執行__exit__函數
        
class Myopen:
    def __enter__(self):
        print('__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__')
        print(exc_type, exc_val, exc_tb)
with Myopen() as m:
    print('start')
    # '1' + 1			#open內代碼執行的過程當中,若是中途代碼異常,則以後的代碼再也不執行,可是當即__exit__函數仍會執行,包含錯誤的類型、錯誤的信息、錯誤的追蹤信息
    print('over')

__enter__
start
over
__exit__
None None None

class Myopen:
    def __init__(self,path):
        self.path = path
    def __enter__(self):
        self.file = open(self.path)
        return self
    def __exit__(self,exc_type, exc_val, exc_tb):
        self.file.close()
        return False        #經過返回的狀態碼判斷代碼運行的過程當中,有誒有錯誤或者錯誤有沒有被處理
with Myopen('a.txt') as m:
    print(m.file.read())

反射

#反射reflect
#什麼是反射
	其實reflect的意思是檢討、自省的意思
    反射指的是一個對象應該具有能夠檢測、修改、增長自身屬性的能力
    反射就是經過字符串操做屬性
    反射涉及到的四個內置函數(python解釋器自帶的),hasattr、getattr、setattr、delattr
    這幾個函數都是針對於對象的,對應於對象屬性是否存在、獲取、設置、刪除
    這幾個函數實際上就是封裝了__dict__等方法而已
    
#hasattr()函數
	#判斷一個屬性屬不屬於某個對象 
class Person:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
p = Person('syy',18,'male')	
print(hasattr(p,'name'))		#True
print(hasattr(p,'names'))   	#False
    
#getattr()函數
	#獲取對象的屬性,能夠設置默認返回值(屬性不存在的時候返回)
class Person:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
p = Person('syy',18,'male')
if hasattr(p,'name'):
    print(getattr(p,'name',None))
    
#setattr()函數
	#爲對象添加新的屬性
class Person:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
p = Person('syy',18,'male')
setattr(p,'id','123')
print(p.id)			#123
    
#delattr()函數
	#刪除對象的屬性
class Person:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
p = Person('syy',18,'male')
delattr(p,'name')
print(p.name)  			#AttributeError:

反射的使用場景

反射實際上就是對屬性的增刪改查,可是若是直接使用內置的__dict__來操做的話,會顯得語法繁瑣,很差理解,因此python封裝了4個函數
    封裝的另外一個緣由就是,若是對象不是我本身寫的而是另外一方提供的,那我就必須判斷這個對象是否具有我須要的屬性和方法
    反射是框架的基石,由於框架的設計者,不可能提早知道你的對象究竟是怎麼設計的,因此你提供給框架的對象,必須經過判斷驗證以後,才能正常的使用,而判斷驗證就是反射要作的事情,因此說反射就是對__dict__的操做進行封裝
    
#需求
	要實現一個用於處理用戶的終端指令的小框架
    所謂的框架,就是已經實現了最基礎的構架,就是全部的項目都同樣的部分
相關文章
相關標籤/搜索