程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Python >> python基礎篇【第八篇】面向對象(下)

python基礎篇【第八篇】面向對象(下)

編輯:Python

一、 面向對象特性之多態:

  上一篇已經介紹了面向對象的三大特性的前兩種(封裝、繼承),下面來說說第三種多態,在python中用不上,但在其他語言中很重要。

多態的意思就是多種類型、多種形態,比如字符類型,數字,字典,列表等。在python中定義類或函數時不需要,指定是那種數據類型全都支持,但是在java、c#等其他語言中需要指定如下實例:

def func(A arg)
               print(arg)
          arg :參數必須是A 類型,或A類型的子類

 

二、類成員:

  類成員分為三大類:字段、方法、屬性

 

1、字段

在類中字段分為靜態字段和普通字段

普通字段(動態字段):存儲在對象中,由對象調用

靜態字段:存儲在類中,由類調用,在代碼加載時,就已經創建了

調用規則:

1、一般情況下自己訪問自己;

2、普通字段,只能由對象訪問

3、靜態字段用類訪問,(萬不得已的時候也可以用對象訪問,不建議使用)

 

兩者區別如下:

class Foo:
    CC = 123  # CC是靜態字段,保存在類中

    def __init__(self):
        self.name = "tom"  # name 就是普通字段,保存在對象中

 

實際調用如下:

class Province:
    contry="中國" #靜態字段

    def __init__(self,name):
        self.name=name   #普通字段

sx=Province("河南")
#靜態調用 如 Province.contry
#普通調用 如 sx.name
print(Province.contry,sx.name)

#顯示結果
中國 河南

 

靜態字段存儲在類中, 只在內存中保存一份;

普通字段存儲在每個對象中,需要在每個對象中都保存一份

應用場景:如果創建對象時,都要需要某一個相同的字段,可以把字段設置為靜態字段,節省內存

 

2、方法

方法都屬於類包括:

靜態方法:屬於類,由類來調用執行,無默認參數,等同於函數。創建方式:  方法上邊加個@staticmethod

普通方法:由對象調用,至少需要一個self參數。執行普通方法時,self就是對象本身,自動將調用該方法的對象賦值給self

類方法:是靜態方法的一種特殊形式。由類調用,至少要有一個參數cls,值為類名。創建方式: @classmethod

class Province:
    contry="中國"

    def __init__(self,name):   #普通方法
        self.name=name

    def show(self):
        print("普通方法")

    @classmethod
    def class_show(cls):     #類方法
        print(cls)

    @staticmethod
    def static_show():   #靜態方法
        print("靜態方法")

sx=Province("河南")

#普通方法調用
sx.show()

#類方法調用
Province.class_show()

#靜態方法調用
Province.static_show()



#顯示結果
普通方法
<class '__main__.Province'>    #是這個類名
靜態方法

相同點:由於所有的方法都屬於類, 所以在內存中存儲只保存一份。

不同點:由於各種方法的調用方式不同,調用方法時自動傳入的參數不同。

給上面字段一樣,在對象中也是可以調用靜態方法和類方法的。不到萬不得已還是不要用,要遵循變成原則。

 

3、屬性

屬性就是普通方法的變種

下面來看一下屬性的定義:

class Foo:
    
    def show(self):
        print("普通方法")
      
    @property   #定義屬性
    def prop(self):
        pass

#調用
obj=Foo()
obj.show()   #調用方法
obj.prop    #調用屬性

注意:

定義時,在普通方法的基礎上添加@property裝飾器;僅有一個參數self

調用時,無需加括號

 

還有一種屬性的定義調用方法:

class Foo():

    @property
    def price(self):
        print('查詢')

    @price.setter
    def price(self, value):
        print('設置')

    @price.deleter
    def price(self):
        print('刪除')

# ############### 調用 ###############
obj = Foo()

obj.price          # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值

obj.price = 123    # 自動執行 @price.setter 修飾的 price 方法,並將  123 賦值給方法的參數

del obj.price      # 自動執行 @price.deleter 修飾的 price 方法

#執行結果
查詢
設置
刪除

 

三、類成員修飾符

公有成員:就是在哪都能訪問,

私有成員,只有在類的內部才能放問,其他都不能訪問,繼承關系也不能

定義私有成員:命名是前面是兩個下劃線,(特殊成員除外,例如:__init__、__call__、__dict__等)

如下:

class Foo:
    def __init__(self):
        self.name="公有字段"
        self.__fuck="私有字段"

 

四、類的特殊成員

1、__init__

構造方法,通過類創建對象時,自動觸發

class Foo:

    def __init__(self,name):
        self.name = name # name 就是普通字段
        self.job="IT"

obj=Foo("tom")   #自動執行類中的__init__方法
print(obj.name)
print(obj.job)

#結果
tom
IT

2、__doc__

  表示類的描述信息,就是注釋

class Foo:
    '''
    描述信息
    '''
    def __init__(self,name):
        self.name = name # name 就是普通字段
        self.job="IT"
print(Foo.__doc__)

#顯示結果
   描述信息
    
View Code

3、__del__

  析構方法,用於對象在內存中垃圾回收時,自動觸發執行

4、__call__

  在對象後面加括號,執行

對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

class Foo:

    def __init__(self,name):
        self.name = name # name 就是普通字段

    def __call__(self, *args, **kwargs):
        print("執行call方法")
obj=Foo("test")  #執行__init__方法
obj()   #執行__call__方法
View Code

5、__module__、__class__

  __module__表示當前操作的對象在那個模塊

  __class__ 表示當前操作的對象的類是誰

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

class Foo:
    '''
    這是類的描述信息, 由特殊成員 __doc__調用
    '''
    def __init__(self,name):
        self.name = name
        # print(self.name)

    def show_info(self):
        print(self.name)

bin/modules.py內容
bin/modules.py
from bin import modules

obj = modules.Foo('DBQ')
print(obj.__module__)   # 查看當前操作的對象屬於哪個模塊

print(obj.__class__)       #查看當前操作的對象的類是哪個


#執行結果:
bin.modules
<class 'bin.modules.F1'>
View Code

6、__dict__

  獲取對象或類中的所有成員

class Foo:
    CC="test"
    def __init__(self,name):
        self.name = name # name 就是普通字段


#獲取類中的所有成員
print(Foo.__dict__)

#獲取對象中的成員
obj=Foo("tom")
print(obj.__dict__)

#顯示結果
{'__weakref__': <attribute '__weakref__' of 'Foo' objects>, 'CC': 'test', '__doc__': None, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__init__': <function Foo.__init__ at 0x00000041DAED61E0>}
{'name': 'tom'}
View Code

7、__str__

  如果一個類中定義了__str__方法,那麼打印對象時,默認輸出該方法的返回值

class Foo:
    CC="test"
    def __init__(self,name):
        self.name = name # name 就是普通字段

    def __str__(self):
        return "hi tom"

obj=Foo("tom")
print(obj)

#結果
hi tom
View Code

8、__iter__

  用於迭代器,之所以列表、字典、元組可以進行for循環,是因為類型內部定義了__iter__

class Foo():

    def __init__(self, sq):
        self.sq = sq

    def __iter__(self):
        return iter(self.sq)

obj = Foo([11,22,33,44])

for i in obj:
    print(i)
View Code

9、__getitem__、__setitem__、__deltiem__

用於索引操作, 如字典。 分別表示獲取、設置、刪除數據。 

class Foo():
    def __getitem__(self, item):
        print("執行get操作,調用__getitem__方法")
    def __setitem__(self, key, value):
        print("執行set操作,調用__setitem__方法")
    def __delitem__(self, key):
        print("執行del操作,調用__delitem__方法")

obj=Foo()
obj['a1']   #get操作,自動觸發__getitem__方法
obj["a1"]=[1,2,3,4,5] #set操作,自動調用__setitem__方法"
del obj['a1']   #del操作,自動調用__delitem__方法

#結果
執行get操作,調用__getitem__方法
執行set操作,調用__setitem__方法
執行del操作,調用__delitem__方法
View Code

10、__getslice__、__setslice__、__delslice__

  用於分片操作

class Foo(object):
 
    def __getslice__(self, i, j):
        print '__getslice__',i,j
 
    def __setslice__(self, i, j, sequence):
        print '__setslice__',i,j
 
    def __delslice__(self, i, j):
        print '__delslice__',i,j
 
obj = Foo()
 
obj[-1:1]                   # 自動觸發執行 __getslice__
obj[0:1] = [11,22,33,44]    # 自動觸發執行 __setslice__
del obj[0:2]                # 自動觸發執行 __delslice__
python2.7執行

在python3中還是運用的__getitem__、__setitem__、__deltiem__

class Foo():

    def __getslice__(self, i, j):
        print('__getslice__',i,j)

    def __setslice__(self, i, j, sequence):
        print('__setslice__',i,j)

    def __delslice__(self, i, j):
        print('__delslice__',i,j)

    def __getitem__(self, item):
        print("執行get操作,調用__getitem__方法")
    def __setitem__(self, key, value):
        print("執行set操作,調用__setitem__方法")
    def __delitem__(self, key):
        print("執行del操作,調用__delitem__方法")
obj = Foo()

obj[-1:1]                   # 自動觸發執行 __getslice__
obj[0:1] = [11,22,33,44]    # 自動觸發執行 __setslice__
del obj[0:2]                # 自動觸發執行 __delslice__

#結果
執行get操作,調用__getitem__方法
執行set操作,調用__setitem__方法
執行del操作,調用__delitem__方法
python3中

11. __new__ 、 __metaclass__

class F1:
    def __init__(self,name):
        self.name = name

    def show_info(self):
        print(self.name)

obj = F1('tom')

print(type(obj))

print(type(F1))

# #執行代碼結果:
# <class '__main__.F1'>      #表示 obj對象由 F1類實例化而來
# <class 'type'>                  #表示 F1類由 type 類創建

python中一切介對象,上述代碼中obj是F1的一個對象,那麼可以推理F1其實也是一個對象

所以,obj對象時F1類的一個實例,F1類對象時type類的一個實例,F1類對象時通過type類構造方法創建

創建方法有兩種普通方法、特殊方法:

普通方法:

class F1:
    def __init__(self,name):
        self.name = name

    def show_info(self):
        print(self.name)

特殊方法:

def show_info(self):
    print('tom')

F1 = type('F1',(object,),{'show_info':show_info})
#第一個參數: 類名
#第二個參數: 當前類的基類
#第三個參數: 類成員

obj = F1()
obj.show_info()

==》 類 是由 type 類實例化產生

那麼問題來了,類默認是由 type 類實例化產生,type類中如何實現的創建類?類又是如何創建對象?

答:類中有一個屬性 __metaclass__,其用來表示該類由 誰 來實例化創建,所以,我們可以為 __metaclass__ 設置一個type類的派生類,從而查看 類 創建的過程。

 

 五、面向對象其他相關知識

  1、isinstance(obj,cls)

 檢查是否obj是否是類 cls 的對象

判斷一個對象是不是類創建的()實例,返回布爾值,繼承的父類也為真

class F1:
    pass

class F2(F1):
    pass

obj = F2()

print(isinstance(obj,F1))    #查看是否是父類的實例, 為真
print(isinstance(obj,F2))    #查看是否是F2類的實例, 為真

 

2、issubclass(F1,F2)

檢查F1是否是F2的子類

查看是否是某類的子類

class F1:
    pass

class F2(F1):
    pass

obj = F2()

print(issubclass(F2,F1))   #查看F2是否是F1的子類, 為真
print(issubclass(F1,F2))   #查看F1是否是F2的子類, 為假

 

3、 super 

擴展別人的源碼 ,盡量不在源碼中修改

class C1:
    def f1(self):
        print('C1.f1')

class C2(C1):
    def f1(self):
        super(C2,self).f1()   #在執行C2代碼之前,執行C1中的f1方法 也就是C2父類的f1方法
        print('C2.f1')

obj = C2()
obj.f1()

#結果
C1.f1
C2.f1

 

五、異常處理與捕獲

1、異常處理基礎

增加友好性,在程序出現bug中一般不會將錯誤信息顯示給用戶,而是顯示一個頁面

while True:

    num = input('請輸入你一個或多個整數: ').strip()

    try:
        num = int(num)
        print('你輸入的數字是: %d'%num)
    except Exception:
        print('%s, 你輸入的不是一個整數格式!'%Exception)


# 如果輸入的是一個整數類型,將返回輸入的號碼
# 如果輸入的是其他的類型,如字符串、浮點數等,會提示用戶輸入的不是一個整數格式!

####執行結果:
請輸入你一個或多個整數: 123
你輸入的數字是: 123
請輸入你一個或多個整數: a
<class 'Exception'>, 你輸入的不是一個整數格式!
請輸入你一個或多個整數: 1.
<class 'Exception'>, 你輸入

 

2.異常處理

捕獲異常可以使用 try / except語句。try: 用來檢測語句塊中的錯誤,從而讓 except中語句捕獲的異常信息並處理。

打開一個文件,往文件中寫入內容,並且沒有發生異常:

try:
    f = open('test.txt','w')
    f.write('測試文件,用於測試異常捕獲')
except IOError:
    print('Error: 寫入失敗, 沒有找到文件或者權限不足!')
else:
    print('寫入成功!')
    f.close()

#執行結果:
寫入成功!

#文件內容:
Daniel-Mac:blog daniel$ cat test.txt &&echo
測試文件,用於測試異常捕獲

修改文件的權限沒有寫,而後在打開文件,往文件中寫入內容,查看異常:

chmod -w test.txt 
try:
    f = open('test.txt','w')
    f.write('測試文件,用於測試異常捕獲')
except IOError:
    print('Error: 寫入失敗, 沒有找到文件或者權限不足!')
else:
    print('寫入成功!')
    f.close()

#再次執行代碼:
Error: 寫入失敗, 沒有找到文件或者權限不足!

 常用異常:

AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x
IOError 輸入/輸出異常;基本上是無法打開文件
ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
KeyError 試圖訪問字典裡不存在的鍵
KeyboardInterrupt Ctrl+C被按下
NameError 使用一個還未被賦予對象的變量
SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了)
TypeError 傳入對象類型與要求的不符合
UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量,
導致你以為正在訪問它
ValueError 傳入一個調用者不期望的值,即使值的類型是正確的


實例:

下標異常

dic = ["tom", 'jerry']
try:
    dic[10]
except IndexError, e:    
    print e


key異常

dic = {'k1':'v1'}
try:
    dic['k20']
except KeyError, e:
    print e

 

元素異常

s1 = 'hello'
try:
    int(s1)
except ValueError, e:
    print e

 

對於上述實例,異常類只能用來處理指定的異常情況,如果沒有指定異常則無法處理。

# 未捕獲到異常,程序直接報錯
 
tt = 234
try:
    str(tt)
except IndexError,e:
    print e

 

如果想通吃各種異常,python中也提供了一個萬能異常Exception,就能捕獲任意異常,目的是保證程序能正常運行

tt = 234
try:
    str(tt)
except Exception,e:
    print e

 

如果你還想要知道異常是什麼,在那一塊報錯了,還有一個更周全的方案,如下:

s1 = 'hello world'
try:
    int(s1)
except KeyError:
    print('Error: Key錯誤!')
except IndexError:
    print('Error: 索引錯誤!')
except ValueError:
    print('Error: 值錯誤!')
except Exception:
    print('Error: 出錯了!')
else:
    print('你的值是: %s'%s1)

 

六、設計模式,單實例

例模式,顧名思義,也就是單個實例的意思。

模式特點:保證類僅有一個實例,並提供一個訪問它的全局訪問點。

class Singleton:
    __instance = None   #定義一個私有靜態字段為初始值

    def __init__(self,name):
        self.name = name

    def show(self):
        print(self.name)
        return 'test_instance'

    @classmethod
    def get_instance(cls):
        if cls.__instance:  #如果字段內有值,直接返回字段值
            return cls.__instance
        else:                     
            obj = cls('DBQ')          #實例化
            cls.__instance = obj    #將對象賦值給字段
            return cls.__instance   #返回對象


a = Singleton.get_instance()
b = Singleton.get_instance()

print(a)
print(id(a))   #內存地址和b相同
print()
print(b)
print(id(b))    #內存地址和a相同
# 後面再來幾個對象,也是一樣的!

#代碼執行結果:
<__main__.Singleton object at 0x101b769b0>

<__main__.Singleton object at 0x101b769b0>
單實例

單例模式的存在主要是保證當前內存中存在單個實例,避免內存資源浪費。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved