#什麼是對象 面向對象是一種編程思想,是指導程序員如何編寫出更好的程序的思想 核心是對象,程序就是一系列對象的集合,咱們程序員只須要調度、控制這些對象來交互完成工做 #案例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)
#什麼是繼承 繼承是一種關係,描述兩個類之間的關係 在程序中,繼承描述的是類和類之間的關係(父類、基類、子類) 在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])
參考網站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裝飾器 該裝飾器爲了解決不能刪除封裝的屬性的問題,相似於觸發器 使用了該裝飾器,類中隱藏屬性的刪除,能夠直接刪除,也能夠設置觸發條件 #原始代碼 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))
#該內置函數不管在不在類中,都能執行 #代碼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'
#這幾個函數體現了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
#這幾個函數體現了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__的操做進行封裝 #需求 要實現一個用於處理用戶的終端指令的小框架 所謂的框架,就是已經實現了最基礎的構架,就是全部的項目都同樣的部分