類及其類屬性
1.屬性就是屬於另一個對象的數據或者函數元素,可以通過我們熟悉的句點屬性標示法來訪問。
2.類屬性僅與其被定義的類相綁定,由於實例對象在日常的面向對象編程中用得最多,實例數據屬性是你將會一直用到的主要數據屬性。
類數據屬性僅當需要更加“靜態”數據類型時才變得有用,他和任何實例都無關。
類的數據屬性
這種屬性是一種靜態變量,表示這些數據是與他們所屬的類對象綁定的,不依賴於任何類實例。
>>>
>>> class my(object):
... foo=100 #類屬性
...
>>> print my.foo
100
>>> my.foo=my.foo+1
>>> my.foo
101
>>>
以上代碼沒有出現任何實例的引用
方法
方法其實就是類中定義的函數,他是屬於類屬性的,並不屬於實例屬性。雖然他是類屬性,但是並不能直接通過類來調用。
>>> class my(object):
... def my_method(self):
... pass
...
>>> c=my()
>>> c.my_method() #通過實例調用
>>> my.my_method() #通過類直接調用這個方法
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method my_method() must be called with my instance as first a
rgument (got nothing instead)
>>>
爲什麼會出錯,明明方法也是類屬性,接下來解釋這個問題
綁定(綁定以及非綁定方法)爲了與oop慣例保持一致,python嚴格要求,沒有實例,方法是不能被調用的,這種限制即Python所描述的綁定概念,方法必須與實例綁定才能被直接被調用。 非綁定方法可能可以被調用,但實例對象一定要明確給出,才能確保調用成功。
dir
要知道一個類有哪些屬性,有兩種方法,最簡單的是使用dir()內建函數,另爲一個就是通過訪問類的字典屬性__dict__
,這是所有類都具備的特殊屬性之一。
>>> class myclass(object):
... myversion=1.0
... def show(self):
... print myclass.myversion
...
>>> dir(myclass)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribut
e__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_e
x__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_
_weakref__', 'myversion', 'show']
>>> myclass.__dict__
dict_proxy({'__module__': '__main__', 'show': <function show at 0x0000000002D51E
B8>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'myversion': 1.0,
'__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__doc__': None})
>>>
從上面的代碼可以看出,dir()返回的僅是對象的屬性的一個名字列表,可以打印類屬性,還可以打印所有的實例屬性,而__dict__
返回的是一個字典,他的鍵是屬性名,鍵值是相應的屬性對象的數據值。
特殊的類屬性
C.__name__ 類C的名字(字符串)
C.__doc__ 類C的文檔字符串
C.__bases__ 類C的所有父類構成的元組
C.__dict__ 類C的屬性
C.__module__ 類C定義所在的模塊
C.__class__ 實例C對應的類(僅新式類中)
實例
如果說類是一種數據結構定義的類型,那麼實例則聲明瞭一個這種類型的變量。
__init__()
“構造器”方法
當類被調用的時候,實例化的第一步就是創建實例對象,一旦對象創建了,Python檢查是否實現了__init__()
方法,默認情況下,如果沒有定義(或者覆蓋)特殊方法__init__()
,對實例不會施加任何特別操作,任何所需要的特定操作,都需要程序員實現__init__()
,覆蓋它的默認行爲。
如果__init__()
已經被實現 ,那麼它將被調用,實例對象作爲第一個參數(self)被傳遞進去,調用類的時候,傳進去的任何參數都交給了__init__()
。
__new__()
“構造器”方法
與__init__()
方法相比,__new__()
方法更像一個真正的構造器,python可以對內建類型進行派生,因此,需要一種途徑來實例化不可變對象,比如派生字符串,數字等
__new__()
相比與__init__()
方法,__new__()
必須返回一個合法的實例,這樣解釋器在調用__init__()
時,就可以把這個實例作爲self傳給他。簡單的說就是new調用了init
__call__
有了這個特殊方法,就可以像函數一樣調用實例了
class Test(object):
def __init__(self,name):
self.name=name
def __call__(self,age):
self.age=age
return "%s is %s"%(self.name,self.age)
t=Test('cmustard')
print t(23) #調用實例
"cmustard is 23"
實例屬性
實例僅擁有數據屬性(方法嚴格來說是類屬性),當一個實例被釋放後,他的屬性同時也被清除了。
構造器是最早設置實例屬性的地方,一旦__init__()
執行完畢,返回實例對象,即完成了實例化過程
查看實例屬性
>>> class my(object):
... pass
...
>>> c=my()
>>> c.foo='cmustard'
>>> c.bar=12312
>>> dir(c) #所有屬性,包括類屬性和實例屬性
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribut
e__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_e
x__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_
_weakref__', 'bar', 'foo']
>>> c.__dict__ #返回實例對象構成的字典
{'foo': 'cmustard', 'bar': 12312}
>>>
實例屬性和類屬性比較
類屬性僅是與類相關的數據值,和實例屬性不同,類屬性和實例屬性無關。類和實例都是名字空間,類是類屬性的名字空間,實例則是實例屬性的。我們可以採用類來訪問類屬性,如果實例沒有同名的屬性的話,你也可以用實例來訪問。
>>> class my(object):
... myversion=1
... def __init__(self,name):
... self.name=name
...
>>> class my(object):
... myversion=1
... def __init__(self,name):
... self.name=name
... def show(self,age):
... print "name is %s age is %s"%(self.name,age)
...
>>> c=my('cmustard')
>>> c.name #訪問實例屬性
'cmustard'
>>> c.myversion #訪問myversion,實例屬性中沒有,於是訪問的是類屬性中的變量
1
>>> c2=my("abner")
>>> c2.show(22) #訪問方法
name is abner age is 22
>>> c.show(18)
name is cmustard age is 18
>>> c2.myversion #訪問myversion,實例屬性中沒有,於是訪問的是類屬性中的變量
1
>>> my.myversion #通過類直接訪問類屬性
1
>>> c.myversion=100 #在實例c中修改myversion,相當與在實例c中動態添加了一個新的屬性
>>> c.myversion
100
>>> c2.myversion #在實例c2中myversion的值依然沒有改變
1
>>> my.myversion=150 #通過類直接修改myversion的值
>>> c.myversion #實例c中的值沒有發生改變,因爲實例c中已經有一個名叫myversion的變量了
100
>>> c2.myversion #實例c2中值隨着類的改變而改變,因爲實例c2中沒有myversion這個變量,只能訪問類屬性中的變量
150
>>> my.myversion
150
>>>
綁定和方法調用
方法只有在所屬的類擁有實例的時候才能被調用,當存在一個實例的時候,方法才被認爲是綁定到了那個實例,沒有實例時方法就是未綁定的
self是什麼:self變量用於在類實例方法中引用方法所綁定的實例,因爲在方法的實例在任何方法調用中總是作爲一個參數傳遞的,self被選中用來代替實例。
調用綁定方法
就是用實例調用這個方法
調用非綁定方法
調用非綁定方法並不經常用到,主要應用場景:你在派生一個子類 ,而且你要覆蓋父類的方法,這時你需要調用哪個父類中想要覆蓋的構造方法。
class Foo(object):
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
def show(self):
print "name is %s,sex is %s"%(self.name,self.sex)
class Bar(Foo):
def __init__(self,name,sex,age,id,salary):
super(Bar,self).__init__(name,sex,age) #這就是在調用父類非綁定方法
#或者Foo.__init__(self,name,sex,age) 現在不用了
self.id=id
self.salary=salary
def info(self):
print "name is %s salary is %s"%(self.name,self.salary)
>>> f=Bar('cmustard','F',22,123123,1000)
>>> f.show()
name is cmustard,sex is F
>>> f.info()
name is cmustard salary is 1000
>>>
靜態方法和類方法
靜態方法中默認是不能訪問類變量和實例變量。
類方法不能訪問實例變量
class TestS(object):
@staticmethod
def foo():
print "calling static method foo()"
class TestC(object):
@classmethod
def foo(cls):
print "calling class method foo()"
print "foo() is part of class",cls.__name__
>>> s=TestS()
>>> s.foo()
calling static method foo()
>>> TestS.foo() #直接調用靜態方法
calling static method foo()
>>>
>>>
>>> c=TestC()
>>> TestC().foo()
calling class method foo()
foo() is part of class TestC
>>> c.foo()
calling class method foo()
foo() is part of class TestC
>>>
property屬性
@property裝飾器是把方法轉變成屬性訪問,簡單來說就是實例訪問這個方法的時候,不需要加括號
#coding:utf-8
class TestP(object):
def __init__(self,name):
self.name=name
self.num=0
@property
def total(self):
return "is %s"%self.num
@total.setter #設置屬性的值
def total(self,num):
self.num=num
@total.deleter #刪除這個屬性
def total(self):
del self.num
p=TestP('cmustard')
print p.total #is 0
#怎麼給他賦值呢 需要setter裝飾器
p.total=10
print p.total #is 10
#怎麼刪除這個屬性
del p.total
print p.total #'TestP' object has no attribute 'num'
這個有什麼作用呢,我們還是可以直接改變num的值呀。。
p.num=100
print p.num #100
這時我們需要引用私有變量
class TestP(object):
def __init__(self,name):
self.name=name
self.__num=0 #設置了一個私有變量
@property
def total(self):
return "is %s"%self.__num
@total.setter #設置屬性的值
def total(self,num):
self.__num=num
@total.deleter #刪除這個屬性
def total(self):
del self.__num
p=TestP('cmustard')
p.total=100
print p.total #is 100
#倘若直接修改呢
p.__num=130
print p.__num #130
print p.__dict__ #{'_TestP__num': 100, 'num': 120, 'name': 'cmustard', '__num': 130},這裏只是在實例屬性中添加了一個新的變量
print p.total #is 100 還是沒有改變
#特殊情況
p._TestP__num=110
print p.total #is 110 這就改變了,不過我們一般不會這麼用的