python面向對象編程基礎

1 面向對象編程的基本思想

類用來定義抽象類型
實例的一個類的具備實現
python中 
class Person:
    pass
數據封裝
class Person:
    def __init__(self, name,age )
        self.name = name
        self.age = age
p = Person('xiaoming',23)

2.類的定義與建立實例

在Python中,類經過 class 關鍵字定義。以 Person 爲例,定義一個Person類以下:python

class Person(object):
    pass

按照 Python 的編程習慣,類名以大寫字母開頭,緊接着是(object),表示該類是從哪一個類繼承下來的。類的繼承將在後面的章節講解,如今咱們只須要簡單地從object類繼承。編程

有了Person類的定義,就能夠建立出具體的xiaoming、xiaohong等實例。建立實例使用 類名+(),相似函數調用的形式建立:python2.7

xiaoming = Person()
xiaohong = Person()

3. python中建立實例屬性

雖然能夠經過Person類建立出xiaoming、xiaohong等實例,可是這些實例看上除了地址不一樣外,沒有什麼其餘不一樣。在現實世界中,區分xiaoming、xiaohong要依靠他們各自的名字、性別、生日等屬性。函數

如何讓每一個實例擁有各自不一樣的屬性?因爲Python是動態語言,對每個實例,均可以直接給他們的屬性賦值,例如,給xiaoming這個實例加上name、gender和birth屬性:spa

xiaoming = Person()
xiaoming.name = 'Xiao Ming'
xiaoming.gender = 'Male'
xiaoming.birth = '1990-1-1'

給xiaohong加上的屬性不必定要和xiaoming相同:code

xiaohong = Person()
xiaohong.name = 'Xiao Hong'
xiaohong.school = 'No. 1 High School'
xiaohong.grade = 2

實例的屬性能夠像普通變量同樣進行操做:orm

xiaohong.grade = xiaohong.grade + 1

任務

請建立包含兩個 Person 類的實例的 list,並給兩個實例的 name 賦值,而後按照 name 進行排序。對象

 1 #python 2.7
 2 class Person(object):
 3     pass
 4 
 5 p1 =Person()
 6 p1.name ='Bart'
 7 p2 =Person()
 8 p2.name ='Adam'
 9 p3 =Person()
10 p3.name ='Lisa'
11 L1 =[p1, p2, p3]
12 L2 = sorted(L1,key=lambda x: x.name)
13 print L2[0].name
14 print L2[1].name
15 print L2[2].name

 

輸出blog

Adam
Bart
Lisa排序

4.python中初始化實例屬性

雖然咱們能夠自由地給一個實例綁定各類屬性,可是,現實世界中,一種類型的實例應該擁有相同名字的屬性。例如,Person類應該在建立的時候就擁有 name、gender 和 birth 屬性,怎麼辦?

在定義 Person 類時,能夠爲Person類添加一個特殊的__init__()方法,當建立實例時,__init__()方法被自動調用,咱們就能在此爲每一個實例都統一加上如下屬性:

1 class Person(object):
2     def __init__(self, name, gender, birth):
3         self.name = name
4         self.gender = gender
5         self.birth = birth

__init__() 方法的第一個參數必須是 self(也能夠用別的名字,但建議使用習慣用法),後續參數則能夠自由指定,和定義函數沒有任何區別。

相應地,建立實例時,就必需要提供除 self 之外的參數:

xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')
xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')

有了__init__()方法,每一個Person實例在建立時,都會有 name、gender 和 birth 這3個屬性,而且,被賦予不一樣的屬性值,訪問屬性使用.操做符:

print xiaoming.name
# 輸出 'Xiao Ming'
print xiaohong.birth
# 輸出 '1992-2-2'

要特別注意的是,初學者定義__init__()方法經常忘記了 self 參數:

>>> class Person(object):
...     def __init__(name, gender, birth):
...         pass
... 
>>> xiaoming = Person('Xiao Ming', 'Male', '1990-1-1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 3 arguments (4 given)

這會致使建立失敗或運行不正常,由於第一個參數name被Python解釋器傳入了實例的引用,從而致使整個方法的調用參數位置所有沒有對上。

任務

請定義Person類的__init__方法,除了接受 name、gender 和 birth 外,還可接受任意關鍵字參數,並把他們都做爲屬性賦值給實例。

 1 #python2.7
 2 class Person(object):
 3     def __init__(self,name,gender,birth,**kw):
 4         self.name = name
 5         self.gender = gender
 6         for k,w in kw.items():
 7             setattr(self,k,w)
 8         
 9 xiaoming =Person('xiao ming','Male','1990-1-1', job='Student')
10 print xiaoming.name
11 print xiaoming.job

輸出

xiao ming
Student

5. python中訪問限制 

咱們能夠給一個實例綁定不少屬性,若是有些屬性不但願被外部訪問到怎麼辦?
Python對屬性權限的控制是經過屬性名來實現的,若是一個屬性由雙下劃線開頭(__),該屬性就沒法被外部訪問。看例子:
class Person(object):
    def __init__(self, name):
        self.name = name
        self._title = 'Mr'
        self.__job = 'Student'
p = Person('Bob')
print p.name
# => Bob
print p._title
# => Mr
print p.__job
# => Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute '__job'

可見,只有以雙下劃線開頭的"__job"不能直接被外部訪問。

可是,若是一個屬性以"__xxx__"的形式定義,那它又能夠被外部訪問了,以"__xxx__"定義的屬性在Python的類中被稱爲特殊屬性,有不少預約義的特殊屬性可使用,一般咱們不要把普通屬性用"__xxx__"定義。

以單下劃線開頭的屬性"_xxx"雖然也能夠被外部訪問,可是,按照習慣,他們不該該被外部訪問。

任務

請給Person類的__init__方法中添加name和score參數,並把score綁定到__score屬性上,看看外部是否能訪問到。

 1 #python2.7
 2 class Person(object):
 3     def __init__(self, name, score):
 4         self.name = name
 5         self.__score = score
 6     def __str__(self):
 7         return str(self.__score)
 8 
 9 p =Person('Bob',59)
10 print p.name
11 print p
12 print p.__score

輸出

Bob
59
Traceback (most recent call last):
File "test.py", line 12, in <module>
print p.__score
AttributeError: 'Person' object has no attribute '__score'

6. python中建立類屬性

類是模板,而實例則是根據類建立的對象。

綁定在一個實例上的屬性不會影響其餘實例,可是,類自己也是一個對象,若是在類上綁定一個屬性,則全部實例均可以訪問類的屬性,而且,全部實例訪問的類屬性都是同一個!也就是說,實例屬性每一個實例各自擁有,互相獨立,而類屬性有且只有一份。

定義類屬性能夠直接在 class 中定義:

class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

由於類屬性是直接綁定在類上的,因此,訪問類屬性不須要建立實例,就能夠直接訪問:

print Person.address
# => Earth

對一個實例調用類的屬性也是能夠訪問的,全部實例均可以訪問到它所屬的類的屬性:

p1 = Person('Bob')
p2 = Person('Alice')
print p1.address
# => Earth
print p2.address
# => Earth

因爲Python是動態語言,類屬性也是能夠動態添加和修改的:

Person.address = 'China'
print p1.address
# => 'China'
print p2.address
# => 'China'

由於類屬性只有一份,因此,當Person類的address改變時,全部實例訪問到的類屬性都改變了。

任務

請給 Person 類添加一個類屬性 count,每建立一個實例,count 屬性就加 1,這樣就能夠統計出一共建立了多少個 Person 的實例。

 1 #python 2.7
 2 class Person(object):
 3     count =0
 4     def __init__(self,name):
 5         self.name = name
 6 
 7 p1 =Person('Bob')
 8 Person.count +=1
 9 print Person.count
10 p2 =Person('Alice')
11 Person.count +=1
12 print Person.count
13 p3 =Person('Tim')
14 Person.count +=1
15 print Person.count

輸出

1
2
3

7. python中類屬性和實例屬性名字衝突怎麼辦

 

修改類屬性會致使全部實例訪 問到的類屬性所有都受影響,可是,若是在實例變量上修改類屬性會發生什麼問題呢?

class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

p1 = Person('Bob')
p2 = Person('Alice')

print 'Person.address = ' + Person.address

p1.address = 'China'
print 'p1.address = ' + p1.address

print 'Person.address = ' + Person.address
print 'p2.address = ' + p2.address

結果以下:

Person.address = Earth
p1.address = China
Person.address = Earth
p2.address = Earth

咱們發現,在設置了 p1.address = 'China' 後,p1訪問 address 確實變成了 'China',可是,Person.address和p2.address仍然是'Earch',怎麼回事?

緣由是 p1.address = 'China'並無改變 Person 的 address,而是給 p1這個實例綁定了實例屬性address ,對p1來講,它有一個實例屬性address(值是'China'),而它所屬的類Person也有一個類屬性address,因此:

訪問 p1.address 時,優先查找實例屬性,返回'China'。

訪問 p2.address 時,p2沒有實例屬性address,可是有類屬性address,所以返回'Earth'。

可見,當實例屬性和類屬性重名時,實例屬性優先級高,它將屏蔽掉對類屬性的訪問。

當咱們把 p1 的 address 實例屬性刪除後,訪問 p1.address 就又返回類屬性的值 'Earth'了:

del p1.address
print p1.address
# => Earth

可見,千萬不要在實例上修改類屬性,它實際上並無修改類屬性,而是給實例綁定了一個實例屬性。

任務

請把上節的 Person 類屬性 count 改成 __count,再試試可否從實例和類訪問該屬性。

1 class Person(object):
2     __count =0
3     def __init__(self, name):
4         Person.__count +=1
5         self.name = name
6 
7 p1 =Person('Bob')
8 p2 =Person('Alice')
9 print Person.__count

輸出

Traceback (most recent call last):
File "test.py", line 9, in <module>
print Person.__count
AttributeError: type object 'Person' has no attribute '__count'

 

 

8. python中定義實例方法

一個實例的私有屬性就是以__開頭的屬性,沒法被外部訪問,那這些屬性定義有什麼用?

雖然私有屬性沒法從外部訪問,可是,從類的內部是能夠訪問的。除了能夠定義實例的屬性外,還能夠定義實例的方法。

實例的方法就是在類中定義的函數,它的第一個參數永遠是 self,指向調用該方法的實例自己,其餘參數和一個普通函數是徹底同樣的:

class Person(object):

    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

get_name(self) 就是一個實例方法,它的第一個參數是self。__init__(self, name)其實也可看作是一個特殊的實例方法。

調用實例方法必須在實例上調用:

p1 = Person('Bob')
print p1.get_name()  # self不須要顯式傳入
# => Bob

在實例方法內部,能夠訪問全部實例屬性,這樣,若是外部須要訪問私有屬性,能夠經過方法調用得到,這種數據封裝的形式除了能保護內部數據一致性外,還能夠簡化外部調用的難度。

任務

請給 Person 類增長一個私有屬性 __score,表示分數,再增長一個實例方法 get_grade(),能根據 __score 的值分別返回 A-優秀, B-及格, C-不及格三檔。

 1 #python 2.7
 2 class Person(object):
 3     def __init__(self, name, score):
 4         self.name = name
 5         self.__score = score
 6     def get_grade(self):
 7         if self.__score >=85:
 8             return'A'
 9         elif self.__score >=60:
10             return'B'
11         else:
12             return'C'
13             
14 p1 =Person('Bob',90)
15 p2 =Person('Alice',65)
16 p3 =Person('Tim',48)
17 print p1.get_grade()
18 print p2.get_grade()
19 print p3.get_grade()

輸出

A
B
C

9. python中方法也是屬性

咱們在 class 中定義的實例方法其實也是屬性,它其實是一個函數對象:

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        return 'A'

p1 = Person('Bob', 90)
print p1.get_grade
# => <bound method Person.get_grade of <__main__.Person object at 0x109e58510>>
print p1.get_grade()
# => A

也就是說,p1.get_grade 返回的是一個函數對象,但這個函數是一個綁定到實例的函數,p1.get_grade() 纔是方法調用。

由於方法也是一個屬性,因此,它也能夠動態地添加到實例上,只是須要用 types.MethodType() 把一個函數變爲一個方法:

import types
def fn_get_grade(self):
    if self.score >= 80:
        return 'A'
    if self.score >= 60:
        return 'B'
    return 'C'

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

p1 = Person('Bob', 90)
p1.get_grade = types.MethodType(fn_get_grade, p1, Person)
print p1.get_grade()
# => A
p2 = Person('Alice', 65)
print p2.get_grade()
# ERROR: AttributeError: 'Person' object has no attribute 'get_grade'
# 由於p2實例並無綁定get_grade

給一個實例動態添加方法並不常見,直接在class中定義要更直觀。

任務

因爲屬性能夠是普通的值對象,如 str,int 等,也能夠是方法,還能夠是函數,你們看看下面代碼的運行結果,請想想 p1.get_grade 爲何是函數而不是方法:

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
        self.get_grade = lambda: 'A'

p1 = Person('Bob', 90)
print p1.get_grade
print p1.get_grade()
結果
<function <lambda> at 0x029F9AF0>
A
4.10 python中定義類方法

和屬性相似,方法也分實例方法類方法

class中定義的所有是實例方法,實例方法第一個參數 self 是實例自己。

要在class中定義類方法,須要這麼寫:

class Person(object):
    count = 0
    @classmethod
    def how_many(cls):
        return cls.count
    def __init__(self, name):
        self.name = name
        Person.count = Person.count + 1

print Person.how_many()
p1 = Person('Bob')
print Person.how_many()

經過標記一個 @classmethod,該方法將綁定到 Person 類上,而非類的實例。類方法的第一個參數將傳入類自己,一般將參數名命名爲 cls,上面的 cls.count 實際上至關於 Person.count

由於是在類上調用,而非實例上調用,所以類方法沒法得到任何實例變量,只能得到類的引用。

任務

若是將類屬性 count 改成私有屬性__count,則外部沒法讀取__score,但能夠經過一個類方法獲取,請編寫類方法得到__count值。

 1 #python 2.7
 2 class Person(object):
 3     __count =0
 4     @classmethod
 5     def how_many(cls):
 6         return cls.__count
 7     def __init__(self,name):
 8         self.name = name
 9         Person.__count +=1
10 
11 print Person.how_many()
12 p1 =Person('Bob')
13 print Person.how_many()

輸出

01

相關文章
相關標籤/搜索