第三模組第24章 面向物件
book.luffycity.com/python-book/index.html
https://www.cnblogs.com/linhaifeng/articles/6182264.html
1.3 程式設計正規化
# 程式設計正規化:
# 按照某種語法風格+資料結構+演算法來編寫程式
1.4 面向過程程式設計
面向過程: 核心是過程二字, 過程指的是解決問題的步驟, 設計一條流水線, 機械式的思維.
優點: 複雜的問題流程化, 進而簡單化
缺點: 可擴充套件性差
1.5 面向物件程式設計介紹
面向物件: 核心是物件二字, 物件是特徵與技能的結合體
優點: 可擴充套件性強
缺點: 程式設計複雜度高
應用場景: 使用者需求經常變化, 如網際網路應用, 遊戲, 企業內部應用
1.6 定義類與例項化出物件
類是一系列物件相似特徵與技能的結合體
強調: 站在不同的角度, 得到的分類是不一樣的
在現實世界中, 先有物件, 後有類
在程式中, 先定義類, 後呼叫類來產生物件
# 先定義類 class LuffyStudent: school = 'luffycity' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') # 後例項化出物件stu1 = LuffyStudent() stu2 = LuffyStudent() print(stu1) # <__main__.LuffyStudent object at 0x000001EB53893D68> print(stu2) # <__main__.LuffyStudent object at 0x000001EB538C6FD0>
1.7 如何使用類
''' 類在定義階段就會執行程式碼 類內定義的變數稱為資料屬性, 定義的函式稱為函式屬性 ''' class LuffyStudent: school = 'luffycity' # 資料屬性 deflearn(self): # 函式屬性 print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') # 在類的定義階段程式碼就會執行 print(LuffyStudent.__dict__) # 檢視類的名稱空間 ''' 結果: {'__module__': '__main__',
'school': 'luffycity', 'learn': <function LuffyStudent.learn at 0x0000019A2BC41488>, 'eat': <function LuffyStudent.eat at 0x0000019A2BC41A60>, 'sleep': <function LuffyStudent.sleep at 0x0000019A2BC41B70>, '__dict__': <attribute '__dict__' of 'LuffyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'LuffyStudent' objects>, '__doc__': None} ''' # 1. 檢視類的屬性 print(LuffyStudent.school) # luffycity print(LuffyStudent.learn) # <function LuffyStudent.learn at 0x0000019A2BC41488> # 2. 增加屬性 LuffyStudent.country = 'china' # 3. 刪除屬性 del LuffyStudent.country # 4. 修改屬性 LuffyStudent.school = 'Luffycity'
1.8 如何使用物件
# __init__方法用來為物件定製物件獨有的特徵 class LuffyStudent: school = 'luffycity' # 物件共有的特徵 def __init__(self, name, gender, age): # 例項化物件時自動呼叫本方法 self.name = name self.gender = gender self.age = age def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') stu1 = LuffyStudent('egon','male',18) # 加上__init__後, 例項化的步驟 # 1. 先產生一個空物件 # 2. 觸發Luffycity.__init__, 將空物件傳給self, 並將後面的引數傳進來 print(stu1.__dict__) # 檢視物件stu1的名稱空間 # 結果: {'name': 'egon', 'gender': 'male', 'age': 18} # 1.檢視物件的屬性 print(stu1.name) # 2. 修改物件的屬性 stu1.name = 'alex' # 3. 刪除 del stu1.name # 4. 增加 stu1.name = 'egon'
1.9 屬性查詢與繫結方法
class LuffyStudent: school = 'luffycity' def __init__(self, name, gender, age): self.name = name self.gender = gender self.age = age def learn(self, x): print('%s is learning %s'%(self.name,x)) def eat(self): print('%s is eating'%self.name) def sleep(self): print('%s is sleeping'%self.name) stu1 = LuffyStudent('egon','male',18) stu2 = LuffyStudent('alex','male',28) # 類中的資料屬性: 是所有物件共有的 print(LuffyStudent.school, id(LuffyStudent.school)) # luffycity 2210760573872, 類可以訪問 print(stu1.school, id(stu1.school)) # luffycity 2210760573872, 物件可以訪問 # 類中的函式屬性: 是繫結給物件使用的, 繫結到不同的物件是不同的繫結方法, 物件呼叫繫結方法時會將物件本身當成第一個引數傳入 print(LuffyStudent.learn) # <function LuffyStudent.learn at 0x00000214BB8E1B70> print(stu1.learn) # <bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x00000214BB8DF978>> print(stu2.learn) # <bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x00000214BB8DF9B0>> LuffyStudent.learn(stu1, '英語') # 類名呼叫函式, 需將物件傳進去, egon is learning 英語 stu1.learn('數學') # 物件呼叫方法, 無需傳物件, 只需傳self後的引數, egon is learning 數學 stu2.learn('語文') # alex is learning 語文 # 類中定義的函式屬性, 在未經處理的情況下, 不是給類用的, 是繫結給物件使用的. # 如果物件中和類中的屬性存在重名, 則優先查詢物件自己中的, 沒有則去類中找, 自己的類中沒有則去父類中去找, 都找不到會報錯, 不會去全域性中找.
1.10python中一切皆物件
1. 站在不同的角度, 定義出來的類是截然不同的
2. 現實中的類不完全等於程式中的類
3. 有時為了變成需要, 程式中可能會出現現實中不存在的類
# python中一切皆物件, python3中統一了類與型別的概念, 型別就是類. print(type([1,2])) # <class 'list'> l = [1, 2, 3] # l = list([1, 2, 3]), 類list例項化物件 l.append(5) #l物件呼叫繫結方法append
1.11 面向物件可擴充套件性總結
1.12 小練習1
''' 練習1: 編寫一個學生類, 產生一堆學生物件 要求: 有一個計數器(屬性), 統計總共例項了多少物件 ''' class Student: school = 'luffycity' count = 0 def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender # self.count += 1 會為物件建立私有屬性count Student.count+=1 def learn(self): print('%s is learning' %self.name) stu1 = Student('egon', 'male', 18) stu2 = Student('alex', 'female', 28) print(Student.count) # 2 print(stu1.count) # 2 print(stu2.count) # 2
1.13 小練習2
''' 練習2: 模仿王者榮耀定義兩個英雄類 要求: 英雄需要有暱稱, 攻擊力, 生命值等屬性 例項化出兩個英雄物件 英雄之間可以互毆, 被毆打的一方掉血, 血量小於0則判定為死亡. ''' class Garen: camp = 'Demacia' def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Riven: camp = 'Noxus' def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity g1 = Garen('草叢倫', 100, 30) r1 = Riven('瑞文', 80, 50) g1.attack(r1) print(r1.life_value) # 50
1.14 繼承與重用性
''' 什麼是繼承? 繼承指的是類與類之間的關係, 是一種'什麼是什麼的關係', 繼承的功能之一就是用來解決程式碼重用問題. 繼承是一種建立新類的方式, 在python中, 新建的類可以繼承一個或多個父類, 父類又可以稱為基類或超類, 新建的類稱為派生類或子類. ''' class ParentClass1: pass class ParentClass2: pass class SubClass1(ParentClass1): pass class SubClass2(ParentClass1, ParentClass2): pass print(SubClass1.__bases__) # 檢視父類, (<class '__main__.ParentClass1'>,) print(SubClass2.__bases__) # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>) class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' class Riven(Hero): camp = 'Noxus' g1 = Garen('草叢倫', 100, 30) r1 = Riven('瑞文', 80, 50) g1.attack(r1) print(g1.nickname) # 從物件自己這找, 如果沒有則去類中找, 如果沒有則去父類中找, 否則報錯, 不會去全域性找. print(r1.life_value) # 50 # 屬性查詢小練習 class Foo: def f1(self): print('from Foo.f1') def f2(self): print('from FOO.f2') self.f1() # b.f1() class Bar(Foo): def f1(self): print('from Bar.f1') b = Bar() b.f2() ''' 結果: from FOO.f2 from Bar.f1 '''
1.15 派生
# 如果子類中派生出新的屬性, 則以新的屬性為主 class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def attack(self, enemy): print('from Garen class') class Riven(Hero): camp = 'Noxus' g1 = Garen('草叢倫', 100, 30) r1 = Riven('瑞文', 80, 50) g1.attack(r1) print(g1.nickname) # 從物件自己這找, 如果沒有則去類中找, 如果沒有則去父類中找, 否則報錯, 不會去全域性找. print(r1.life_value) # 80
1.16 繼承的實現原理
python到底是如何實現繼承的? 對於定義的每一個類, python會計算出一個方法解析順序(MRO)列表, 該列表是一個簡單的所有基類的線性排序列表, 例如:
>>> F.mro() #等同於F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
為了實現繼承, python會在MRO列表上從左到右開始查詢基類, 查詢到第一個匹配這個屬性的類為止, 而這個MRO列表的構造是通過一個C3線性演算法來實現的. 它實際上就是合併所有父類的MRO列表並遵循如下三條準則:
1. 子類會先於父類被檢查
2. 多個父類會根據它們在列表中的順序被檢查
3. 如果對下一個類存在兩個合法的選擇, 選擇第一個父類.
在Java和C#中子類只能繼承一個父類,而Python中子類可以同時繼承多個父類,如果繼承了多個父類,那麼屬性的查詢方式有兩種,分別是:深度優先和廣度優先
python中類分為兩種: 新式類和經典類, python2中有這兩種分法, python3中只有新式類.
python2中的經典類: 沒有繼承object的類, 以及它的子類都稱為經典類.
python2中的新式類: 繼承了object的類, 以及它的子類都稱為新式類.
python3中預設繼承object類, 都是新式類
class Foo: pass print(Foo.__bases__) # (<class 'object'>,)
class Foo: pass print(Foo.__bases__) # (<class 'object'>,) # 驗證多繼承情況下的屬性查詢 class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有這個屬性可以檢視線性列表,經典類沒有這個屬性 ''' 結果: (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) ''' #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類
1.17 在子類中重用父類的方法或屬性
# 在子類派生出的新的方法中重用父類的方法, 有兩種實現方式: # 方式一: 指名道姓(不依賴繼承) # 方式二: super()(依賴繼承), super會在MRO列表中尋找關係 class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): # Hero.__init__(self, nickname, life_value, aggresivity) # 指明道姓的方式 super(Garen, self).__init__(nickname, life_value, aggresivity) # super(Garen, self)建立了一個物件, python2中需要這麼寫, 但是在python3中可以簡寫成以下方式: # super().__init__(nickname, life_value, aggresivity) self.weapon = weapon def attack(self, enemy): # Hero.attack(self, enemy) # 指明道姓的方式, 不依賴於繼承關係 super(Garen, self).attack(enemy) print('from Garen class') class Riven(Hero): camp = 'Noxus' g1 = Garen('草叢倫', 100, 30, '金箍棒') r1 = Riven('瑞文', 80, 50) g1.attack(r1) print(r1.life_value) # 50 class A: def f1(self): print('from A') super().f1() class B: def f1(self): print('from B') class C(A, B): pass print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] c = C() c.f1() ''' 結果: from A from B ''' # 說明, 在查詢時基於C的MRO列表進行查詢.
1.18 組合
# 組合: 解決誰有誰的類之間的關係. class Person: school = 'luffycity' def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender class Teacher(Person): school = 'luffycity' def __init__(self, name, age, gender, level, salary): super().__init__(name, age, gender) self.level = level self.salary = salary def teach(self): print('%s is teaching' %self.name) class Student(Person): school = 'luffycity' def __init__(self, name, age, gender, clas): super().__init__(name, age, gender) self.clas = clas def teach(self): print('%s is teaching' %self.name) class Course: def __init__(self, cname, price, period): self.cname = cname self.price = price self.period = period def tell_info(self): print('課程名<%s>'%self.cname) t1 = Teacher('alex', 18, 'female', 10, 3000) s1 = Student('egon', 28, 'female', '一班') c1 = Course('語文', 100, 20) c2 = Course('數學', 200, 50) t1.course1 = c1 # t1的屬性指向了一個物件 print(t1.course1.cname) t1.course1.tell_info() t1.course2 = c2 # t1的屬性指向了一個物件 print(t1.course2.cname) t1.course2.tell_info() s1.courses = [] s1.courses.append(c1) s1.courses.append(c2)
1.19 抽象類與歸一化
import abc class Animal(metaclass=abc.ABCMeta): # 抽象類, 只能被繼承, 不能被例項化. 其作用是規範子類. all_type = 'animal' @abc.abstractmethod def run(self): pass @abc.abstractmethod def eat(self): pass class People(Animal): def run(self): # 必須包含父類中的run和eat方法. print('People is running') def eat(self): print('People is eating') class Pig(Animal): def run(self): print('Pig is running') def eat(self): print('Pig is eating') peop1 = People() pig1 = Pig() peop1.run() # People is running pig1.run() # Pig is running
1.20 多型與多型性
# 多型: 同一類事物的多種形態 import abc class Animal(metaclass=abc.ABCMeta): all_type = 'animal' @abc.abstractmethod def run(self): pass class People(Animal): def run(self): print('People is running') class Pig(Animal): def run(self): print('Pig is running') # 多型性: 指的是可以在不考慮物件型別的情況下直接使用物件 # peop1, pig1都是動物, 只要是動物肯定有run方法, 於是我們可以不用考慮它們具體是什麼型別, 可以直接使用 peop1 = People() pig1 = Pig() peop1.run() # People is running pig1.run() # Pig is running # 以上稱為動態多型性 # 靜態多型性: 如任何型別都可以使用運算子+進行運算 # 更進一步, 我們可以定義一個統一的介面進行使用 def func(animal): animal.run() func(peop1) func(pig1) # 以上示例中繼承了同一父類, 子類受父類的約束 ''' 多型性的好處: 1. 增加了程式的靈活性 以不變應萬變, 不論物件千變萬化, 使用者都是同一形式去呼叫 2. 增加了程式的可擴充套件性 ''' class File: def read(self): print('file read') def write(self): print('file write') class Disk: def read(self): print('disk read') def write(self): print('disk write') class Text: def read(self): print('text read') def write(self): print('text write') f = File() d = Disk() t = Text() def f1(x): x.read() x.write() f1(f) f1(d) f1(t) # 以上示例沒有繼承同一父類, 但是彼此存在相似之處. ''' python崇尚鴨子型別, 只要長得像鴨子, 就是鴨子. 類與類之間不繼承同一父類, 只要有相似之處, 就可以了. '''
1.21 封裝之如何隱藏屬性
class A: __x = 1 # 變形隱藏 _A__x = 1 def __init__(self, name): self.__name = name def __foo(self): # 定義階段, 檢測語法時轉化: def _A__foo(self): print('run foo') def bar(self): self.__foo() # self._A__foo() print('from bar') # print(A.__x) # AttributeError: type object 'A' has no attribute '__x', 說明已經隱藏了 a = A('egon') print(A.__dict__) print(a.__dict__) # {'_A__name': 'egon'} a.bar() # run foo from bar a._A__foo() # run foo ''' 這種變形的特點: 1. 在類外部無法直接通過obj.__AttriName訪問 2. 在類內部可以直接通過obj.__AttriName訪問 3. 子類無法覆蓋父類__開頭的屬性 ''' class Foo: def __func(self): # _Foo__func print('from foo') class Bar(Foo): def __func(self): # _Bar__func print('from bar') # 加__會在類的定義階段進行變形 ''' 總結這種變形需要注意的問題: 1. 這種機制並沒有真正意義上限制我們從外部直接訪問屬性, 知道了類名和屬性名就可以拼出名字: _類名__屬性名,然後就可以訪問了. 2. 變形的過程只在類的定義階段發生一次, 在定義後的賦值操作, 不會變形. 3. 在繼承中, 父類如果不想讓子類覆蓋自己的方法, 可以將方法定義為私有 ''' # 驗證問題2 class B: __x = 1 def __init__(self, name): self.__name = name B.__y = 2 print(B.__dict__) # {..., '__y': 2} # 驗證問題3 class C: def foo(self): print('C.foo') def bar(self): print('C.bar') self.foo() class D(C): def foo(self): print('D.foo') d = D() d.bar() #C.bar D.foo class C: def __foo(self): # _C__foo print('C.foo') def bar(self): print('C.bar') self.__foo() # self._C__foo class D(C): def __foo(self): # _D__foo print('D.foo') d = D() d.bar() # C.bar C.foo
1.22 封裝的意義
# 一, 封裝資料屬性的目的: 明確地區分內外, 控制外部對隱藏屬性的操作行為. class People: def __init__(self, name, age): self.__name = name self.__age = age def tell_info(self): print('Name:<%s>, Age:<%s>' %(self.__name, self.__age)) def set_info(self, name, age): if not isinstance(name,str): print('名字必須是字串型別') return if not isinstance(age, int): print('年齡必須是數字型別') return self.__name = name self.__age = age p = People('egon', 18) p.tell_info() # 加了__後則無法直接訪問或修改屬性, 必須通過特定的介面(如tell_info, set_info)來間接進行訪問和修改. # 介面上附加一些邏輯, 來控制外部對隱藏屬性的訪問和修改. # 二, 封裝方法的目的: 隔離複雜度 # 不讓直接接觸到零散的方法, 如__card, __auth, 可以直接接觸到封裝好的方法, 如withdraw. class ATM: def __card(self): print('插卡') def __auth(self): print('使用者認證') def __input(self): print('輸入取款金額') def __print(self): print('列印賬單') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print() self.__take_money() a = ATM() a.withdraw()
1.23 封裝與可擴充套件性
class Room: def __init__(self, name, owner, height, weight, length): self.name = name self.owner = owner self.__height = height self.__weight = weight self.__length = length def tell_area(self): return self.__weight * self.__length r=Room('衛生間','alex',10,10,10) print(r.tell_area()) # 使用者只需要記住介面即可, 無需關注裡面的原理.
1.24 property的使用
class People: def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height @property # 將bmi的呼叫方式由bmi()轉為bmi, 使使用者感覺呼叫的不是一個功能, 而是一個屬性. def bmi(self): return self.weight / (self.height ** 2) p = People('egon', 75, 1.81) print(p.bmi) # 注意: bmi本質是一個方法, 不能對其進行賦值. class People: def __init__(self, name): self.__name = name @property def name(self): return self.__name # 注意: name本質是一個方法, 不能對其進行賦值. # 不過, 通過以下方法可以實現賦值. @name.setter def name(self, val): if not isinstance(val, str): print('名字必須是字串型別') self.__name = val @name.deleter def name(self): print('不允許刪除') p = People('egon') p.name = 'EGON' print(p.name) # EGON del p.name # 不允許刪除
1.25 繫結方法與非繫結方法介紹
''' 在類內部定義的函式, 分為兩大類: 一: 繫結方法: 繫結給誰, 就應該由誰來呼叫, 誰來呼叫就自動把誰當作第一個引數傳入. 繫結到物件的方法: 在類內定義的沒有被任何裝飾器修飾的方法 繫結到類的方法: 在類內定義的被裝飾器classmethod修飾的方法 二: 非繫結方法: 沒有自動傳值這麼一說, 就是在類中定義的一個普通工具, 物件和類都可以使用. 非繫結方法: 不與類或者物件繫結 ''' class Foo: def __init__(self, name): self.name = name def tell(self): # 繫結到物件的方法 print('名字是%s' %self.name) @classmethod def func(cls): # 繫結到類的方法 print(cls) @staticmethod def func1(x, y): #括號裡面可以寫引數, 也可以不寫引數. 沒有自動傳遞的引數. print(x+y) f = Foo('egon') print(Foo.tell) # <function Foo.tell at 0x0000026860001A60> Foo.tell(f) # 此處, 必須對self進行傳值, self不能自動傳值. print(f.tell) # <bound method Foo.tell of <__main__.Foo object at 0x000002B04F8B3CC0>> f.tell() print(Foo.func) # <bound method Foo.func of <class '__main__.Foo'>> Foo.func() # <class '__main__.Foo'> print(Foo.func1) # <function Foo.func1 at 0x000002BF6F551BF8> print(f.func1) # <function Foo.func1 at 0x000002BF6F551BF8> Foo.func1(1,2) # 3 f.func1(1,3) # 4
1.26 繫結方法與非繫結方法的使用
import settings import hashlib import time class People: def __init__(self, name, age, gender): self.id = self.create_id() self.name = name self.age = age self.gender = gender def tell_info(self): # 繫結到物件的方法 print('Name: %s, Age:%s, Gender:%s'%(self.name, self.age, self.gender)) @classmethod def from_conf(cls): obj = cls(settings.name, settings.age, settings.gender) return obj @staticmethod def create_id(): time.sleep(0.01) m = hashlib.md5(str(time.time()).encode('utf-8')) return m.hexdigest() p1 = People('egon', 18, 'male') # 繫結給物件, 就應該由物件來呼叫, 自動將物件本身當作第一個引數傳入 p1.tell_info() # Name: egon, Age:18, Gender:male # 繫結給類, 就應該由類來呼叫, 自動將類本身當作第一個引數傳入 p2 = People.from_conf() p2.tell_info() # Name: alex, Age:10, Gender:male # 非繫結方法, 不與類或者物件繫結, 誰都可以來呼叫, 沒有自動傳值一說. p3 = People('aaa', 12, 'female') p4 = People('bbb', 22, 'male') p5 = People('ccc', 18, 'female') print(p3.id) # e4487e9a7fed738946cd85e9d38bd795 print(p4.id) # b02113fa5ebd6a9e1c50b25c2e7c48f3 print(p5.id) # 8f157e0fd5d76047055fc5f77fc90b98
# settings.py name = 'alex' age = 10 gender = 'male'
1.27 反射
# 反射: 通過字串對映到物件的屬性 class People: country = 'china' def __init__(self, name, age): self.name = name self.age = age def talk(self): print('%s is talking' %self.name) obj = People('egon', 18) # 1. hasattr print(hasattr(obj,'name')) # True, 判斷obj下是否有name屬性 print(hasattr(obj,'talk')) # True, 判斷obj下是否有talk # 2. getattr print(getattr(obj, 'name')) # egon 有則返回結果, 沒有則報錯, 如果不想讓其報錯, 可以使用default print(getattr(obj, 'namex', None)) # None, 沒有返回None # 3. setattr setattr(obj, 'age', 50) print(obj.age) # 50 setattr(obj, 'gender', 'male') print(obj.__dict__) # {'name': 'egon', 'age': 50, 'gender': 'male'} print(obj.gender) # male # 4. delattr delattr(obj, 'gender') print(obj.__dict__) # {'name': 'egon', 'age': 50} # 以上方法也適用於類 print(getattr(People, 'country')) # china # 反射的應用: class Service: def run(self): while True: inp = input('>>>:').strip() cmds = inp.split() if hasattr(self, cmds[0]): func=getattr(self, cmds[0]) func(cmds) def get(self, cmds): print('get...', cmds) def put(self, cmds): print('put...', cmds) obj = Service() # 接收使用者輸入並觸發後面的方法 obj.run() ''' 結果: >>>:get get... ['get'] >>>:get a.txt get... ['get', 'a.txt'] '''
1.28 內建方法介紹
https://www.cnblogs.com/linhaifeng/articles/6204014.html
一: isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)檢查是否obj是否是類 cls 的物件
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 isinstance(obj, Foo)
issubclass(sub, super)檢查sub類是否是 super 類的派生類
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 issubclass(Bar, Foo)
二: item系列
# item系列 # 將物件模擬地像字典 class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): return self.__dict__.get(item) def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): # del self.__dict__[key] self.__dict__.pop(key) obj = Foo('egon') # 檢視屬性 print(obj['name']) # 設定屬性 obj['gender'] = 'male' # 刪除屬性 del obj['name']
# __str__方法: class People: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return '<name:%s, age:%s>'%(self.name, self.age) obj = People('egon', 18) print(obj) # 列印物件時觸發物件下的__str__方法, 並且要求返回的一定是字串型別的資料
# __del__方法 # 在python自動回收物件之前觸發__del__, 回收跟物件相關聯的某些資源. class Open: def __init__(self, filename): print('open file...') self.filename = filename def __del__(self): print('回收作業系統等資源: f.close()') f = Open('settings.py') print('--------main---------') # del f 會手動觸發__del__, 不使用這種方法則自動呼叫__del__ ''' 結果: open file... --------main--------- del... '''
1.29 元類
https://www.cnblogs.com/linhaifeng/articles/8029564.html
# 元類介紹 ''' 儲備知識exec命令的使用, 有三個引數 引數1: 字串形式的命令 引數2: 全域性作用局(字典形式), 如果不指定就預設使用globals() 引數3: 區域性作用局(字典形式), 如果不指定就預設使用locals() ''' g = { 'x':1, 'y':2 } l = {} exec(''' global x,m x = 10 m = 100 z = 3 ''', g, l) print(g) # 可以將exec當作函式看待 ''' python中一切皆物件, 物件可以怎麼用? 1. 都可以被引用, x = obj 2. 都可以當作函式的引數傳入 3. 都可以當作函式的返回值 4. 都可以當作容器型別的元素 ''' # 類也是物件, 物件是經過例項化類得來的, 那麼Foo也應該是由類例項化得到的. class Foo: # Foo = type(...) pass obj = Foo() print(type(obj)) # <class '__main__.Foo'> print(type(Foo)) # <class 'type'> #產生類的類稱之為元類, 預設所有用class定義的類, 它們的元類是type. # 定義類的兩種方式: # 方式一: class class Chinese: # Chinese = type(...) country = 'china' def __init__(self, name, age): self.name = name self.age = age def talk(self): print('%s is talking' %self.name) # 方式二: # 定義類的三要素: 類名, 類的基類們, 類的名稱空間 class_name = 'Chinese' class_bases = (object,) class_body = ''' country = 'china' def __init__(self, name, age): self.name = name self.age = age def talk(self): print('%s is talking' %self.name) ''' class_dic = {} exec(class_body,globals(),class_dic) print(class_dic) # {'country': 'china', '__init__': <function __init__ at 0x0000022563A21EA0>, 'talk': <function talk at 0x0000022563A380D0>} Chinese1 = type(class_name,class_bases,class_dic) print(Chinese1) # <class '__main__.Chinese'> obj1 = Chinese1('alex', 20) print(obj1, obj1.name, obj1.age) # <__main__.Chinese object at 0x0000018894F4FF98> alex 20
# 定義元類控制類的建立 class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): if not class_name.istitle(): raise TypeError('類名的首字母必須大寫') if '__doc__' not in class_dic or not class_dic['__doc__'].strip(): raise TypeError('必須有註釋, 且註釋不能為空') super(Mymeta, self).__init__(class_name, class_bases, class_dic) class Chinese(object,metaclass=Mymeta): # Chinese = Mymeta(class_name, class_bases, class_dic) '''''' country = 'china' def __init__(self, name, age): self.name = name self.age = age def talk(self): print('%s is talking' %self.name)
# 自定義元類控制類的例項化行為 # 知識儲備__call__方法 class Foo: def __call__(self, *args, **kwargs): print(args, kwargs) obj = Foo() obj(1, 2, x = 'aa') # (1, 2) {'x': 'aa'}, 在呼叫物件時會自動觸發執行__call__方法 # 元類內部也應該有一個__call__方法, 會在呼叫Foo時觸發執行. class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): if not class_name.istitle(): raise TypeError('類名的首字母必須大寫') if '__doc__' not in class_dic or not class_dic['__doc__'].strip(): raise TypeError('必須有註釋, 且註釋不能為空') super(Mymeta, self).__init__(class_name, class_bases, class_dic) def __call__(self, *args, **kwargs): # 第一件事: 先造出一個空物件obj obj = object.__new__(self) # 第二件事: 初始化obj self.__init__(obj,*args,**kwargs) # 第三件事: 返回obj return obj class Chinese(object,metaclass=Mymeta): # Chinese = Mymeta(class_name, class_bases, class_dic) ''' xxx ''' country = 'china' def __init__(self, name, age): self.name = name self.age = age def talk(self): print('%s is talking' %self.name) obj = Chinese('egon', age=18) print(obj.__dict__)
# 自定義元類控制類的例項化行為的應用 # 單例模式 # 實現方式一: class MySQL: __instance = None def __init__(self): self.host = '127.0.0.1' self.port = 3306 @classmethod def singleton(cls): if not cls.__instance: obj = cls() cls.__instance=obj return cls.__instance obj1 = MySQL.singleton() obj2 = MySQL.singleton() # 實現方式二: 元類的方式 class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): if not class_name.istitle(): raise TypeError('類名的首字母必須大寫') if '__doc__' not in class_dic or not class_dic['__doc__'].strip(): raise TypeError('必須有註釋, 且註釋不能為空') super(Mymeta, self).__init__(class_name, class_bases, class_dic) self.__instance = None def __call__(self, *args, **kwargs): if not self.__instance: obj = object.__new__(self) self.__init__(obj) self.__instance = obj return self.__instance class Mysql(object,metaclass=Mymeta): ''' xxx ''' def __init__(self): self.host = '127.0.0.1' self.port = 3306 obj1 = Mysql() obj2 = Mysql() obj3 = Mysql() print(obj1 is obj2 is obj3) # True
面向物件實戰:
https://www.cnblogs.com/linhaifeng/articles/6182264.html#_label15