第十三次課:Python類

  在Python中,可以通過class關鍵字定義自己的類,然後通過自定義的類對象類創建實例對象。如下,創建一個people類,並定義了一個初始化__init__函數。

class people(object):
   '''This is a people class'''
   address=[]
   code='0590'
   def __init__(self,name,age):
         self.name=name
         self.age=age

 屬性

  藉助這個例子我們來看看Python中類的相關內容。

  在上面的people類中,”address””code””name”和”age”都被稱爲類的數據屬性,但是它們又分爲類數據屬性和實例數據屬性。我們通過一個例子來演示,如下:

people.address.extend(['China','Fujian','Fuzhou'])
print 'Address is %s' %people.address
people.food=['foutiaoqiao','yuwan','rouyan']
print 'Food is %s' %people.food
print dir(people)
John=people('John',29)
print '%s is %s year old' %(John.name,John.age)
John.gender='Man'
print '%s is %s' %(John.name,John.gender)
print dir(John)
John.address.append('hudonglu')
print John.address
Wiki=people('Wiki',22)
print dir(Wiki)
print Wiki.address

運行結果:
Address is ['China', 'Fujian', 'Fuzhou']
Food is ['foutiaoqiao', 'yuwan', 'rouyan']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'address', 'code', 'food']
John is 29 year old
John is Man
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'address', 'age', 'code', 'food', 'gender', 'name']
['China', 'Fujian', 'Fuzhou', 'hudonglu']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'address', 'age', 'code', 'food', 'name']
['China', 'Fujian', 'Fuzhou', 'hudonglu']

  注:通過內建函數dir(),或者訪問類的字典屬性__dict__可以查看類有哪些屬性。如上例子dir(people)

  通過如上例子我們可以歸納幾點有關類的特性:

(1)類數據屬性屬於類本身,可以通過類名進行訪問或修改;如people.address

(2)類數據屬性也可以被類的所有實例訪問或修改;如Wiki.address='hudonglu'

(3)在類定義之後,可以通過類名動態添加類數據屬性,新增的類屬性也被類和所有實例共有;如

people.address.extend(['China','Fujian','Fuzhou'])

(4)實例數據屬性只能通過實例訪問;如John.gender

(5)在實例生成後,還可以動態添加實例數據屬性,但是這些實例數據屬性只屬於該實例;如John.gender

  對於所有的類,都有一組特殊的屬性:

__name__類的名字(字符串)
__doc__類的文檔字符串
__bases__類的所有父類組成的元組
__dict__類的屬性組成的字典
__module__類所屬的模塊
__class__類對象的類型

  通過這些屬性,可以得到 people類的一些信息

print people.__name__
print people.__doc__
print people.__bases__
print people.__dict__
print people.__module__

運行結果:
people
This is a people class
(<type 'object'>,)
{'__module__': '__main__', 'code': '0590', 'food': ['foutiaoqiao', 'yuwan', 'rouyan'], 'address': ['China', 'Fujian', 'Fuzhou', 'hudonglu'], '__dict__': <attribute '__dict__' of 'people' objects>, '__weakref__': <attribute '__weakref__' of 'people' objects>, '__doc__': 'This is a people class', '__init__': <function __init__ at 0x0000000005A27B38>}
__main__
<type 'type'>

  從上面的介紹瞭解到,類數據屬性屬於類本身,被所有該類的實例共享;並且,通過實例可以去訪問或修改類屬性。但是,在通過實例中訪問類屬性的時候一定要謹慎,因爲可能出現屬性”隱藏”的情況。那什麼情況下有隱藏呢,下面舉例說明:

print people.code
print people.__dict__
John.code='0722'
print John.code
print John.__dict__
del John.code

運行結果:
0590
{'__module__': '__main__', 'code': '0590', 'food': ['foutiaoqiao', 'yuwan', 'rouyan'], 'address': ['China', 'Fujian', 'Fuzhou', 'hudonglu'], '__dict__': <attribute '__dict__' of 'people' objects>, '__weakref__': <attribute '__weakref__' of 'people' objects>, '__doc__': 'This is a people class', '__init__': <function __init__ at 0x0000000005A27B38>}
0722
{'gender': 'Man', 'age': 29, 'code': '0722', 'name': 'John'}
0590

  從上述例子中分析得出如下結論:

(1)對於類屬性people.code,可以通過實例John進行訪問,切John.code等同於people.code;

(2)當通過實例賦值code屬性的時候,都將爲實例John新建一個code實例屬性,這時John.code不等同於people.code;

(3)當通過”del John.code”語句刪除實例的code屬性後,John.code又等同於people.code;

(4)當通過實例修改code屬性的時候,將修改John.code指向的內存地址(即people.code),此時John.code又等同於people.code;

  注意,雖然通過實例可以訪問類屬性,但是,不建議這麼做,最好還是通過類名來訪問類屬性,從而避免屬性隱藏帶來的不必要麻煩。

方法

  在一個類中,可能出現三種方法,實例方法、靜態方法和類方法,下面來看看三種方法的不同。

  實例方法的第一個參數必須是“self”,實例方法只能通過類實例進行調用,這時候“self”就代表這個類實例本身。通過“self”可以直接訪問實例的屬性。如下例子:

class people(object):
   '''This is a people class'''
   address=[]
   code='0590'
   def __init__(self,name,age):
         self.name=name
         self.age=age
   def info(self):
       print '%s is %s' %(self.name,self.age)
       
John=people('John',29)
John.info()

運行結果:
John is 29

  類方法以cls作爲第一個參數,cls表示類本身,定義時使用@classmethod裝飾器。通過cls可以訪問類的相關屬性。

class people(object):
   '''This is a people class'''
   address=[]
   code='0590'
   def __init__(self,name,age):
         self.name=name
         self.age=age
   @classmethod
   def classinfo(cls):
       print cls.__name__
       print dir(cls)
       
people.classinfo()
John=people('John',29)
John.classinfo()

運行結果:
people
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'address', 'classinfo', 'code', 'food', 'info']

  靜態方法沒有參數限制,既不需要實例參數,也不需要類參數,定義的時候使用@staticmethod裝飾器。

class people(object):
   '''This is a people class'''
   address=[]
   code='0590'
   def __init__(self,name,age):
         self.name=name
         self.age=age
   @staticmethod
   def static():
   print people.code
   print people.address

people.satic()
John=people('John',29)
John.static()

運行結果:
0590
['China', 'Fujian', 'Fuzhou', 'hudonglu']

  三種方法的主要區別在於參數,實例方法被綁定到一個實例,只能通過實例進行調用;但是對於靜態方法和類方法,可以通過類名和實例兩種方式進行調用。

繼承

  在Python中,同時支持單繼承與多繼承,基礎語法如下:

  class SubClassName(ParentClass1 [, ParentClass2, ...]):

     class_suite

  實現繼承之後,子類將繼承父類的屬性,也可以使用內建函數insubclass()來判斷一個類是不是另一個類的子孫類。如下例子:

class parent():
   '''This is a parent class'''
   num=[]
   def plus(self,a,b):
       return a+b
class child(parent):
   '''This is a child class'''
child.num.extend(xrange(5))
print child.num
print '10 + 20 = ',child().plus(10,20)
print issubclass(child,parent)
print issubclass(child,object)
print child.__bases__
print parent.__doc__
print child.__doc__

運行結果:
[0, 1, 2, 3, 4]
10 + 20 =  30
True
False
(<class __main__.parent at 0x0000000004E59828>,)
This is a parent class
This is a child class

  注:注意例子中的文檔字符串。文檔字符串對於類,函數/方法,以及模塊來說是唯一的,也就是說__doc__屬性是不能從父類中繼承來的。

  當在Python中出現繼承的情況時,一定要注意初始化函數__init__的行爲。有以下幾點需要注意:

(1)如果子類沒有定義自己的初始化函數,父類的初始化函數會被默認調用;但是如果要實例化子類的對象,則只能傳入父類的初始化函數對應的參數,否則會出錯。如下例子:

class Parent(object):
   def __init__(self, name):
       self.name = name
       print self.name

class Child(Parent):
   '''This is a child class'''

c = Child('John Test')
print c
Child()

運行結果:
Traceback (most recent call last):
John Test

 File "C:/Users/YangQing/PycharmProjects/Test/class.py", line 81, in <module>
   Child()
TypeError: __init__() takes exactly 2 arguments (1 given)

(2)如果子類定義了自己的初始化函數,而沒有顯示調用父類的初始化函數,則父類的屬性不會被初始化

class Parent(object):
   def __init__(self, name):
       self.name = name
       print self.name

class Child(Parent):
   '''This is a child class'''
   def __init__(self):
       print 'This is a self test'

c = Child()
print c.name

運行結果:
This is a self test
Traceback (most recent call last):
 File "C:/Users/YangQing/PycharmProjects/Test/class.py", line 83, in <module>
   print c.name
AttributeError: 'Child' object has no attribute 'name'

(3)如果子類定義了自己的初始化函數,顯示調用父類,子類和父類的屬性都會被初始化

class Parent(object):
   def __init__(self, name):
       self.name = name
       print self.name

class Child(Parent):
   '''This is a child class'''
   def __init__(self):
       print 'This is a self test'
       super(Child,self).__init__('Data from Child class')

c = Child()

運行結果:
This is a self test
Data from Child class

  從上面的例子中我們通過super()函數調用了父類__init__方法,下面就介紹下super():

  在子類中,一般會定義與父類相同的屬性(數據屬性,方法),從而來實現子類特有的行爲。也就是說,子類會繼承父類的所有的屬性和方法,子類也可以覆蓋父類同名的屬性和方法。

class Parent(object):
   name = "This is a parent class"
   def data(self):
       print "This is data from Parent"

class Child(Parent):
   name = "This is a child class"
   def data(self):
       print "This is data from child"

c = Child()
c.data()
print Child.name

運行結果:
This is data from child
This is a child class

  在上述例子中子類的屬性”name”和”data”覆蓋了父類的屬性,所以子類有了自己的行爲。但是,有時候可能需要在子類中訪問父類的一些屬性,這時候,可以通過父類名直接訪問父類的屬性,當調用父類的方法是,需要將”self”顯示的傳遞進去的方式。

class Parent(object):
   name = "This is a parent class"
   def data(self):
       print "This is data from Parent"

class Child(Parent):
   name = "This is a child class"
   def data(self):
       print "This is data from child"
       print Parent.name
       Parent.data(self)

c = Child()
c.data()

運行結果:
This is data from child
This is a parent class
This is data from Parent

這種方式有一個不好的地方就是,需要經父類名硬編碼到子類中,爲了解決這個問題,可以使用Python中的super關鍵字:

class Parent(object):
   name = "This is a parent class"
   def data(self):
       print "This is data from Parent"

class Child(Parent):
   name = "This is a child class"
   def data(self):
       print "This is data from child"
       print super(Child, self).name
       super(Child, self).data()

c = Child()
c.data()

運行結果:
This is data from child
This is a parent class
This is data from Parent

  對於”super(Child, self).data()”可以理解爲,首先找到Child的父類Parent,然後調用父類的data方法,同時將Child的實例self傳遞給data方法。

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