類實際上就是一個數據結構,對於python而言,它是一個相似於字典的結構。當根據類建立了對象以後,這個對象就有了一個數據結構,包含一些賦值了的屬性。在這一點上,它和其它語言的struct的做用是相似的:存儲數據並提供數據檢索功能。java
例如,下面是史上最簡單的類:python
class Person: pass
pass關鍵字表示這個類沒有實際的邏輯體。這裏只是定義了一個類,這個類的對象初始化時不會存聽任何數據。如今,構造一個對象,讓它和dict同樣存放一些數據:數據結構
p = Person() # 構造對象 p.name = "longshuai" # 建立對象的屬性name p.age = 23 # 建立對象的屬性age
如今,Person的實例對象p中就存放了兩個屬性:p.name和p.age。能夠直接去檢索存放在p中的數據:函數
print(p.name) # 輸出"longshuai" print(p.age) # 輸出23
也可使用dict來存儲這些數據:學習
>>> d={} >>> d["name"]="longshuai" >>> d["age"]=23 >>> print(d["name"]) longshuai >>> print(d["age"]) 23
在數據存儲方面,它們的做用是徹底等價的。實際上對象/類在內部就是使用一個名爲__dict__
的dict類型來存放它所擁有的數據的。code
>>> p.__dict__ {'name': 'longshuai', 'age': 23}
上面的name和age屬性是在構建了對象以後附加上去的,若是想要建立對象時就存放好數據,能夠定義類的構造函數__init__()
。例如:對象
class Person: def __init__(self,name,age): self.name = name self.age = age
而後建立對象的時候,傳遞name參數和age參數便可。blog
>>> p = Person("longshuai",23) >>> p.__dict__ {'name': 'longshuai', 'age': 23}
若是想定義這個類公有的數據,能夠將公有屬性定義爲類的屬性。好比中國人都是黃皮膚:get
class Person: skin = "yellow" def __init__(self,name,age): self.name = name self.age = age
這樣每次建立Person的對象實例時,每一個對象都會有相同的膚色:yellow。但注意,這個skin屬性是類屬性,不是對象屬性,它是存放在類的名稱空間中的。當對象真的須要這個屬性的時候,會臨時去檢索類的名稱空間來獲取。看下面的__dict__
字典便可知道:it
>>> p = Person("longshuai",23) >>> p.__dict__ {'name': 'longshuai', 'age': 23} >>> p.skin 'yellow'
但注意,按照面向對象的封裝原則,在類中定義類變量屬性是不合理的,由於要在外部訪問它須要經過x.y
的方式,這意味着打開了封裝好的"黑匣子",暴露了屬性。除非真的有須要,不然能夠將類變量的定義放進構造函數__init__()
中,這樣每一個初始化的對象都會有該屬性。
在面向對象的角度上考慮,通常是不建議直接在類的外部經過x.name
的方式賦值、取值的。而是定義對應的方法,經過方法來取得對應的值。這兩類方法稱爲setter、getter方法:setter用於賦值或設置屬性值,getter用於取得屬性值。
class Person: skin = "yellow" def __init__(self,name,age): self.name = name self.age = age def set(self,job): self.job = job return self def get(self): return self.name,self.age,self.job
上面的set方法用於設置一個新屬性job。get用於返回對象的3個屬性。
>>> p = Person("longshuai",23) >>> p.set("manager") >>> name, age, job = p.get() >>> print([name,age,job]) ['longshuai', 23, 'manager']
須要注意,setter方法能夠有多種類型的返回值,經常使用的有4種:
這4種返回值都很常見,特別是第三種用來串聯對象方法的時候很是好用。修改上面的set方法:
class Person: skin = "yellow" def __init__(self,name,age): self.name = name self.age = age def set(self,job): self.job = job return self def get(self): return self.name,self.age,self.job
上面的set()返回self對象自身。因而串聯set()和get():
>>> p = Person("longshuai",23) >>> name,age,job = p.set("manager").get()
不管使用何種返回值方式,都不會真正影響程序的使用。但使用合理的返回值類型,可能會簡化代碼的編寫。另外,決定了返回值的方式後,就不要再去修改,由於極可能會牽一髮而動全身。
上面的getter返回了多個值,但通常來講getter只返回一個對應的屬性。好比getname()返回name屬性,getage()返回age屬性等。這樣須要定義多個getter方法。
def get_name(self): return self.name def get_age(self): return self.age def get_job(self): return self.job
不少時候能夠合併setter和getter方法。合併的方式是判斷方法的參數,若是調用方法的時候給了參數,就表示setter,沒有給定參數,就表示是getter。
例如,對於job屬性:
def set_get_job(self, job=None): if job: self.job = job else: return self.job
如今能夠以給參數和不給參數兩種不一樣的方式來調用set_get_job()方法:
p = Person("longshuai", 23) p.set_get_job("manager") # 給了參數,說明是setter job = p.set_get_job() # 沒給參數,說明是getter
上面解釋了各類setter、getter的方式,還解釋了將它們進行合併。
實際上在python中訪問、設置、刪除對象屬性的時候,大概有如下幾種方式:
getter()
、setter()
、deleter()
方法__getattr__()
、__setattr__()
、__delattr__()
運算符,這決定了x.y
的訪問、賦值方式以及del x.y
的方式__getattribute__()
方法這些還未介紹到的屬性管理方式,在後面的文章中會逐漸展開解釋。
本文介紹了各類設置對象屬性的方式,屬性其實就是數據,對象/類就是屬性的容器,這一點很重要。我最開始學java的面向對象時,雖然對類和對象有那些教科書式的理解,但始終沒有感覺到類/對象其實就是一種用來存儲數據的數據結構。直到學習了Python/Perl,我才意識到這一點,而後理解面向對象就容易的多了。