Python(六)面向對象、異常處理、反射、單例模式

本章內容:python

  • 建立類和對象
  • 面向對象三大特性(封裝、繼承、多態)
  • 類的成員(字段、方法、屬性)
  • 類成員的修飾符(公有、私有)
  • 類的特殊成員
  • isinstance(obj, cls) & issubclass(sub, super)
  • 異常處理
  • 反射
  • 單例模式
建立類和對象

面向對象編程是一種編程方式,此編程方式的落地須要使用 「類」 和 「對象」 來實現,因此,面向對象編程其實就是對 「類」 和 「對象」 的使用。程序員

  類就是一個模板,模板裏能夠包含多個函數,函數裏實現一些功能編程

  對象則是根據模板建立的實例,經過實例對象能夠執行類中的函數併發

  • class是關鍵字,表示類
  • 建立對象,類名稱後加括號便可
# 建立類
class Foo:
     
    def buy(self):
        print("This is buy.")
 
    def Hello(self, name):
        print("This is hello.")
 
# 根據類Foo建立對象obj
obj = Foo()
obj.buy()            #執行Bar方法
obj.Hello('nick')    #執行Hello方法

類和對象在內存中是如何保存的?ide

類以及類中的方法在內存中只有一份,而根據類建立的每個對象都在內存中須要存一份,大體以下圖:模塊化

 

如上圖所示,根據類建立對象時,對象中除了封裝 name 和 age 的值以外,還會保存一個類對象指針,該值指向當前對象的類。函數式編程

當經過 obj1 執行方法時,過程以下:函數

  1. 根據當前對象中的 類對象指針 找到類中的方法
  2. 將對象 obj1 看成參數傳給 方法的第一個參數 self 

注:Java和C#來講只支持面向對象編程,而python比較靈活即支持面向對象編程也支持函數式編程fetch

面向對象三大特性

面向對象的三大特性是指:封裝、繼承和多態。url

1、封裝

封裝,顧名思義就是將內容封裝到某個地方,之後再去調用被封裝在某處的內容。

因此,在使用面向對象的封裝特性時,須要:

  • 將內容封裝到某處
  • 從某處調用被封裝的內容

一、將內容封裝到某處

self 是一個形式參數,當執行 obj = Foo('nick', 18 ) 時,self 等於 obj

                     當執行 obj2 = Foo('jenny', 21 ) 時,self 等於 obj2

二、從某處調用被封裝的內容

調用被封裝的內容時,有兩種狀況:

  • 經過對象直接調用
  • 經過self間接調用
class Foo:
 
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
obj = Foo('nick', 18)
print obj.name    # 直接調用obj對象的name屬性
print obj.age     # 直接調用obj對象的age屬性
 
obj2 = Foo('jenny', 21)
print obj2.name    # 直接調用obj2對象的name屬性
print obj2.age     # 直接調用obj2對象的age屬性
class Foo:
  
    def __init__(self, name, age):
        self.name = name
        self.age = age
  
    def detail(self):
        print self.name
        print self.age
  
obj = Foo('nick', 18)
obj.detail()  # Python默認會將obj傳給self參數,即:obj.detail(obj),因此,此時方法內部的 self = obj,即:self.name 是 nick ;self.age 是 18
  
obj2 = Foo('jenny', 21)
obj2.detail()  # Python默認會將obj2傳給self參數,即:obj1.detail(obj2),因此,此時方法內部的 self = obj2,即:self.name 是 jenny ; self.age 是 21
#封裝
#非主流方式
class Foo:

    def fetch(self):
        print(self.nick)

    def add(self):
        print(self.jenny)

obj = Foo()
obj.nick = "Nick_cool"
obj.fetch()

# obj2 = Foo()
obj.nick = "Nick_cool_2"
obj.fetch()

obj1 = Foo()
obj1.jenny = "Jenny_nice"
obj1.add()


#封裝

class Foo:

    def __init__(self,bk):
        """ 構造方法 """          #析構方法在垃圾回收是解釋器本身調用
        self.name = bk
        self.job = "pythoner"   # obj.job = "pythoner"
        self.age = 18           # obj.age = 18

    def fetch(self):
        print(self.name)
        print(self.age)
        print(self.job)

obj = Foo("nick")
obj.fetch()

綜上所述,對於面向對象的封裝來講,其實就是使用構造方法將內容封裝到 對象 中,而後經過對象直接或者self間接獲取被封裝的內容。

2、繼承 

對於面向對象的繼承來講,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而沒必要一一實現每一個方法。

注:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類只是叫法不一樣而已。

# 繼承

# 基類
class Animals:

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

    def eat(self):
        print(self.name,"吃")


# 派生類
class dog(Animals):

    def tell(self):
        print("汪星人")

dog = dog("啊黃")
dog.tell()
dog.eat()

 

繼承 __init__

派生類默認不繼承基類__init__,須要用super聲明

class A:
    def __init__(self):
        self.name = "nick"

class B(A):
    def __init__(self):
        self.age = 18
        super(B, self).__init__()   #super首先找到B的父類A,而後把類B的對象self轉換爲類A的對象,而後「被轉換」的類A對象調用本身的__init__函數
        # A.__init__(self)          #指定運行A中__init__,不推薦

obj = B()
print(obj.__dict__)

 

多繼承:

Python的類能夠繼承多個類,Java和C#中則只能繼承一個類

Python3的類繼承多個類的尋找方法的方式,Python 3中沒有經典類、新式類之分

# 多繼承

class A:
    def f1(self):
        print("A")

class B(A):
    def f(self):
        print("B")

class C(A):
    def f(self):
        print("C")

class D(B):
    def f(self):
        print("D")

class E(C):
    def f1(self):
        print("E")

class F(D,E):
    def f(self):
        print("F")

f1 = F()
f1.f1()

Python2的類若是繼承了多個類,那麼其尋找方法的方式有兩種,分別是:深度優先廣度優先

  • 當類是經典類時,多繼承狀況下,會按照深度優先方式查找
  • 當類是新式類時,多繼承狀況下,會按照廣度優先方式查找

經典類和新式類,從字面上能夠看出一個老一個新,新的必然包含了跟多的功能,也是以後推薦的寫法,從寫法上區分的話,若是 當前類或者父類繼承了object類,那麼該類即是新式類,不然即是經典類。

 

class D:

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 執行bar方法時
# 首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去D類中找,若是D類中麼有,則繼續去C類中找,若是仍是未找到,則報錯
# 因此,查找順序:A --> B --> D --> C
# 在上述查找bar方法的過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了
a.bar()
經典類多繼承
class D(object):

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 執行bar方法時
# 首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去C類中找,若是C類中麼有,則繼續去D類中找,若是仍是未找到,則報錯
# 因此,查找順序:A --> B --> C --> D
# 在上述查找bar方法的過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了
a.bar()
新式類多繼承

經典類:首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去D類中找,若是D類中麼有,則繼續去C類中找,若是仍是未找到,則報錯

新式類:首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中麼有,則繼續去C類中找,若是C類中麼有,則繼續去D類中找,若是仍是未找到,則報錯

注意:在上述查找過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了

 

函數方法裏調用函數方法執行順序

# 函數方法裏調用函數方法執行順序

class D:

    def buy(self):
        self.f1()       #調用 f1()

    def f1(self):
        print("This is D f1.")

class C(D):

    def f1(self):
        print("This is C f1.")

class B:

    def f1(self):
        print("This is B f1.")

class A(B, C):
    pass

obj = A()
obj.buy()

 

3、多態

  多態性(polymorphisn)是容許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值以後,父對象就能夠根據當前賦值給它的子對象的特性以不一樣的方式運做。簡單的說,就是一句話:容許將子類類型的指針賦值給父類類型的指針。
  那麼,多態的做用是什麼呢?咱們知道,封裝能夠隱藏實現細節,使得代碼模塊化;繼承能夠擴展已存在的代碼模塊(類);它們的目的都是爲了——代碼重用。而多態則是爲了實現另外一個目的——接口重用!多態的做用,就是爲了類在繼承和派生的時候,保證使用「家譜」中任一類的實例的某一屬性時的正確調用。
  Pyhon不支持多態而且也用不到多態,多態的概念是應用於Java和C#這一類強類型語言中,而Python崇尚「鴨子類型」。
class F1:
    pass


class S1(F1):

    def show(self):
        print 'S1.show'


class S2(F1):

    def show(self):
        print 'S2.show'


# 因爲在Java或C#中定義函數參數時,必須指定參數的類型
# 爲了讓Func函數既能夠執行S1對象的show方法,又能夠執行S2對象的show方法,因此,定義了一個S1和S2類的父類
# 而實際傳入的參數是:S1對象和S2對象

def Func(F1 obj):
    """Func函數須要接收一個F1類型或者F1子類的類型"""
    
    print obj.show()
    
s1_obj = S1()
Func(s1_obj) # 在Func函數中傳入S1類的對象 s1_obj,執行 S1 的show方法,結果:S1.show

s2_obj = S2()
Func(s2_obj) # 在Func函數中傳入Ss類的對象 ss_obj,執行 Ss 的show方法,結果:S2.show
Python僞代碼實現Java或C#的多態
class F1:
    pass


class S1(F1):

    def show(self):
        print 'S1.show'


class S2(F1):

    def show(self):
        print 'S2.show'

def Func(obj):
    print obj.show()

s1_obj = S1()
Func(s1_obj) 

s2_obj = S2()
Func(s2_obj) 
Python 「鴨子類型」
class Animal:
    def __init__(self, name):    # Constructor of the class
        self.name = name
    def talk(self):              # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")
 
class Cat(Animal):
    def talk(self):
        return 'Meow!'
 
class Dog(Animal):
    def talk(self):
        return 'Woof! Woof!'
 
animals = [Cat('Missy'),
           Dog('Lassie')]
 
for animal in animals:
    print animal.name + ': ' + animal.talk()
經過Python模擬的多態

 

類的方法

類的成員能夠分爲三大類:字段、方法和屬性。

注:全部成員中,只有普通字段的內容保存對象中,即:根據此類建立了多少對象,在內存中就有多少個普通字段。而其餘的成員,則都是保存在類中,即:不管對象的多少,在內存中只建立一份。

 

 1、字段

字段包括:普通字段和靜態字段,他們在定義和使用中有所區別,而最本質的區別是內存中保存的位置不一樣,

  • 普通字段屬於對象
  • 靜態字段屬於
class Foo:
     
    # 靜態字段
    country = "China"

    def __init__(self, name):

        # 普通字段
        self.name = name

# 直接訪問靜態字段
Foo.country

# 直接訪問普通字段
obj = Foo("山西")

由上述代碼能夠看出【普通字段須要經過對象來訪問】【靜態字段經過類訪問】,在使用上能夠看出普通字段和靜態字段的歸屬是不一樣的。 

  • 靜態字段在內存中只保存一份
  • 普通字段在每一個對象中都要保存一份

應用場景: 經過類建立對象時,若是每一個對象都具備相同的字段,那麼就使用靜態字段

 

2、方法

方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不一樣。

  • 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self
  • 類方法:由調用; 至少一個cls參數;執行類方法時,自動將調用該方法的複製給cls
  • 靜態方法:由調用;無默認參數;
class Foo:


    #靜態方法
    @staticmethod
    def xo(arg1, arg2):     #無默認參數,可不傳參數,可傳任意參數
        print("xo")


    #類方法
    @classmethod
    def xxoo(cls):             #定義類方法,至少有一個cls參數
        print(cls)


    #普通方法,類中
    def show(self):           #定義普通方法,至少有一個self參數
        print("show")


# 調用靜態方法
Foo.xo(1,2)

# 調用類方法
Foo.xxoo()

# 調用普通方法
obj = Foo:
obj.show()

相同點:對於全部的方法而言,均屬於類(非對象)中,因此,在內存中也只保存一份。

不一樣點:方法調用者不一樣、調用方法時自動傳入的參數不一樣。

 

3、屬性

屬性的基本使用

由屬性的定義和調用要注意一下幾點:

  • 定義時,在普通方法的基礎上添加 @property 裝飾器;
  • 定義時,屬性僅有一個self參數
  • 調用時,無需括號
               方法:foo_obj.func()
               屬性:foo_obj.prop

注意:屬性存在乎義是:訪問屬性時能夠製造出和訪問字段徹底相同的假象

      屬性由方法變種而來,若是Python中沒有屬性,方法徹底能夠代替其功能。

class Foo:

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

    # 屬性,將方法僞形成一種字段
    @property
    def end(self):
        return self.name

    # 修改end值
    @end.setter
    def end(self, new_name):
        self.name = new_name


obj = Foo("nick")

# 調用屬性,不須要加括號
result2 = obj.end
print(result2)

# 調用修改end.setter屬性(自動將jenny傳入當參數new_name)
obj.end = "jenny"

result3 = obj.end
print(result3)

 

屬性的兩種定義方式 

屬性的定義有兩種方式:

  • 裝飾器 即:在方法上應用裝飾器
  • 靜態字段 即:在類中定義值爲property對象的靜態字段

裝飾器方式:在類的普通方法上應用@property裝飾器

咱們知道Python中的類有經典類和新式類,新式類的屬性比經典類的屬性豐富。( 若是類繼object,那麼該類是新式類 )
經典類,具備一種@property裝飾器

# ############### 定義 ###############    
class Goods:

    @property
    def price(self):
        return "nick"
# ############### 調用 ###############
obj = Goods()
result = obj.price  # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
View Code

新式類,具備三種@property裝飾器

# ############### 定義 ###############
class Goods(object):

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

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

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

# ############### 調用 ###############
obj = Goods()

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

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

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

注:經典類中的屬性只有一種訪問方式,其對應被 @property 修飾的方法
      新式類中的屬性有三種訪問方式,並分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法

因爲新式類中具備三種訪問方式,咱們能夠根據他們幾個屬性的訪問特色,分別將三個方法定義爲對同一個屬性:獲取、修改、刪除

class Goods(object):

    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deltter
    def price(self, value):
        del self.original_price

obj = Goods()
obj.price         # 獲取商品價格
obj.price = 200   # 修改商品原價
del obj.price     # 刪除商品原價
實例

 

 

 

 

 

 

 

 

 

 

 

 

 

靜態字段方式,建立值爲property對象的靜態字段

因此,定義屬性共有兩種方式,分別是【裝飾器】和【靜態字段】,而【裝飾器】方式針對經典類和新式類又有所不一樣。

當使用靜態字段的方式建立屬性時,經典類和新式類無區別

class Foo:

    def get_bar(self):
        return 'nick'

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR        # 自動調用get_bar方法,並獲取方法的返回值
print reuslt
View Code

property的構造方法中有個四個參數

  • 第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
  • 第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
  • 第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法
  • 第四個參數是字符串,調用 對象.屬性.__doc__ ,此參數是該屬性的描述信息
class Foo:

    def get_bar(self):
        return 'nick'

    # *必須兩個參數
    def set_bar(self, value): 
        return 'set value' + value

    def del_bar(self):
        return 'nick'

    BAR = property(get_bar, set_bar, del_bar, 'description...')

obj = Foo()

obj.BAR              # 自動調用第一個參數中定義的方法:get_bar
obj.BAR = "jenny"     # 自動調用第二個參數中定義的方法:set_bar方法,並將「jenny」看成參數傳入
del Foo.BAR          # 自動調用第三個參數中定義的方法:del_bar方法
obj.BAR.__doc__      # 自動獲取第四個參數中設置的值:description...
View Code

因爲靜態字段方式建立屬性具備三種訪問方式,咱們能夠根據他們幾個屬性的訪問特色,分別將三個方法定義爲對同一個屬性:獲取、修改、刪除

class Goods(object):

    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    def get_price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self, value):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '價格屬性描述...')

obj = Goods()
obj.PRICE         # 獲取商品價格
obj.PRICE = 200   # 修改商品原價
del obj.PRICE     # 刪除商品原價
實例

 

 

類成員的修飾符

每個類的成員都有兩種形式:

  • 公有成員,在任何地方都能訪問
  • 私有成員,只有在類的內部才能方法

 私有成員和公有成員的定義不一樣:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:__init__、__call__、__dict__等)

class Foo:

    xo = "xo"         #公有字段
    __ox = "ox"     #私有字段

    def __init__(self):
        self.name = "nick"         #公有字段
        self.__name2 = "nick"   #私有字段

私有成員和公有成員的訪問限制不一樣

靜態字段

  • 公有靜態字段:類能夠訪問;類內部能夠訪問;派生類中能夠訪問
  • 私有靜態字段:僅類內部能夠訪問;
class C:

    name = "公有靜態字段"

    def func(self):
        print C.name

class D(C):

    def show(self):
        print C.name


C.name         # 類訪問

obj = C()
obj.func()     # 類內部能夠訪問

obj_son = D()
obj_son.show() # 派生類中能夠訪問
公有靜態字段
class C:

    __name = "公有靜態字段"

    def func(self):
        print C.__name

class D(C):

    def show(self):
        print C.__name


C.__name       # 類訪問            ==> 錯誤

obj = C()
obj.func()     # 類內部能夠訪問     ==> 正確

obj_son = D()
obj_son.show() # 派生類中能夠訪問   ==> 錯誤
私有靜態字段

普通字段

  • 公有普通字段:對象能夠訪問;類內部能夠訪問;派生類中能夠訪問;
  • 私有普通字段:僅類內部能夠訪問;
class C:
    
    def __init__(self):
        self.foo = "公有字段"

    def func(self):
        print self.foo  # 類內部訪問

class D(C):
    
    def show(self):
        print self.foo # 派生類中訪問

obj = C()

obj.foo     # 經過對象訪問
obj.func()  # 類內部訪問

obj_son = D();
obj_son.show()  # 派生類中訪問
公有字段
class C:
    
    def __init__(self):
        self.__foo = "私有字段"

    def func(self):
        print self.foo  # 類內部訪問

class D(C):
    
    def show(self):
        print self.foo # 派生類中訪問

obj = C()

obj.__foo     # 經過對象訪問    ==> 錯誤
obj.func()  # 類內部訪問        ==> 正確

obj_son = D();
obj_son.show()  # 派生類中訪問  ==> 錯誤
私有字段

方法、屬性的訪問於上述方式類似,即:私有成員只能在類內部使用

ps:若是想要強制訪問私有字段,能夠經過 【對象._類名__私有字段明 】訪問(如:obj._C__foo),不建議強制訪問私有成員。

 

類的特殊成員

成員名前若是有兩個下劃線,則表示該成員是私有成員,私有成員只能由類內部調用。

1. __doc__

表示類的描述信息

class Foo:
    """ 描述類信息 """

    def func(self):
        pass

print Foo.__doc__
#輸出:類的描述信息
__doc__

2. __module__ 和  __class__

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

  __class__     表示當前操做的對象的類是什麼

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

class C:

    def __init__(self):
        self.name = 'nick'
lib/aa.py
from lib.aa import C

obj = C()
print obj.__module__  # 輸出 lib.aa,即:輸出模塊
print obj.__class__      # 輸出 lib.aa.C,即:輸出類
index.py

3. __init__

構造方法,經過類建立對象時,自動觸發執行。

class Foo:

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


obj = Foo('nick')     # 自動執行類中的 __init__ 方法
__init__

4. __del__

析構方法,當對象在內存中被釋放時,自動觸發執行。

注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。

class Foo:

    def __del__(self):
        pass
__del__

5. __call__

對象後面加括號,觸發執行。

注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

# __call__
class Foo:

    def __init__(self):
        print("This is init")

    def __call__(self, *args, **kwargs):
        print("This is call")
        return "CC"

obj = Foo()    # 執行 __init__
obj()              # 執行 __call__

result = Foo()()   # 執行 __call__
print(result)
__call__

6. __dict__

類或對象中的全部成員

class Province:

    country = 'China'

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

    def func(self, *args, **kwargs):
        print 'func'

# 獲取類的成員,即:靜態字段、方法、
print Province.__dict__
# 輸出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}

obj1 = Province('shangxi',10000)
print obj1.__dict__
# 獲取 對象obj1 的成員
# 輸出:{'count': 10000, 'name': 'shangxi'}

obj2 = Province('shangdong', 3888)
print obj2.__dict__
# 獲取 對象obj1 的成員
# 輸出:{'count': 3888, 'name': 'shangdong'}
__dict__

 7. __str__

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

class Foo:

    def __str__(self):
        return 'nick'


obj = Foo()
print obj
# 輸出:nick
__str__

八、__getitem__、__setitem__、__delitem__

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

class Foo:

    def __getitem__(self, item):
        print(item)

    def __setitem__(self, key, value):
        print(key, value)

    def __delitem__(self, key):
        print(key)

obj = Foo()

obj["nick"]                     # 自動觸發執行 __getitem__
obj["nick"] = "jenny"     # 自動觸發執行 __setitem__
del obj["nick"]                # 自動觸發執行 __delitem__
__getitem____setitem____delitem__

九、__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__
__getslice____setslice____delslice__

10. __iter__

用於迭代器,之因此列表、字典、元組能夠進行for循環,是由於類型內部定義了 __iter__ 

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

class Foo(object):

    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
__iter__
#!/usr/bin/env python
# -*- coding:utf-8 -*-

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

while True:
    val = obj.next()
    print val
For循環語法內部

11. __mro__ 和__slot__

在解析父類的__init__時,定義解析順序的是子類的__mro__屬性,內容爲一個存儲要解析類順序的元組

 

class A(object):
    def __init__(self):
        print '    -> Enter A'
        print '    <- Leave A'

class B(A):
    def __init(self):
        print '    -> Enter B'
        # A.__init__(self)
        super(B, self).__init__()
        print '    <- Leave B'

class C(A):
    def __init__(self):
        print "    -> Enter C"
        # A.__init__(self)
        super(C, self).__init__()
        print "    <- Leave C"

class D(B, C):
    def __init__(self):
        print "    -> Enter D"
        # B.__init__(self)
        # C.__init__(self)
        super(D, self).__init__()
        print "    <- Leave D"

if __name__ == "__main__":
    d = D()
    print "MRO:", [x.__name__ for x in D.__mro__]
    print type(D.__mro__)


執行以上代碼,獲得的輸出爲:

-> Enter D
-> Enter C
-> Enter A
<- Leave A
<- Leave C
<- Leave D
MRO: ['D', 'B', 'C', 'A', 'object']
<type 'tuple'>
View Code

 

__slot__定義類中能夠被外界訪問的屬性。

當父類中定義了__slot__時,不能向父類中添加屬性。若是子類中沒有定義__slot__,則子類不受父類__slot__定義的限制。

若是父類與子類中都定義了__slot__,則郵箱的結果爲父類與子類__slot__的合集。

 

12. __new__ 和 __metaclass__ 

閱讀如下代碼:

class Foo(object):
 
    def __init__(self):
        pass
 
obj = Foo()   # obj是經過Foo類實例化的對象

上述代碼中,obj 是經過 Foo 類實例化的對象,其實,不只 obj 是一個對象,Foo類自己也是一個對象,由於在Python中一切事物都是對象

若是按照一切事物都是對象的理論:obj對象是經過執行Foo類的構造方法建立,那麼Foo類對象應該也是經過執行某個類的 構造方法 建立。

print type(obj) # 輸出:<class '__main__.Foo'>     表示,obj 對象由Foo類建立
print type(Foo) # 輸出:<type 'type'>              表示,Foo類對象由 type 類建立

因此,obj對象是Foo類的一個實例Foo類對象是 type 類的一個實例,即:Foo類對象 是經過type類的構造方法建立。

那麼,建立類就能夠有兩種方式:

1> 普通方式

class Foo(object):
 
    def func(self):
        print 'hello word'

2> 特殊方式(type類的構造函數)

def func(self):
    print 'hello word'
 
Foo = type('Foo',(object,), {'func': func})
#type第一個參數:類名
#type第二個參數:當前類的基類
#type第三個參數:類的成員

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

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

答:類中有一個屬性 __metaclass__,其用來表示該類由 誰 來實例化建立,因此,咱們能夠爲 __metaclass__ 設置一個type類的派生類,從而查看 類 建立的過程。

class MyType(type):

    def __init__(self, what, bases=None, dict=None):
        super(MyType, self).__init__(what, bases, dict)

    def __call__(self, *args, **kwargs):
        obj = self.__new__(self, *args, **kwargs)

        self.__init__(obj)

class Foo(object):

    __metaclass__ = MyType

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

    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)

# 第一階段:解釋器從上到下執行代碼建立Foo類
# 第二階段:經過Foo類建立obj對象
obj = Foo()
View Code

 

isinstance(obj, cls) & issubclass(sub, super)

isinstance(obj, cls)

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

class Foo(object):
    pass
 
obj = Foo()
 
isinstance(obj, Foo)

issubclass(sub, super)

 檢查sub類是不是 super 類的派生類

class Foo(object):
    pass
 
class Bar(Foo):
    pass
 
issubclass(Bar, Foo)

  

異常處理

 一、異常簡介

在編程過程當中爲了增長友好性,在程序出現bug時通常不會將錯誤信息顯示給用戶,而是現實一個提示的頁面,通俗來講就是不讓用戶看見大黃頁!!!

try:
    pass
except Exception,ex:
    pass

二、異常種類

 python中的異常種類很是多,每一個異常專門用於處理某一項異常!!!

AttributeError          試圖訪問一個對象沒有的樹形,好比foo.x,可是foo沒有屬性x
IOError                 輸入/輸出異常;基本上是沒法打開文件
ImportError             沒法引入模塊或包;基本上是路徑問題或名稱錯誤
IndentationError        語法錯誤(的子類) ;代碼沒有正確對齊
IndexError              下標索引超出序列邊界,好比當x只有三個元素,卻試圖訪問x[5]
KeyError                試圖訪問字典裏不存在的鍵
KeyboardInterrupt       Ctrl+C被按下
NameError               使用一個還未被賦予對象的變量
SyntaxError             Python代碼非法,代碼不能編譯(我的認爲這是語法錯誤,寫錯了) 
TypeError               傳入對象類型與要求的不符合
UnboundLocalError       試圖訪問一個還未被設置的局部變量,基本上是因爲另有一個同名的全局變量,致使你覺得正在訪問它
ValueError              傳入一個調用者不指望的值,即便值的類型是正確的
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
更多異常

異常類只能用來處理指定的異常狀況,若是非指定異常則沒法處理。

 

# 未捕獲到異常,程序直接報錯
 
s1 = 'hello'
try:
    int(s1)
except IndexError as e:
    print e
except (KeyError, ValueError) as e:
    print e
 
# 萬能異常
except Exception,e:
    print e

 

三、異常其餘結構

try:
    # 主代碼塊
    pass
except KeyError,e:
    # 異常時,執行該塊
    pass
else:
    # 主代碼塊成功執行完,執行該塊
    pass
finally:
    # 不管異常與否,最終執行該塊
    pass

四、主動觸發異常

try:
    raise Exception('錯誤了。。。')
except Exception,e:
    print e

五、自定義異常

class NickException(Exception):
 
    def __init__(self, msg):
        self.message = msg
 
    def __str__(self):
        return self.message
 
try:
    raise NickException('個人異常')
except NickException,e:
    print e

六、斷言

# assert 條件
# 條件成立則pass,條件不成立則報錯
 
assert 1 == 1
 
assert 1 == 2

 

反射

  python中的反射功能是由如下四個內置函數提供:hasattr、getattr、setattr、delattr,改四個函數分別用於對對象內部執行:檢查是否含有某成員、獲取成員、設置成員、刪除成員。

# commons.py 文件

name = "nick"

def f1():
    return "This is f1."

def f2():
    return "This is f2."

def nb():
    return "This is niubily."



# index.py 文件
import commons

#根據字符串的形式去某個模塊中尋找東西
target_func = getattr(commons,"f1")     # 找函數
result = target_func()
print(result)

target_func = getattr(commons,"name")   # 找全局變量
print(target_func)

target_func = getattr(commons,"age",None)   # 找不到返回None
print(target_func)

#根據字符串的形式去某個模塊中判斷東西是否存在
tarhas_func = hasattr(commons,"f5")     # 找函數
print("before:",tarhas_func)

# tarhas_func = hasattr(commons,"name") # 找全局變量
# print(tarhas_func)

#根據字符串的形式去某個模塊中設置東西
setattr(commons,"f5","lambda x: return \"This is new func.\"")  # 設置一個函數
setattr(commons,"age",18)       # 設置全局變量

tarhas_func = hasattr(commons,"f5")     # 檢查函數是否存在
print("after:",tarhas_func)

#根據字符串的形式去某個模塊中刪除東西
delattr(commons,"f5")       # 刪除一個函數

tarhas_func = hasattr(commons,"f5")     # 檢查函數是否存在
print("end:",tarhas_func)

 

# 經過字符串的形式,導入模塊。起個別名 ccas。

comm = input("Please:")
ccas = __import__(comm)
ccas.f1()

# 須要作拼接導入時後加 fromlist=True(不然只導入lib)
ccas = __import__("lib."+comm, fromlist=True)
補充__import__
##### 路由系統 #####

# 輸入 模塊名/函數名  (例如:commons/nb)
url = input("Please input you want url:")

target_module, target_func = url.split("/")

#m = __import__("lib."+target_module,fromlist=True)
m = __import__(target_module)

if hasattr(m,target_func):
    target_func = getattr(m,target_func)
    result = target_func()
    print(result)
else:
    print("Sorry,it's 404 not found.")
路由系統

 

單例模式

  單例模式存在的目的是保證當前內存中僅存在單個實例,避免內存浪費!!!

程序若是併發量大的話,內存裏就會存在很是多功能上如出一轍的對象。存在這些對象確定會消耗內存,對於這些功能相同的對象能夠在內存中僅建立一個,須要時都去調用

# 單例模式

class Foo:

    __n = None

    def __init__(self):
        self.name = "nick"
        self.age = 18
        self.job = "pythoner"

    @staticmethod
    def dl():
        if Foo.__n:
            return Foo.__n
        else:
            Foo.__n = Foo()
            return Foo.__n


# 建立對象時不能再直接使用:obj = Foo(),而應該調用特殊的方法:obj = Foo.dl() 。

f1 = Foo.dl()
print(f1)
f2 =Foo.dl()
print(f2)
f3 =Foo.dl()
print(f3)

# 運行結果
<__main__.Foo object at 0x0000000001142390>
<__main__.Foo object at 0x0000000001142390>
<__main__.Foo object at 0x0000000001142390>

裝飾器方式單例模式

# 裝飾器方式單例模式

def singleton(argv):
    dic = {}

    def s(*args, **kwargs):

        if argv not in dic:
            dic[argv] = argv(*args, **kwargs)
            return dic[argv]
        else:
            return dic[argv]

    return s


# 類上加單例裝飾器
@singleton
class Foo:
    pass

@singleton
class Foo2:
    pass
相關文章
相關標籤/搜索