Python基礎:Python類(真累~)

類的聲明:

一、類的屬性

(私有屬性和公有屬性)

(類屬性)

二、類的方法

(構造方法、析構方法、自定義方法、特殊成員方法)

(靜態方法、類方法、類屬性)

三、類的繼承

(方法和屬性的繼承,方法的重構)

(抽象類,多重繼承)

四、類的多態

(實現接口的重用)

五、類的特殊裝飾

(@staticmethod、@classmethod、@property)

六、類的來源和原類(metaclass)

七、反射

  • 類的聲明

    使用class聲明類,建議類名單詞首字母大寫。

    “新式類”和“經典類”的區分在Python 3之後就已經不存在,在Python 3.x之後的版本,因爲所有的類都派生自內置類型object(即使沒有顯示的繼承object類型),即所有的類都是“新式類”。

    新式類:

class Management(object):

    def add():
        pass

      經典類:

class Management:
    pass

 

 

 

  • 類的屬性 

    類的屬性就是類定義的變量值。

       公有屬性:在類裏直接定義的屬性,它在類名下面直接定義。

                        調用:1、類中調用:類名.屬性名 ,更改原公有屬性值

                                   2、實例調用:實例.屬性名

class Management(object):
    num = 10

    def add(self):
        Management.num +=10            # 類中調用公有屬性並更改值,num=11
        pass

s1 = Management()
s2 = Management()

# 第一種情況:s1實例中調用公有屬性,s2實例沒有調用公有屬性
s1.num +=1
Management.num += 2
"""
<結果>
s1不變,s2和Management都改變了
s1_num:11   s2_num:12   Manage_num:12
"""

# 第二種情況:先調用s1實例公有屬性,再通過S1調用add更改,然後再使用類更改
s1.num +=1
s1.add()
Management.num += 2
"""
<結果>
先調用的s1實例num依然不變,s2和Management都被修改了
s1_num:11   s2_num:22   Manage_num:22

"""

 問題:爲什麼修改num的值以後,實例s1和實例s2會有不同的結果呢 ?

            因爲公有屬性查找的順序是:先找實例的公有屬性,沒找到再找類裏的公有屬性

            可以這樣理解:Management相當於一個微信羣,num是羣裏發的一張照片,S1和S2是羣裏面的兩個人。

            情況1:S1把照片存到本地,P了個雙眼皮,S2說太難看了,我也不會P圖,不保存。這個時候發照片的Management說我也覺得難看,把圖撤回,重新發了一個P的圖。S2就只能保存最新的圖片。

            情況2:S1找到Management說你的圖太醜了,重新改一下吧,Management說好!撤回圖片修改了~

 

       私有屬性:加兩個下劃線,__membername,編譯的時候自動加上類名,變成_classname__membername,這種技術叫變量名壓縮(mangling),以達到外部不能調用的目的。實際使用_classname__membername是可以調用的,但是不符合規定。標準方法是通過定義函數來獲取。

class Classname(object):
 '''this is a demo!'''
 def __init__(self):
  self.__membername = 88

 def read_membername(self):            # 標準的外部訪問方法,使用函數來讀取私有屬性。
  return self.__membername

s= Classname()
print(s._Classname__membername)
print(s.read_membername())
'''
<結果>
88
88
------像下面的調用,會出現AttributeError錯誤------
print(s.__membername)
'''

       類屬性:  類自帶的屬性,需要注意的是實例化的類屬性,和原類的類屬性不同。用上面的例子作演示。

屬性
作用
示例
結果
__doc__類的文檔字符串print(s.__doc__)
print(Classname.__doc__)

this is a demo!

this is a demo!

__dict__類的屬性組成的字典print(s.__dict__)
print(Classname.__dict__)

{'_Classname__membername': 88}

{'__init__':, '__module__': '__main__', '__doc__': '\nthis is a demo!\n', 'read_membername':}

__name__類的名字(字符串)##不能用於實例print(s.__name__ )
print(Classname.__name__ )


Classname

__bases__類的所有父類組成的元組#不能用於實例print(s.__bases__)
print(Classname.__bases__)


(,)爲什麼沒有值?可能是編譯器問題

__module__類所屬的模塊print(s.__module__)
print(Classname.__module__)
__main__
__main__
__class__類對象的類型print(s.__class__)
print(Classname.__class__)
待測
__slots__

限定類屬性,在類屬性位置定義

未在slots定義的屬性都是非法屬性

__slots__.('name','age','sexy')使用'name','age','sexy'的以外屬性會報錯

           

  • 類的方法

    類的方法就是類裏面定義的函數。類的構造方法、析構方法、自定義類方法、靜態方法、類方法、屬性方法、特殊成員方法。

    構造方法:__init__

    實例化類的時候就會運行的函數。希望初始化的參數放置在init下面。(個人覺得,這個初始化參數可以是一切對象!)

class A(object):
 def instense(self):
  print("init obj A")
  
class B(object):
 def __init__(self, para):
  self.init_para = para
  self.obj_A = A()
  self.num = 1
 
 def show(self):
  print(self.init_para)
  self.obj_A.instense()
  print(self.num)
haha = B("this is para")
haha.show()


----------
this is para

init obj A

1

   析構方法:

   __del__:銷燬實例時,方法纔會執行。

class Hello(object):
    def __del__(self):
        print("你刪除了實例")
# 在python上測試
instance = Hello()
del instance
# 當然也可以使用實例調用,但沒有這麼用的~~
instance.__del__()


  自定義方法:

  除去類中自帶的以_下劃線開頭的函數,在類中定義一個函數,實現相應的功能。

class Person(object):
    def __init__(self,person_name, person_age)

   靜態方法:

    @staticmethod,不需要訪問類裏的任何參數。所帶的參數都是從外部傳入的。

class Person(object):
    def __init__(self,person_name, person_age):
        self.name = person_name
        self.age = person_age
        
    @staticmethod
    def info(country):
        print(country)

   類方法:

   @classmethod,第一個參數必須是類屬性。

class Person(object):
    country = "china"
    def __init__(self,person_name, person_age):
        self.name = person_name
        self.age = person_age
        
    @classmethod
    def info(country):
        print(country)

    屬性方法:

    @property把一個函數變成一個靜態屬性

    直接調用函數名字,不需要加括號,就能獲取到函數返回值。一般用在不注重過程,只要結果的情況!

class Person(object):
    country = "china"
    def __init__(self,person_name, person_age):
        self.name = person_name
        self.age = person_age

    @property
    def health_point(self):
        print("HP:【{}】".format(self.age*2))
        return self.age*2

P = Person("laowang",23)
P.health_point                # 不需要括號,看起來完全是一個屬性,這就是屬性方法

'''上面的類屬性只是只讀的,即然是叫屬性,那麼只讀就顯得太LOW了'''

    類屬性裝飾器@property,裝飾以後,函數就有:賦值setter\銷燬deleter兩個方法。

class Person(object):
    country = "china"
    def __init__(self,person_name, person_age):
        self.name = person_name
        self.age = person_age

    @property
    def health_point(self):
        self.age = self.age*2
        print("HP:【{}】".format(self.age))
        return self.age

    @health_point.setter                    # 增加了一個賦值方法
    def health_point(self, add):
        self.age = add

    @health_point.deleter                   # 增加了一個銷燬屬性的方法
    def health_point(self):
        del self.age

P = Person("laowang", 33)
print(P.health_point)

P.health_point = 22                        # 給health.point賦值
print(P.health_point)

del P.health_point                         # 銷燬屬性


    

    特殊成員方法:

方法
作用示例結果
__call__

默認未定義

類實例化後,調用實例運行的方法

p = Person()

p()

Person是類名

實例p沒有調用函數,加()運行call方法

__str__

默認未定義,定義時必須有返回值

定義時,打印實例,輸入str返回值

p = Person()

print (p)

Person是類名

打印實例p,運行str方法,打印返回值

__getitem__
用於索引操作,如字典。獲取數據

p = Person()

p['name']

自動運行getitem
__setitem__
用於索引操作,如字典。賦值

p = Person()

p['name'] = 'David'

自動運行setitem
__delitem__
用於索引操作,如字典。刪除數據

p = Person()

del p['name']

自動運行delitem
__new__

類實例化時,執行__new__,並且會阻止

init運行,可以在new裏調用init


p = Person()參照例子二
__len__
待續


__cmp__待續

'''例子一call\str\getitem\setitem\delitem方法'''
class Person(object):
 def __call__(self):
  print("print call")
 
 def __str__(self):
  print("print str:",end='')
  return "1"
  
 def __getitem__(self,key):  
  print("getitem:",key)
  
 def __setitem__(self,key,value):
  print('setitem:',key,value)
 
 def __delitem__(self,key):
  print('delitem:',key)
  
p = Person()
p()
print(p)
print('-----------')
get = p['name']
p['name'] = 'David'
del p['name']
'''例子二:__new__/__init__'''
class Person(object):

 def __init__(self):

  print('this is init!')
  

 def __new__(self):                  # __new__會阻斷__init__執行,要想執行,需要寫init方法

  print("this is new!")

  self.__init__(self)        # 如果去掉這一行,init不能執行
  
p = Person()


    類的來源和元類:

    http://blog.jobbole.com/21351/中文版詳細解答。下面寫一個自己理解的簡版的。

    首先,類也是對象,可以:

    1)   你可以將它賦值給一個變量

    2)   你可以拷貝它

    3)   你可以爲它增加屬性

    4)   你可以將它作爲函數參數進行傳遞

    

    類也是是由type()這個函數創建的,type是類的類,類的爹。學名叫元類!

    也許有人會問那爲啥type()能查看到數據類型呢?

    可能你會注意到,type的結果前是class。。。因爲數據類型在Python中都是類定義的,這也說明了,爲什麼數字,字符等等全是對象。


    type格式:

    type(類名,(父類元組),{屬性和方法的字典})  ,  父類元組可以沒有;後面兩個典省略時,默認值爲None。

'''一言不合就上例子'''

Person = type('Person',(),{'country':'china'})

print(Person.country)

# 帶繼承Person類的用法
def run():                    # 定義函數,把run傳給Action的leg方法。
    print('running...')

Action = type('Action',(Person,),{'leg':run})

print(Action.country)
Action.leg()


    metaclass:

    創建類的時候,使用metaclass,python就會使用指定的元類創建類。找不到則使用type創建。

class MyType(type):
    def __init__(self,*args,**kwargs):

        print("Mytype __init__",*args,**kwargs)

    def __call__(self, *args, **kwargs):
        print("Mytype __call__", *args, **kwargs)
        obj = self.__new__(self)
        print("obj ",obj,*args, **kwargs)
        print(self)
        self.__init__(obj,*args, **kwargs)
        return obj

    def __new__(cls, *args, **kwargs):
        print("Mytype __new__",*args,**kwargs)
        return type.__new__(cls, *args, **kwargs)

print('here...')
class Foo(object,metaclass=MyType):
    def __init__(self,name):
        self.name = name

        print("Foo __init__")

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

f = Foo("Alex")
print("f",f)
print("fname",f.name)

 

  • 類的繼承

    代碼重用。

    Python3中類有單繼承和多繼承,其中多繼承與Python2的繼承順序不同。

    子類方法與父類重名,子類方法會覆蓋父類

  單繼承:

'''一言不合就上例子'''

class Person(object):
    def __init__(self,person_name):
        self.name = person_name
    def info(self):
        print("this is personal info:\nname:{}".format(self.name))

class Action(Person):
    def __init__(self,person_name):
    '''父類析構函數有兩種繼承方法'''
        super(Action, self).__init__(person_name)     # 方法一
        # Person.__init__(self, person_name)          # 方法二

    def run(self):
        print("{} is running...".format(self.name))

a = Action('Alex')
a.run()                            # 繼承了Person的屬性。
a.info()                           # 繼承了Person的方法。


多重繼承:

不能同時繼承兩父親類和爺爺類比如B繼承A,C繼承時就不能同時寫A和B,只寫B就可以了。


class ROOT(object):
    def __init__(self):
        print("this is root")
    def action(self):
        print("ROOT is action...")


class A(ROOT):
    def __init__(self):
        super(A, self).__init__()
        print ('init A...')
    def action(self):
        print("A is action...")

class B(ROOT):
    def __init__(self):
        super(B, self).__init__()
        print ('init B...')
    def action(self):
        print("B is action...")
        
class C(A):
    def __init__(self):
        super(C, self).__init__()
        print ('init C...')
    def action(self):
        print("C is action...")
        
class D(B,C):
    def __init__(self):
        super(D, self).__init__()
        print ('init D...')
    def action(self):
        print("D is action...")

d = D()


要理解繼承的順序,因爲當方法重名時,順序靠後的方法覆蓋前面的方法。

類的繼承關係:                D(A,B)實例化時候的繼承先後順序:先找B(及其父類),再找C(及其父類)

blob.png        blob.png

 

  • 類的多態

    多態就是同一個父類的方法,不同子類繼承可以進行不同的改寫,實現多種不同的功能。

    任何依賴父類作爲參數的函數或者方法都可以不加修改地正常運行,原因就在於多態。

'''接着上面的例子'''

# 定義一個函數,把ROOT類傳進裏面

def who_action(class_name):
    class_name.action()

who_action(ROOT())
who_action(D())
who_action(A())


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章