python關於類的理解

    Python中所有的數據都是對象,它提供了許多高級的內建數據類型,功能強大,使用方便,是Python的優點之一。那麼什麼時候使用自定義類呢?比如設計一個Person類,如果不使用自定義類,可以這樣做:

 person=['mike', 23, 'male']  #0-姓名, 1-年紀, 2-性別

 print(person[0], person[1], person[2])

       可以看到,使用內建類型list,需要用下標來引用成員數據,不直觀。可以改用dic類型來做:

 person1={'name':'mike', 'age': 23, 'sex':'male'}

 person2={'name':'hellen', 'age': 20, 'sex':'female'}

 print(person1['name'], person1['age'],person1['sex'])

       這樣不用記憶下標,直觀多了。但是字典的語法仍然有些麻煩,如果能夠像這樣引用:person.name,person.age等,就更好。這就是自定義類存在的好處了:

 class Person:

     def __init__(self, name, age,sex):

         self.name =name

         self.age = age

         self.sex = sex

     def __str__(self): #重載該函數便於測試

         sep = ','

         returnself.name+sep+str(self.age)+sep+self.sex

 person1 = Person('mike', 23, 'male') 

 person2 = Person('hellen', 20, 'female')

 print(person1)

 print(person2.name, person2.age,person2.sex)

        可以看到,只要定義好這個類的構造函數,就可以很方便的生成這個類的實例,並且引用數據成員也很方便,比直接使用內建類型方便多了。其實Python就是用內建類型dic來實現自定義類的成員的存儲和引用的,從這個角度來看,自定義類是內建類的簡化使用方式,內建類型是自定義類型內部必要的組成部分。同時,由於自定義類可以定義自己的成員函數或者重載預定義的方法,所以自定義類擴展了內建類的功能,可以提供對現實事物更好的模擬,這正是面向對象編程的優點。編程的時候,先對所要模擬的事物形成概念,然後儘量使用類來抓住概念,這是面向對象設計的關鍵。如果需要產生多個同類的對象,應該儘可能地設計一個自定義類去抽象它們。

       對自定義類的使用也別過分,有些功能只需要定義一個函數就可以做到了,這個時候就沒有必要設計一個自定義類。

        相關概念:

  

  • 類(Class): 用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。
  • 類變量:類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作爲實例變量使用。
  • 數據成員:類變量或者實例變量用於處理類及其實例對象的相關的數據。
  • 方法重寫:如果從父類繼承的方法不能滿足子類的需求,可以對其進行改寫,這個過程叫方法的覆蓋(override),也稱爲方法的重寫。
  • 實例變量:定義在方法中的變量,只作用於當前實例的類。
  • 繼承:即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也允許把一個派生類的對象作爲一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模擬"是一個(is-a)"關係(例圖,Dog是一個Animal)。
  • 實例化:創建一個類的實例,類的具體對象。
  • 方法:類中定義的函數。
  • 對象:通過類定義的數據結構實例。對象包括兩個數據成員(類變量和實例變量)和方法。
  • Python中的類提供了面向對象編程的所有基本功能:類的繼承機制允許多個基類,派生類可以覆蓋基類中的任何方法,方法中可以調用基類中的同名方法。對象可以包含任意數量和類型的數據。

1.類的概念

    自己先創建一個類,根據類創建一個子類。這個子類有點不同。如下面範例mycat是父類,而tcat是子類。

    Mycat是個對象,而tt是mycat的實例。

    Mycat中leg和head是靜態的屬性,而def定義的方法就是從靜態的屬性中動態得到另外的數據。比如mycat中的weight就是從head中得到的。而hh是從子類中實例化的。Hh的weight就跟mycat不同。



class mycat(object):
    leg='short'
    head='big'
    def __init__(self):
        self.weight=len(self.head)

class tcat(mycat):
    head = 'smell'

tt=mycat()
print tt.leg

print tt.weight


hh=tcat()
print hh.head
print hh.leg
print hh.weight

 

    子類super()調用父類方法,並做出自己獨特更改

    super()是一個特殊函數, 幫助Python將父類和子類關聯起來。 這行代碼讓Python調用ElectricCar 的父類的方法__init__() , 讓ElectricCar 實例包含父類的所有屬性。 父類也稱爲超類 ( superclass) ,名稱super因此而得名。


class Person():
    def __init__(self, name):
        self.name = name
 
class email(Person):
    def __init__(self, name, email):
        super().__init__(name)
        self.email = email
 
a = email('me', '[email protected]')
>>> a.name
... 'me'
>>> a.email
... '[email protected]'
#在Python 2.7中, 繼承語法稍有不同, ElectricCar 類的定義類似於下面這樣:
class Car(object):
def __init__(self, make, model, year):
--snip--

class ElectricCar(Car):
def __init__(self, make, model, year):
super(ElectricCar, self).__init__(make, model, year)
--snip

2.對實例,類以及靜態方法的總結


    實例方法就是類的實例能夠使用的方法;

    靜態方法是一種普通函數,就位於類定義的命名空間中,它不會對任何實例類型進行操作。使用裝飾器@staticmethod定義靜態方法。類對象和實例都可以調用靜態方法。

    類對象(python中定義的類本身也是對象)

    類方法是將類本身作爲對象進行操作的方法。類方法使用@classmethod裝飾器定義,其第一個參數是類,約定寫爲cls。類對象和實例都可以調用類方法。

 


class Foo(object):
    val1 = 'aa'                #類變量
    def __init__(self):
        self.val2 = 'bb'           #實例變量
		val3= 'cc'         #順便設置的變量不知道屬於什麼變量
        print self.__class__.val1   #實例對象訪問類變量的另一種方法

if __name__ == '__main__':
    foo = Foo()
    print foo.val1    #實例變量輸出爲1
    print '#'*80

    print foo.val2
    print '#'*80

    print Foo.val1
    print '#'*80

    print Foo.val2    #輸出出錯
    print Foo.val3    #輸出出錯



類變量定義在類的定義之後,實例變量則是以爲self.開頭。例如:

從上述的例子可以得到實例對象可以訪問類變量和實例變量

但是類對象卻只能訪問類變量,在實例方法中的變量都不能訪問。

而我隨便設置的變量則是實例和類對象都不能訪問。

備註:self是當前,類的實例的變量,self.__class__用於獲得對應的類的本身的變量。

 

靜態方法:聲明和調用時,都沒有隱含參數。

實例方法:聲明時,要定義一個隱含參數:self。調用時,實例本身被隱含地傳遞給這個參數。(這樣是否可以說明實例方法中的不同變量可以互相調用?)不可以必須通過函數傳遞。

類方法:聲明時,要定義一個隱含參數:cls。調用時,類本身的變量被隱含地傳遞給這個參數。

 

總結以下幾點:

一、類對象能使用本身的類變量和調用類方法和靜態方法;但是特殊的是類可以調用類中的普通函數,而實例不能調用。

二、實例對象的等級較高,能使用類變量,實例變量,而且能調用實例方法,類方法和靜態方法。不能調用類中的普通函數。只能用靜態方法來聲明普通函數實例才能調用。

三、普通函數無法訪問實例變量和類變量。


還有幾點總結如下:

實例方法:

特點是有個self作爲第一個參數;

這樣用__init__調用的時候的參數就會傳入實例方法;

print (self.fname)

但是類不能調用實例變量,也不能使用實例方法。只有當類實例化成爲實例對象才能使用以上的變量和方法

一個實例方法不能調用另一個實例方法;

###########################

實例可以使用類變量但是也可以使用實例變量

實例可以使用實例方法和類方法

實例中可以使用特殊方法使用類變量self.__class__.middlename

 

類方法:

特點是有個關鍵詞@classmethod;方法有個關鍵字func(cls)

不能使用實例變量和實例方法;

print (cls.middlename)

類對象和實例都可以調用這個方法;

######################################

類對象可以使用類變量但不可以使用實例變量

類對象可以調用類方法但是不能調用實例方法

類對象不可以調用實例方法

 

靜態方法:

用特殊關鍵字@staticmethod可以聲明;

類對象和實例都可以調用這個方法;

靜態方法不能訪問實例變量和類變量


爲什麼要使用靜態方法和類方法呢?或者說這兩種東西設計出來是做什麼用的呢?

    類中最常用的方法是實例方法, 即通過通過實例作爲第一個參數的方法。

    如果現在我們想寫一些僅僅與類交互而不是和實例交互的方法會怎麼樣呢? 我們可以在類外面寫一個簡單的方法來做這些,但是這樣做就擴散了類代碼的關係到類定義的外面. 於是我們@classmethod裝飾器來創建類方法,讓它在類中運行而不在實例中運行。


class Kls(object):
    no_inst = 0
    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1
    @classmethod
    def get_no_of_instance(cls_obj):
        return cls_obj.no_inst
ik1 = Kls()
ik2 = Kls()
print ik1.get_no_of_instance()
print Kls.get_no_of_instance()


    經常有一些跟類有關係的功能但在運行時又不需要實例和類參與的情況下需要用到靜態方法. 比如更改環境變量或者修改其他類的屬性等能用到靜態方法.


IND = 'ON'
class Kls(object):
    def __init__(self, data):
        self.data = data
    @staticmethod
    def checkind():
        return (IND == 'ON')
    def do_reset(self):
        if self.checkind():
            print('Reset done for:', self.data)
    def set_db(self):
        if self.checkind():
            self.db = 'New db connection'
        print('DB connection made for: ', self.data)
ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()

下面我們來看一個既使用了靜態方法也用了類方法的例子:


 class Kls(object):
    def __init__(self, data):
        self.data = data
    def printd(self):
        print(self.data)
    @staticmethod
    def smethod(*arg):
        print('Static:', arg)
    @classmethod
    def cmethod(*arg):
        print('Class:', arg)
 
>>> ik = Kls(23)
>>> ik.printd()
23
>>> ik.smethod()
Static: ()
>>> ik.cmethod()
Class: (<class '__main__.Kls'>,)
>>> Kls.printd()
TypeError: unbound method printd() must be called with Kls instance as first argument (got nothing instead)
>>> Kls.smethod()
Static: ()
>>> Kls.cmethod()
Class: (<class '__main__.Kls'>,)

        下面在再給出一個國外論壇的例子。classmothod和staticmethod是很相似的,不同的地方在於類方法可必須以cls作爲第一個參數,而靜態方法可以不需要任何參數:

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')






先寫到這裏。以後再慢慢補充!

時間倉促。如有錯漏希望能指正(留言或者電郵我)。本人郵箱[email protected]

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