一.類的定義
定義方式:class關(guān)鍵字
例如,下面創(chuàng)建了一個Person的類什黑,并且實現(xiàn)了這個類的初始化函數(shù)"init":
#coding=UTF-8
class Person(object):
count = 0
books = ["python"]
def __init__(self, name, age):
self.name = name
self.age = age
接下來就通過上面的Person類來看看Python中類的相關(guān)內(nèi)容摘盆。
二.數(shù)據(jù)屬性
在上面的Person類中坝冕,"count"欢峰、"books"以及"name"梨树、"age"都被稱為類的數(shù)據(jù)屬性坑夯,但是它們又分為類數(shù)據(jù)屬性和實例數(shù)據(jù)屬性
1.類數(shù)據(jù)屬性
類變量必須是緊接在類名后面定義的變量,相當于java和c++的static變量
如"count"抡四、"books"
class Person(object):
count = 0
books = ["python"]
def __init__(self, name, age):
self.name = name
self.age = age
def fcn(self, val=400):
val=300
self.val1 = val
self.val2 = 500
>>Person.val
AttributeError: type object 'Person' has no attribute 'val'
這說明類變量只能是在類名后面的定義的變量柜蜈,其他處的變量不是類變量
(1)類可以訪問已定義的類數(shù)據(jù)屬性
主要有三種訪問方式:
a.
>>print Person.books
['python']
b.
class Person(object):
count = 0
books = ["python"]
def __init__(self, name, age):
self.name = name
self.age = age
print self.__class__.count #__class__:類對象的類型
>>Mary=Person("Mary",12)
0
c.
class Person(object):
count = 0
books = ["python"]
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def echo(cls):
print cls.count
>>Person.echo()
0
(2)類可以修改已定義的類數(shù)據(jù)屬性
>>Person.books.append("R")
>>print Person.books
['python', 'R']
(3)類也可以增加新的類數(shù)據(jù)屬性
>>Person.hobbies = ["reading", "swimming"]
>>print Person.hobbies
['reading', 'swimming']
#還可以用內(nèi)建函數(shù)dir()或者訪問類的字典屬性__dict__來查看類的所有屬性,可以看出是否增加了hobbies類數(shù)據(jù)屬性
>>print dir(Person)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'books', 'count', 'hobbies']
>>print Person.__dict__
{'count': 0, '__module__': '__main__', 'books': ['python', 'R'], 'hobbies': ['reading', 'swimming'], '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, '__init__': <function __init__ at 0x0000000002442EB8>}
2.實例數(shù)據(jù)屬性
實例變量必須在____init____函數(shù)里定義土匀,相當于java和c++的普通變量如"name"律姨、"age"
class Person(object):
count = 0
books = ["python"]
def __init__(self, name, age):
self.name = name
self.age = age
def fcn(self, val=400):
val=300
self.val1 = val
self.val2 = 500
>>Mary=Person("Mary",12)
>>Mary.val1
AttributeError: 'Person' object has no attribute 'val1'
這說明實例變量必須是init函數(shù)里以self開頭的變量棱诱,其他以self開頭的變量并不是實例變量
(1)類實例可以訪問已定義的實例數(shù)據(jù)屬性
>>Mary= Person("Mary", 28)
>>print "%s is %d years old" %(Mary.name, Mary.age)
Mary is 28 years old
(2)類實例可以修改已定義的實例數(shù)據(jù)屬性
>>Mary.age=15
>>print Mary.age
15
(3)類實例也可以增加新的實例數(shù)據(jù)屬性
>>wilber.gender = "male"
>>print Mary.gender
"male"
>>print dir(wilber) # 查看發(fā)現(xiàn)類實例Mary實例是否增加了gender實例數(shù)據(jù)屬性
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'books', 'count', 'gender', 'hobbies', 'name']
# "gender" 是新增的實例屬性,是僅僅屬于 Mary實例的秘噪,別的實例不能訪問
>>Jack= Person("Jack", 27)
>>print Jack.gender#實例Jack引用屬性gender會報錯
AttributeError: 'Person' object has no attribute 'gender'
3.類數(shù)據(jù)屬性和實例數(shù)據(jù)屬性的聯(lián)系
(1)類實例可以訪問類數(shù)據(jù)屬性,包括類定義中的和新增的類數(shù)據(jù)屬性
>>print Mary.books,Mary.hobbies
['python', 'R'] ['reading', 'swimming']
(2)類實例可以修改類數(shù)據(jù)屬性
>>Mary.books.append("C#")
>>print Mary.books
['python', 'R', 'C#']
但是勉耀,在通過實例來訪問類屬性的時候一定要謹慎指煎,因為可能出現(xiàn)屬性"隱藏"的情況。
當一個類的實例被構(gòu)造時便斥,會將當前類變量賦值給這個實例至壤,當前類變量的值是是多少,這個實例得到的類變量的值就是多少
a.不可變類型的類數(shù)據(jù)屬性
當通過實例來賦值或修改類數(shù)據(jù)屬性時枢纠,相當于為實例新建一個實例屬性像街,此時無論類如何修改此類數(shù)據(jù)屬性,通過實例訪問此數(shù)據(jù)屬性仍是不變的晋渺,因為這個數(shù)據(jù)屬性是屬于實例新建的實例數(shù)據(jù)屬性镰绎,而不是原來的那個類數(shù)據(jù)屬性,只是名字一樣而已些举,因此通過類訪問此數(shù)據(jù)屬性和通過實例訪問此數(shù)據(jù)屬性的值都是不同的跟狱;如果我們刪除了實例的新建的實例數(shù)據(jù)屬性,那么類修改類數(shù)據(jù)屬性后户魏,通過實例訪問此數(shù)據(jù)屬性的值也會隨著類改變而改變
>>print Mary.count,Person.count
0 0
>>print "Person.count is Mary.count: ", Person.count is Mary.count
Person.count is Mary.count: True
#通過實例來賦值count屬性驶臊,相當于為實例新建了一個count屬性
>>Mary.count=1
>>print Mary.count,Person.count
1 0
>>print "Person.count is Mary.count: ", Person.count is Mary.count
Person.count is Mary.count: False
>>Person.count=3
>>print Mary.count,Person.count
1,3
#刪掉實例的count屬性
>>del Mary.count
>>print Mary.count,Person.count
3,3
>>print "Person.count is Mary.count: ", Person.count is Mary.count
Person.count is Mary.count: True
#刪除實例的count實例數(shù)據(jù)屬性后,通過類來修改count類屬性時叼丑,實例訪問和通過類訪問的值是一樣的
>>Person.count = 1
>>print Mary.count,Person.count
1,1
>>print "Person.count is Mary.count: ", Person.count is Mary.count
Person.count is Mary.count: True
b.可變類型的類數(shù)據(jù)屬性
當通過實例來賦值類數(shù)據(jù)屬性時关翎,相當于為實例新建一個實例屬性,此時通過類訪問此數(shù)據(jù)屬性和通過實例訪問此數(shù)據(jù)屬性是不同的;
通過實例來修改類數(shù)據(jù)屬性鸠信,實際上是修改實例的數(shù)據(jù)屬性指向的內(nèi)存地址,此時通過類來訪問數(shù)據(jù)屬性和通過實例來訪問數(shù)據(jù)屬性是相同的
>>print Person.books,Mary.books
['python'] ['python']
>>Person.books.append("R")
>>print Person.books,Mary.books
['python', 'R'] ['python', 'R']
#通過實例來修改類數(shù)據(jù)屬性books纵寝,相當于改變了Mary.books指向的內(nèi)存地址即Person.books
>>Mary.books.append("scala")
>>print Person.books,Mary.books
['python', 'R', 'scala'] ['python', 'R', 'scala']
#通過實例來賦值類數(shù)據(jù)屬性,相當于為實例新建了一個books屬性
>>Mary.books=["S"]
>>print Person.books,Mary.books
['python', 'R', 'scala'] ['S']
>>print "Person.books is Mary.books: ", Person.books is Mary.books
Person.books is Mary.books: False
#刪掉實例的books屬性
>>del Mary.books
>>print Person.books,Mary.books
['python', 'R', 'scala'] ['python', 'R', 'scala']
>>print "Person.books is Mary.books: ", Person.books is Mary.books
Person.books is Mary.books: True
#刪除實例的實例數(shù)據(jù)屬性count后星立,又和開始一樣
>>Mary.books.append("C#")
>>print Person.books,Mary.books
['python', 'R', 'scala', 'C#'] ['python', 'R', 'scala', 'C#']
>>print "Person.books is Mary.books: ", Person.books is Mary.books
Person.books is Mary.books: True
因此爽茴,盡管通過實例可以訪問類屬性葬凳,但是,不建議這么做室奏,最好還是通過類名來訪問類屬性火焰,從而避免屬性隱藏帶來的不必要麻煩。
(3)類不能訪問實例數(shù)據(jù)屬性--無論是類定義中已有的實例數(shù)據(jù)屬性還是新增的實例數(shù)據(jù)屬性
>>print Person.name #類定義中的實例數(shù)據(jù)屬性
AttributeError: type object 'Person' has no attribute 'name'
>>print Person.gender#實例新增的數(shù)據(jù)屬性
AttributeError: type object 'Person' has no attribute 'gender'
4.總結(jié)
對于類數(shù)據(jù)屬性和實例數(shù)據(jù)屬性胧沫,可以總結(jié)為:
1.類數(shù)據(jù)屬性屬于類本身昌简,可以通過類名進行訪問、修改绒怨、增加
2.類數(shù)據(jù)屬性也可以被類的所有實例訪問纯赎、修改,包括已有的和新增的南蹂,即類數(shù)據(jù)屬性可以被類和所有實例所共享犬金,但是通過實例訪問類數(shù)據(jù)屬性要注意可能會出現(xiàn)屬性“隱藏”的情況
3.實例數(shù)據(jù)屬性屬于實例本身,可以通過實例進行訪問碎紊、修改佑附、增加,但是增加的實例數(shù)據(jù)屬性只屬于該實例仗考,不能被其他實例訪問
4.實例數(shù)據(jù)屬性只能通過實例訪問音同,不能通過類訪問
三.方法
在一個類中,可能出現(xiàn)三種方法秃嗜,實例方法权均、靜態(tài)方法和類方法,下面來看看三種方法的不同锅锨。
1.實例方法
(1) 實例方法的第一個參數(shù)默認為"self"叽赊,這里"self"就代表這個類實例本身,通過"self"可以直接訪問實例的屬性 必搞。self不是一個關(guān)鍵字必指,而是約定的寫法。
(2) 實例方法只能通過類實例進行調(diào)用
將Person的定義改為以下形式:
class Person(object):
count = 0
books = []
def __init__(this, name, age):
this.name = name
this.age = age
def hi(this):
print this.name
>>Mary=Person("Mary",12)#用this代替self運行依然正確恕洲,其中init()是生成實例時默認調(diào)用的實例方法
>>print Mary.age
12
>>Mary.hi()
Mary
>>Person.hi()
TypeError: unbound method hi() must be called with Person instance as first argument (got nothing instead)
2.類方法
(1)類方法以cls作為第一個參數(shù)塔橡,cls表示類本身,通過cls可以訪問類的相關(guān)屬性霜第;
(2)定義時使用@classmethod裝飾器
(3)類方法既可以通過類名訪問葛家,也可以通過實例訪問;
class Person(object):
count = 0
books = []
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def printClassInfo(cls):
print cls.__name__
>>Person.printClassInfo()#通過類名訪問
Person
>>Mary= Person("Mary", 28)
>>Mary.printClassInfo()#通過實例訪問
Person
注;很多其他的編程語言不允許實例調(diào)用類方法泌类。
3.靜態(tài)方法
(1)與實例方法和類方法不同的是癞谒,靜態(tài)方法沒有參數(shù)限制,既不需要實例參數(shù),也不需要類參數(shù);
(2)定義的時候使用@staticmethod裝飾器弹砚;
(3)同類方法一樣双仍,靜態(tài)法可以通過類名訪問,也可以通過實例訪問迅栅;
(4)使用靜態(tài)方法的好處是殊校,不需要定義實例即可使用這個方法。另外读存,多個實例共享此靜態(tài)方法。
class Person(object):
count = 0
books = ["python"]
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def printClassInfo(cls):
print cls.__name__
@staticmethod
def printClassAttr():
print Person.count
print Person.books
#print self.name 靜態(tài)方法不能訪問類變量和實例變量
>>Person.printClassAttr()
0
['python']
>>Mary= Person("Mary", 28)
>>Mary.printClassAttr()
0
4.總結(jié)
這三種方法的區(qū)別在于:
1.實現(xiàn)靜態(tài)方法和類方法都是依賴于python的修飾器來實現(xiàn)的呕屎。對象方法有self參數(shù)让簿,類方法有cls參數(shù),靜態(tài)方法是不需要這些附加參數(shù)的秀睛。
2.類方法的隱含調(diào)用參數(shù)是類尔当,而類實例方法的隱含調(diào)用參數(shù)是類的實例,靜態(tài)方法沒有隱含調(diào)用參數(shù)
3.實例方法被綁定到一個實例蹂安,只能通過實例進行調(diào)用椭迎;但是對于靜態(tài)方法和類方法,可以通過類名和類實例兩種方式進行調(diào)用田盈。拋開靜態(tài)方法不說畜号,實例方法、類方法與實例數(shù)據(jù)屬性允瞧、類數(shù)據(jù)屬性之間的區(qū)別是一樣的简软,即實例的可以訪問類的,而類的不能訪問實例的;
四.訪問控制
Python中沒有訪問控制的關(guān)鍵字述暂,例如private痹升、protected等等。但是畦韭,在Python編碼中疼蛾,有一些約定來進行訪問控制。
1.單下劃線"_"
在Python中艺配,通過單下劃線來實現(xiàn)模塊級別的私有化察郁,一般約定以單下劃線開頭的變量、函數(shù)為模塊私有的妒挎,也就是說"from moduleName import *"將不會引入以單下劃線"_"開頭的變量绳锅、函數(shù)。
現(xiàn)在有一個模塊lib.py酝掩,內(nèi)容如下鳞芙,模塊中一個變量名和一個函數(shù)名分別以單下劃線開頭:
a= 10
_a = 100
def print():
print "a is:", a
print "_a is:", _a
def _print():
print "a is:", a
print "_a is:", _a
當通過下面代碼引入lib.py這個模塊后,所有的以"_"開頭的變量和函數(shù)都沒有被引入,如果訪問將會拋出異常:
>>from lib import *
>>print a
10
>>print()
a is: 10
_a is: 100
>>print _a
NameError: name '_a' is not defined
>>_print()
NameError: name '_print' is not defined
2.雙下劃線"__"
(1) Python中的類屬性原朝,可以通過雙下劃線"__"來實現(xiàn)一定程度的私有化
在Person類中驯嘱,加入了一個"__address"屬性:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
self.__address = "hangzhou"
>>Mary= Person("Mary", 28)
>>print Mary.__address
AttributeError: 'Person' object has no attribute '__address'
當通過實例Mary訪問這個屬性的時候,就會得到一個異常喳坠,提示屬性"__address"不存在鞠评。
下面我們通過內(nèi)建函數(shù)dir()就可以看到原因,"__address"屬性在運行時壕鹉,屬性名被改為了"_Person__address"(屬性名前增加了單下劃線和類名)
>>print dir(Mary)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'books', 'count', 'printClassAttr', 'printClassInfo']
['_Person__address', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'books', 'count', 'name', 'printClassAttr', 'printClassInfo']
所以說剃幌,即使是雙下劃線,也沒有實現(xiàn)屬性的完全的私有化晾浴,因為通過下面的方式還是可以直接訪問"__address"屬性:
>>> print Mary._Person__address
hangzhou
(2)雙下劃線的另一個作用是负乡,避免子類對父類同名屬性的沖突。**
看下面一個例子:
class A(object):
def __init__(self):
self.__private()
self.public()
def __private(self):
print 'A.__private()'
def public(self):
print 'A.public()'
class B(A):
def __private(self):
print 'B.__private()'
def public(self):
print 'B.public()'
b = B()
在父類中定義了一個私有方法脊凰,然后在子類中定義了一個一樣的私有方法是無效的,子類是不能重寫父類中的私有方法的
當實例化B的時候抖棘,由于沒有定義init函數(shù),將調(diào)用父類的init狸涌,但是由于雙下劃線的"混淆"效果切省,"self.__private()"將變成 "self._A__private()"。
3.總結(jié)
單下劃線和雙下劃線的使用更多的是一種規(guī)范/約定帕胆,并沒有真正達到限制的目的:
單下劃線開頭的表示的是protected類型的變量朝捆,即只能允許其本身與子類進行訪問;同時表示弱內(nèi)部變量標示惶楼,即當使用"from moduleNmae import *"時右蹦,不會將以一個下劃線開頭的對象引入
雙下劃線的表示的是私有類型的變量,只能是允許這個類本身進行訪問歼捐,連子類也不可以訪問何陆,這類屬性在運行時屬性名前面會加上單下劃線和類名。
五.總括
本文介紹了Python中類的一些基本點:
1.實例數(shù)據(jù)屬性和類數(shù)據(jù)屬性的特點和區(qū)別
2.實例方法豹储,類方法和靜態(tài)方法的特點和區(qū)別
3.Python中通過單下劃線和雙下劃線實現(xiàn)的訪問控制