在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方法。