1. 程式人生 > >python之初識面向物件

python之初識面向物件

面向過程,函式式,面向物件三者之間的區別:

  • 面向過程:根據業務邏輯從上到下寫壘程式碼
  • 函式式:將某功能程式碼封裝到函式中,日後便無需重複編寫,僅呼叫函式即可
  • 面向物件:對函式進行分類和封裝,讓開發“更快更好更強...
1.類的基本結構class A:    name = 'alex' #  靜態屬性,靜態變數,靜態欄位。    def func1(self):  # 函式,動態屬性,方法。        pass2.函式與面向物件的區別    1,函式封裝一個功能,而面向物件封裝多個相關的功能。    2,面向物件抽象,它是一種思想,站在上帝的角度去理解他。    3,程式可擴充套件,物件都是一個個獨立的。耦合性,差異性。
3.類與物件的區別 類:具有相同屬性或者功能的一類實物。 物件:物件是類的具體體現。類的申明:  class 類名: #class申明一個類,類名首字母大寫    類屬性      類方法例項化:類名加括號就是例項化,會自動觸發__init__函式的執行,可以用它來為每個例項定製自己的特徵
 1 class Person:   #定義一個人類
 2     role = 'person'  #人的角色屬性都是人
 3     def __init__(self,name):
 4         self.name = name  # 每一個角色都有自己的暱稱;
 5         
 6
def walk(self): #人都可以走路,也就是有一個走路方法 7 print("person is walking...") 8 9 10 print(Person.role) #檢視人的role屬性 11 print(Person.walk) #引用人的走路方法,注意,這裡不是在呼叫

例項化的過程就是類——>物件的過程

原本我們只有一個Person類,在這個過程中,產生了一個egg物件,有自己具體的名字、攻擊力和生命值。

語法:物件名 = 類名(引數)

例項化過程內部進行了三個階段:1,在記憶體中開闢了一個物件空間2,自動執行類中的__init__方法,並且將物件空間自動傳給self引數,其他引數手動傳入。
3, 執行__init__方法 給物件空間封裝相應的屬性。

self

self:在例項化時自動將物件/例項本身傳給__init__的第一個引數

類屬性的補充

 1 一:我們定義的類的屬性到底存到哪裡了?有兩種方式檢視
 2 dir(類名):查出的是一個名字列表
 3 類名.__dict__:查出的是一個字典,key為屬性名,value為屬性值
 4 
 5 二:特殊的類屬性
 6 類名.__name__# 類的名字(字串)
 7 類名.__doc__# 類的文件字串
 8 類名.__base__# 類的第一個父類
 9 類名.__bases__# 類所有父類構成的元組
10 類名.__dict__# 類的字典屬性
11 類名.__module__# 類定義所在的模組
12 類名.__class__# 例項對應的類(僅新式類中)
1 print(Person.animal)  #
2 print(Person.language)
3 Person.name = 'alex'  #
4 print(Person.name)
5 Person.animal = '低階動物' #
6 del Person.walk_way  #
7 print(Person.__dict__)
檢視,(增刪改)類中某個,某些屬性 用萬能的點 .

類名稱空間與物件的名稱空間

建立一個類就會建立一個類的名稱空間,用來儲存類中定義的所有名字,這些名字稱為類的屬性

而類有兩種屬性:靜態屬性和動態屬性

  • 靜態屬性就是直接在類中定義的變數
  • 動態屬性就是定義在類中的方法

其中類的資料屬性是共享給所有物件的,而類的動態屬性是繫結到所有物件的。

建立一個物件/例項就會建立一個物件/例項的名稱空間,存放物件/例項的名字,稱為物件/例項的屬性

物件操作物件空間        物件檢視物件空間所有的屬性  __dict__        print(obj.__dict__)        物件操作物件的某個屬性 增刪改查  萬能的點.      物件操作類空間的屬性 只能查物件能呼叫類中的屬性與方法而且只是呼叫不能改變物件與物件之間可不可互相呼叫?同一個類例項化出來的物件之間是不能互相訪問的。不同類例項化的物件有可能互相訪問。給物件封裝屬性:__init__ 任意位置。
組合: 給一個類的物件封裝一個屬性,這個屬性是另一個類的物件。組合的意義:讓類的物件與另一個類的物件產生關係,類與類之間產生關係
 1 class Gamerole:
 2     def __init__(self,nickname,ad,hp):
 3         self.nickname = nickname
 4         self.ad = ad
 5         self.hp = hp
 6 
 7     def attack(self,role):
 8         role.hp = role.hp - self.ad
 9         print('%s攻擊%s,%s掉了%s血,還剩%s血' %\
10               (self.nickname,role.nickname,role.nickname,self.ad,role.hp))
11 
12     def equip_weaon(self,w):  # 給人物封裝了一個武器屬性,這個屬性值是Weapon類的一個物件
13         self.weapon = w   # 組合
14 
15 
16 class Weapon:
17     def __init__(self,name,ad):
18         self.name = name
19         self.ad = ad
20     def fight(self,role1,role2):
21         role2.hp = role2.hp - self.ad
22         print('%s 用 %s 攻擊了%s, %s掉了%s血,還剩%s血'\
23               %(role1.nickname,self.name,role2.nickname,role2.nickname,self.ad,role2.hp))
24 
25 p1 = Gamerole('蓋倫',20,500)
26 p2 = Gamerole('劍豪',100,200)
27 p1.attack(p2)
28 print(p2.hp)
29 w1.fight(p1,p2) # 這樣不好,動作的發起者應該是人而不是武器
30 w1 = Weapon('大寶劍',30)
31 w2 = Weapon('武士刀',80)
32 print(w1)
33 p1.equip_weaon(w1)
34 print(p1.weapon)  #其實 他就是w1
35 p1.weapon.fight(p1,p2)
36 讓劍豪利用武士刀給蓋倫一刀
37 p2.equip_weaon(w2)
38 p2.weapon.fight(p2,p1)

 面向物件三大特性(封裝,繼承,多型)

1.封裝封裝的定義    隱藏物件的屬性和實現細節,僅對外提供公共訪問方式。封裝的優點:    1. 將變數隔離;    2. 便於使用;    3. 提高複用性;    4. 提高安全性;封裝原則      1. 將不需要對外提供的內容都隱藏起來;      2. 把屬性都隱藏,提供公共方法對其訪問。封裝的分類:廣義封裝和狹義封裝廣義上的封裝(把屬性函式都放到類裡)class 類名:    def 方法1(self):pass是為了只有這個類的物件才能使用定義在類中的方法狹義上的封裝(定義私有成員,私有變數和私有方法)把一個名字藏在類中(採用變數名前加雙下劃線方式)class Goods:    __discount = 0    # 私有的靜態變數    print(__discount)print(Goods.__discount)  # 在類的外部不能引用私有的靜態變數類中的靜態變數和方法名在程式載入的過程中就已經執行完了,不需要等待呼叫在這個類載入完成之前,Goods這個名字還沒有出現在記憶體空間中私有的靜態屬性可以在類的內部使用,用來隱藏某個變數的值類中所有雙下劃線開頭的名稱如__x都會自動變形成:_類名__x的形式:
1 class A:
2     __N=0 #類的資料屬性就應該是共享的,但是語法上是可以把類的資料屬性設定成私有的如__N,會變形為_A__N
3     def __init__(self):
4         self.__X=10 #變形為self._A__X
5     def __foo(self): #變形為_A__foo
6         print('from A')
7     def bar(self):
8         self.__foo() #只有在類內部才可以通過__foo的形式訪問到.
A._A__N是可以訪問到的,即這種操作並不是嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形這種自動變形的特點:1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。2.這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。3.在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。這種變形需要注意的問題是:1.這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,    知道了類名和屬性名就可以拼出名字:_類名__屬性,然後就可以訪問了,如a._A__N2.變形的過程只在類的內部生效,在定義後的賦值操作,不會變形
 1 class Goods:
 2     __discount = 0    # 私有的靜態變數
 3     # 變形 : _類名__私有變數
 4 
 5 print(Goods.__dict__)
 6 print(Goods._Goods__discount)  # 程式設計規範的角度上出發 我們不能在類的外部使用私有的變數
 7 
 8 class Srudent:
 9     def __init__(self,name,age):
10         self.__name = name
11         self.age = age
12     def name(self):
13         return self.__name
14 zhuge = Srudent('諸葛',20)
15 print(zhuge.name())
16 print(zhuge.age)
17 zhuge.age = 'aaaa'
18 print(zhuge.age)
 1 class Goods:
 2     __discount = 0.7    # 私有的靜態變數
 3     def __init__(self,name,price):
 4         self.name = name
 5         self.__price = price
 6 
 7     def price(self):
 8         return self.__price * Goods.__discount
 9 
10     def change_price(self,new_price):
11         if type(new_price) is int:
12             self.__price = new_price
13         else:
14             print('本次價格修改不成功')
15 
16 apple = Goods('APPLE',5)
17 print(apple.price())
18 apple.change_price('aaaa')
19 print(apple.price())
 1 class User:
 2     def __init__(self,username,password):
 3         self.usr = username
 4         self.__pwd = password
 5         self.pwd = self.__getpwd()
 6 
 7     def __getpwd(self):
 8         return hash(self.__pwd)
 9 
10 obj = User('alex','alex3714')
11 print(obj.usr,obj.pwd)
類中的私有成員:    私有的靜態屬性    私有的物件屬性    私有的方法我為什麼要定義一個私有變數呢:    我不想讓你看到這個值    我不想讓你修改這個值    我想讓你在修改這個值得時候有一些限制    有些方法或者屬性不希望被子類繼承私有變數能不能在外部被定義???
1 class A :
2     __country = 'China'  # 在類的內部會發生變形
3     print(__country)      # '_A__country'
4 print(A.__dict__)
5 A.__Language = 'Chinese'
6 print(A.__dict__)
私有變數能不能被繼承???
 1 class A:
 2     __country = 'China'
 3     def __init__(self,name):
 4         self.__name = name  # '_A__name'
 5 
 6 class B(A):
 7     # print(__country)
 8     # NameError: name '_B__country' is not defined
 9     def get_name(self):
10         return self.__name  # '_B__name'
11 
12 b = B('alex')
13 print(b.__dict__)
property是一個裝飾器函式將一個類的函式定義成特性以後,物件再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函式然後計算出來的,這種特性的使用方式遵循了統一訪問的原則所有的裝飾器函式都怎麼用? 在函式、方法、類的上面一行直接@裝飾器的名字裝飾器的分類:    裝飾函式    裝飾方法 : property    裝飾類
 1 class Student:
 2     def __init__(self,name,age):
 3         self.__name = name
 4         self.age = age
 5     @property   # 將一個方法偽裝成一個屬性
 6     def name(self):
 7         return self.__name
 8 zhuge = Student('諸葛',20)
 9 print(zhuge.name)
10 
11 from math import pi
12 class Circle:
13     def __init__(self,r):
14         self.r = r
15     @property
16     def area(self):
17         return self.r ** 2 * pi
18     @property
19     def perimeter(self):
20         return 2 * self.r * pi
21 
22 c1 = Circle(10)
23 print(c1.area)
24 print(c1.perimeter)
25 c1.r = 5
26 print(c1.area)
27 print(c1.perimeter)
一個方法被偽裝成屬性之後應該可以執行一個屬性的增刪改查操作那麼增加和修改 就對應這被setter裝飾的方法 :這個方法又一個必傳的引數new,表示賦值的時候等號後面的值刪除一個屬性 對應著 被deleter裝飾的方法,這個方法並不能在執行的時候真的刪除這個屬性,而是你在程式碼中執行什麼就有什麼效果一個靜態屬性property本質就是實現了get,set,delete三種方法
 1 class Foo:
 2     @property
 3     def AAA(self):
 4         print('get的時候執行我啊')
 5 
 6     @AAA.setter
 7     def AAA(self,new):
 8         print('set的時候執行我啊')
 9 
10     @AAA.deleter
11     def AAA(self):
12         print('delete的時候執行我啊')
13 
14 #只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter
15 f1=Foo()
16 f1.AAA
17 f1.AAA='aaa'
18 del f1.AAA
property的應用
 1 class Goods:
 2 
 3     def __init__(self):
 4         # 原價
 5         self.original_price = 100
 6         # 折扣
 7         self.discount = 0.8
 8 
 9     @property
10     def price(self):
11         # 實際價格 = 原價 * 折扣
12         new_price = self.original_price * self.discount
13         return new_price
14 
15     @price.setter
16     def price(self, value):
17         self.original_price = value
18 
19     @price.deleter
20     def price(self):
21         del self.original_price
22 
23 
24 obj = Goods()
25 obj.price         # 獲取商品價格
26 obj.price = 200   # 修改商品原價
27 print(obj.price)
28 del obj.price     # 刪除商品原價
2.繼承    # 繼承是一種建立新類的方式,在python中,新建的類可以繼承一個或多個父類    # 父類又可稱為基類或超類,新建的類稱為派生類或子類檢視繼承方法
1     # subclass.__bases__
2     #如果沒有指定基類,python的類會預設繼承object類,object是所有python類的基類,它提供了一些常見方法(如__str__)的實現。
3     #>>> ParentClass1.__bases__
4         (<class 'object'>,)
5         >>> ParentClass2.__bases__
6         (<class 'object'>,)繼承與抽象(先抽象後繼承)
    抽象即抽取類似或者說比較像的部分    繼承:是基於抽象的結果,通過程式語言去實現它,    肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。繼承的分類:1.單繼承    語法 : 子類名(父類名)    繼承與重用        # 子類可以使用父類中的名字(變數和方法)    繼承與派生        # 子類在父類的基礎上又新建立了自己需要的方法和屬性    父類有的子類沒有 - 子類物件直接呼叫 就會直接執行父類的方法    父類有的子類也有 - 子類物件呼叫 直接執行子類中的方法    想在子類中使用父類的名字 : 父類名、super()去呼叫    當子類當中有要被呼叫的方法的時候,子類的物件會直接選擇子類中的方法、變數,父類中的方法不會被自動執行    如果我們既想要執行子類的方法,也想要執行父類的方法,那麼需要在子類的方法中呼叫父類的方法:    1.父類名.方法名(self,...)    2.super().方法名(...)抽象類    抽象類是一個規範,它基本不會實現什麼具體的功能,抽象類是不能被例項化    要想寫一個抽象類        from abc import ABCMeta,abstractmethod        在這個類建立的時候指定 metaclass = ABCMeta        在你希望子類實現的方法上加上一個 @abstractmethod裝飾器    使用抽象類        繼承這個類        在建立新類時,必須實現這個類中被@abstractmethod裝飾器裝飾的方法        如果沒有寫入這個方法則會報錯        TypeError: Can't instantiate abstract class qq with abstract methods pay, shouqian
 1 from abc import ABCMeta,abstractmethod
 2 
 3 class Payment(metaclass=ABCMeta):   # 模板的功能
 4     @abstractmethod     # abstractmethod是一個裝飾器,裝飾器怎麼用?放在函式/類的上一行
 5     def pay(self):pass
 6 
 7     @abstractmethod
 8     def shouqian(self):pass
 9 
10 class Alipay(Payment):
11     def pay(self,money):
12         print('使用支付寶支付了%s元'%money)
13 
14 class Wechatpay(Payment):
15     def pay(self,money):
16         print('使用微信支付了%s元'%money)
17 
18 class ApplePay(Payment):
19     def pay(self,money):
20         print('使用applepay支付了%s元' % money)
21 
22 def pay(obj,money):
23     obj.pay(money)
2.多繼承    不是所有的語言都支援多繼承 java    c++ 支援多繼承
 1 class Animal:
 2     def __init__(self,name):
 3         self.name = name
 4     def talk(self):
 5         print('%s說話了'%self.name)
 6 
 7     def swim(self):
 8         print('%s在游泳'%self.name)
 9 
10     def fly(self):
11         print('%s在飛'%self.name)
12 
13     def walk(self):
14         print('%s在走路'%self.name)
15 class Animal:
16     def __init__(self,name):
17         self.name = name
18 
19 class FlyAnimal(Animal):
20     def fly(self):
21         print('%s在飛' % self.name)
22 class WalkAnimal(Animal):
23     def walk(self):
24         print('%s在走路'%self.name)
25 class SwimAnimal(Animal):
26     def swim(self):
27         print('%s在游泳'%self.name)
28 
29 class Tiger(SwimAnimal,WalkAnimal):
30     pass
31 
32 class Swan(SwimAnimal,WalkAnimal,FlyAnimal):
33     pass
34 
35 class Parrot(FlyAnimal,WalkAnimal):
36     def talk(self):
37         print('%s說話了'%self.name)
38 
39 swan = Swan('天鵝')
40 swan.fly()
41 swan.walk()
繼承原理    python到底是如何實現繼承的,對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,
    這個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'>]    這個MRO列表的構造是通過一個C3線性化演算法來實現的    有父類的MRO列表並遵循如下三條準則:    1.子類會先於父類被檢查    2.多個父類會根據它們在列表中的順序被檢查    3.如果對下一個類存在兩個合法的選擇,選擇第一個父類繼承的作用:    減少程式碼的重用    提高程式碼可讀性    規範程式設計模式抽象中的幾個概念:    抽象:抽象即抽取類似或者說比較像的部分。是一個從具題到抽象的過程。    繼承:子類繼承了父類的方法和屬性    派生:子類在父類方法和屬性的基礎上產生了新的方法和屬性多繼承問題在繼承抽象類的過程中,我們應該儘量避免多繼承;而在繼承介面的時候,我們反而鼓勵你來多繼承介面鑽石繼承新式類:廣度優先經典類:深度優先介面    # java c# 不允許多繼承    # 介面 Interface  介面可以被多繼承    # Interface FlyAnimal:  # 規範繼承我的類必須實現這個方法        # def fly():pass    #java不允許寫任何程式碼    # Interface WalkAnimal:         #def walk():pass    # Interface SwimAnimal:        # def swim():pass    # class Tiger(WalkAnimal,SwimAnimal):  繼承了一個規範    #     def walk():程式碼    #     def swim():程式碼新式類和經典類新式類中    所有的多繼承關係尋找方法的順序  -  遵循廣度優先演算法    繼承object    mro方法    super : super不是單純的找父類,而是遵循mro順序的新式類在python3.x版本中 所有的類都是新式類所有的新式類都有一個預設的父類  : object    #class Person1:pass    # class Person2():pass    # class Person3(object):pass    # # p = Person2()  # __init__ 初始化方法    # print(Person1.__bases__)    # print(Person2.__bases__)    # print(Person3.__bases__)python 2.7經典類 和 新式類 並存class Student:pass # 經典類class Student(object):pass繼承了object的類就是新式類在py3中所有的類都是新式類在py2中既有新式類又有經典類多繼承的順序 在新式類和經典類之間的區別
class A:
    def func(self):
        print('A')

class B(A):
    pass
    # def func(self):
    #     print('B')

class C(A):
    pass
    # def func(self):
    #     print('C')

class D(B,C):
    pass
    # def func(self):
    #     print('D')
print(D.mro())
d = D()
d.func()
新式類中    所有的多繼承關係尋找方法的順序  -  遵循廣度優先演算法    繼承object    mro方法    super : super不是單純的找父類,而是遵循mro順序的經典類python2.x不主動繼承object經典類在找父類中方法的過程中 遵循 —— 深度優先不提供mro方法和super
 1 class A:
 2     def func(self):
 3         print('A')
 4 
 5 class B(A):
 6     def func(self):
 7         super().func()
 8         print('B')
 9 
10 class C(A):
11     def func(self):
12         super().func()
13         print('C')
14 
15 class D(B,C):
16     def func(self):
17         super().func()
18         print('D')
19 
20 D().func()
21 B().func()
經典類    python2.x    不主動繼承object    經典類在找父類中方法的過程中 遵循 —— 深度優先    不提供mro方法和super
3.多型
什麼是多型呢?一個類表現出的多種狀態 : 通過繼承來實現的在java中的表現 : 在一個函式中需要給引數指定資料型別,如果這個地方可以接收兩個以上型別的引數,              那麼這些型別應該有一個父類,這個父類是所有子類物件的型別java中運用多型來解決傳引數的時候 資料型別的規範問題def func(Cat mao):passdef func(Dog gou):passdef func(Animal gou|mao):pass在python中:函式的引數不需要指定資料型別,所以我們也不需要通過繼承的形式來統一一組類的型別,            換句話說 所有的物件其實都是object型別,所以在python當中其實處處是多型鴨子型別def len(obj)len() # str list tuple dict set range(3)print() # 所有的物件都是鴨子型別不是明確的通過繼承實現的多型而是通過一個模糊的概念來判斷這個函式能不能接受這個型別的引數類與物件呼叫的方法的區別類呼叫自己的方法型別是FunctionType,除呼叫類方法型別是MethodType
函式呼叫類方法是MethodType,除呼叫靜態方法時是FunctionType類方法(只使用類中的資源,且這個資源可以直接用類名引用的使用,那這個方法應該被改為一個類方法)類中包含的內容    1.靜態屬性         類  所有的物件都統一擁有的屬性    2.類方法           類  如果這個方法涉及到操作靜態屬性、類方法、靜態方法                            cls 表示類    3.靜態方法         類  普通方法,不使用類中的名稱空間也不使用物件的名稱空間   : 一個普通的函式    沒有預設引數    4.方法            物件                                                                        self 表示物件    5.property方法    物件                                                                        slef 表示物件檢測物件與類之間的關係(isinstance(object,class))判斷這個物件是否是這個類、這個類的子類的物件
1 class A:
2     pass
3 class B(A):
4     pass
5 a = A()
6 b =