面向對象進階

書....接...上...回...python

經典類vs新式類

先試着用python2和python3分別把下面代碼執行一邊程序員

class A:
    def __init__(self):
        self.n = 'A'
 
class B(A):
    def __init__(self):
         self.n = 'B'
 
class C(A):
    def __init__(self):
        self.n = 'C'
 
class D(B,C):
    def __init__(self):
         self.n = 'D'
 
obj = D()
print(obj.n)
View Code

  而後分別用python2和python3按照順序,分別把D、B、C的構造函數註釋掉換成pass執行,你會發現,在python2中,幾回輸出分別是D、B、A、C,在python3中幾回輸出分別是D、B、C、A。但若是咱們在A後面加上一個(object),那麼它就從經典類變成了一個新式類,而後你再次在python2中像剛纔那樣運行。你會發現此次的輸出結果由上次的D、B、A、C變成了D、B、C、A。python3再次運行無變化。ide

由此咱們能夠得出結果:函數

  python2:spa

  • 經典類:深度優先
  • 新式類:廣度優先

  python33d

  • 經典類:廣度優先
  • 新式類:廣度優先

靜態方法  

  @staticmethod裝飾器便可把其裝飾的方法變爲一個靜態方法,什麼是靜態方法呢?其實不難理解,普通的方法,能夠在實例化後直接調用,而且在方法裏能夠經過self.調用實例變量或類變量,但靜態方法是不能夠訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實至關於跟類自己已經沒什麼關係了,它與類惟一的關聯就是須要經過類名來調用這個方法。code

class Person(object):
    def __init__(self,name):
        self.name = name
 
    @staticmethod      #把eat方法變爲靜態方法
    def eat(self):
        print("%s is eating" % self.name)
 
p = Person("jack")
p.eat()

  上面的調用會出錯誤,說是eat須要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法後,再經過實例調用時就不會自動把實例自己看成一個參數傳給self了。對象

  解決方法有兩種:blog

  •  調用時主動傳遞實例自己給eat方法,即d.eat(d) 
  •  在eat方法中去掉self參數,但這也意味着,在eat中不能經過self.調用實例中的其它變量了

類方法

  類方法經過@classmethod裝飾器實現,類方法和普通方法的區別是,類方法只能訪問類變量,不能訪問實例變量。而且類方法傳入的第一個參數爲cls,是類自己。類方法能夠經過類直接調用,或經過實例直接調用。但不管哪一種調用方式,最左側傳入的參數必定是類自己。內存

class Dog(object):
    name = 'jack'
    def __init__(self, name):
        self.name = name

    @classmethod
    def eat(cls):
        print("%s is eating" % cls.name)

Dog.eat()

d = Dog("tom")
d.eat()

但不管上哪種的調用方式,結果打印的都是同樣的:

jack is eating
jack is eating

屬性方法

  屬性方法的做用就是經過@property把一個方法變成一個靜態屬性

class Dog(object):
    def __init__(self,name):
        self.name = name
 
    @property
    def eat(self):
        print(" %s is eating" %self.name)
 
d = Dog("Jack")
d.eat()

  上面這種代碼的運行會報錯這種錯誤,TypeError: 'NoneType' object is not callable,由於此時的eat已是一個靜態屬性了,不能再經過加()來調用。

正常調用以下:

d = Dog("jack")
d.eat
 
輸出
 jack is eating

PS:若是屬性方法還傳入了固定參數,想對這個固定參數進行修改怎麼辦呢?由於咱們知道,屬性方法的調用是不能加括號的,既然不能加括號,那就不能傳參數,擦,這樣的話,參數不就寫死了。。。固然是能夠改的,能夠在屬性方法的下方再寫一個同名的函數,用下面這種裝飾器  @函數名.setter  來進行裝飾,能夠在這個函數中進行參數的修改,固然還有裝飾器  @函數名.deleter   來裝飾函數,對屬性進行刪除操做。

面向對象中類成員的特殊方法

一、__doc__  表示類的描述信息

class Foo:
    """ 這是一個神奇的類 """
    def func(self):
        pass

print(Foo.__doc__)
f = Foo()
print(f.__doc__)

輸出結果:
這是一個神奇的類
這是一個神奇的類

PS:用類或者實例均可以進行調用該方法。

二、__module__、__class__

  •  __module__ 表示當前操做的對象在那個模塊
  •  __class__     表示當前操做的對象的類是什麼
class C:

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

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

三、__init__   構造函數,經過類建立對象時自動執行,用於完成對類的初始化操做

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

五、__call__  對象後面加括號,觸發執行。即:對象()或者類()()會觸發執行。

class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('我是call')
obj = Foo()  # 執行 __init__
obj()        # 執行 __call__

輸出結果:
我是call
View Code

六、__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__)
# 獲取對象obj1的成員
obj1 = Province('HeBei', 10000)
print(obj1.__dict__)
# 獲取對象obj2的成員
obj2 = Province('HeNan', 3888)
print(obj2.__dict__)
View Code

輸出結果:

{'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x000001875B266598>, 'func': <function Province.func at 0x000001875B266620>, '__dict__': <attribute '__dict__' of 'Province' objects>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}
{'name': 'HeBei', 'count': 10000}
{'name': 'HeNan', 'count': 3888}
View Code

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

class Foo:
    def __str__(self):
        return 'wusir'

obj = Foo()
print(obj)

# 輸出結果 wusir
View Code

八、 __getitem__,__setitem__,__delitem__

class Foo(object):
    def __getitem__(self, key):
        print('__getitem__', key)

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

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


obj = Foo()
result = obj['k1']  # 自動觸發執行 __getitem__
obj['k2'] = 'wusir'  # 自動觸發執行 __setitem__
del obj['k1']   #自動觸發執行 __delitem__,按上述寫法並未真正刪除,如需刪除,需在        
                      #__delitem__ 方法中寫刪除操做          

輸出:
__getitem__ k1
__setitem__ k2 wusir
__delitem__ k1
View Code

九、__new__  \  __metaclass__

class Foo(object):
    def __init__(self,name):
        self.name = name
 
f = Foo("wusir")

  上述代碼中,f是經過 Foo 類實例化的對象,其實,不只 f 是一個對象,Foo類自己也是一個對象,由於在python中,一切皆對象,那麼若是按照一切皆對象的理論:f對象是經過執行Foo類的構造方法建立,那麼Foo類對象應該也是經過執行某個類的構造方法建立。

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

  由上述打印結果咱們能夠發現:f是Foo的一個實例,Foo是type的一個實例,即:Foo類對象 是經過type類的構造方法建立。

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

通常方式:

class Foo():
    def func(self):
        pass

特殊方式:

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

def __init__(self,name,age):
    self.name = name
    self.age = age
Foo = type('Foo',(object,),{'func':func,'__init__':__init__})

f = Foo("wusir",22)
f.func()
特殊方式加上構造方法

因此,要牢記:類是由type類實例化產生的!

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

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

class MyType(type):
    def __init__(self, what, bases=None, dict=None):
        print("--MyType init---")
        super(MyType, self).__init__(what, bases, dict)

    def __call__(self, *args, **kwargs):
        print("--MyType call---")
        obj = self.__new__(self, *args, **kwargs)
        self.__init__(obj, *args, **kwargs)


class Foo(object):
    __metaclass__ = MyType

    def __init__(self, name):
        self.name = name
        print("Foo ---init__")

    def __new__(cls, *args, **kwargs):
        print("Foo --new--")
        return object.__new__(cls) 


# 第一階段:解釋器從上到下執行代碼建立Foo類
# 第二階段:經過Foo類建立obj對象
obj = Foo("Alex")
自定義元類

類的生成 調用 順序依次是 __new__ --> __init__ --> __call__

 反射

  經過字符串映射或修改程序運行時的狀態、屬性、方法, 內置函數分別爲:getattr、hasattr、setattr、delattr  獲取成員、檢查成員、設置成員、刪除成員。

class Foo(object):
    def __init__(self):
        self.name = 'wusir'

    def func(self):
        return 'jack'

obj = Foo()

#### 檢查是否含有成員 ####
print(hasattr(obj, 'name'))
print(hasattr(obj, 'func'))

#### 獲取成員 ####
print(getattr(obj, 'name'))
print(getattr(obj, 'func')())

#### 設置成員 ####
print(hasattr(obj,'age'))
setattr(obj, 'age', 18)
print(getattr(obj,'age'))

#### 刪除成員 ####
delattr(obj, 'name')
print(hasattr(obj,'name'))
View Code

  輸出結果:

True
True
wusir
jack
False
18
False
View Code
相關文章
相關標籤/搜索