歡迎光臨
我們一直在努力

Python物件導向–高階(一)

## 屬性的型別

– 屬性可分為類屬性和例項屬性

– 例項屬性可以通過在類中使用self定義,或者直接在類外部使用例項變數定義

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5 
 6 
 7 per1 = Person("Stanley", 22)
 8 print(per1.name)        # 輸出:Stanley
 9 per1.weight = "50kg"    
10 print(per1.weight)      # 輸出:50kg

– 類屬性則直接在類中定義

– 類屬性通過類名訪問,也可以通過例項訪問

 1 class Person(object):
 2     class_name = "Person"
 3 
 4     def __init__(self, name, age):
 5         self.name = name
 6         self.age = age
 7 
 8 
 9 per1 = Person("Stanley", 22)
10 print(per1.class_name)      # 輸出:Person
11 
12 print(Person.class_name)    # 輸出:Person

– 注意:

– 如果例項屬性和類屬性使用了相同的名字,則例項化後例項屬性將覆蓋類屬性,例項將無法在訪問該同名類屬性,而通過類名訪問類屬性將不受影響

 1 class Person(object):
 2     name = "Person"
 3 
 4     def __init__(self, name, age):
 5         self.name = name
 6         self.age = age
 7 
 8 
 9 print(Person.name)      # 輸出:Person
10 
11 per1 = Person("Stanley", 22)
12 print(per1.name)        # 輸出:Stanley
13 print(Person.name)      # 輸出:Person

## 方法的型別

– 在一個類中可以存在三種類型的方法

– 在類的定義中,以self(即例項本身)作為第一個引數的方法為例項方法

 1 class Person(object):
 2 
 3     def __init__(self, name, age):
 4         self.name = name
 5         self.age = age
 6 
 7     # 這是一個例項方法,self為例項本身,作為第一個引數傳入,通過例項呼叫
 8     def eat(self):
 9         print("I'm %s, I want to eat!" % self.name)
10 
11 
12 per1 = Person("Stanley", 22)
13 per1.eat()      # 輸出:I'm Stanley, I want to eat!

– 在類的內部定義,用修飾符@classmethod指定的方法是類方法

– 類方法會作用於整個類,該方法對類做出的改變會影響該類的所有例項

– 與例項方法類似,類方法的第一個引數是類本身,通常寫作cls

class Person(object):
    count = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Person.count += 1   # 每例項化一個物件,count加1

    # 這是一個例項方法,self作為第一個引數傳入,通過例項呼叫
    def eat(self):
        print("I'm %s, I want to eat!" % self.name)

    @classmethod        # 使用@classmethod修飾
    def kids(cls):      # 第一個引數為類本身,這是一個類方法
        print("class %s has %d instance(es)." % (cls.__name__, Person.count))


per1 = Person("Stanley", 22)
per2 = Person("Bob", 18)
per3 = Person("Lily", 17)
Person.kids()       # 輸出:class Person has 3 instance(es).

– 第三種方法型別稱為靜態方法

– 靜態方法使用@staticmethod修飾,它不需要self引數或者cls引數

– 靜態方法的存在不會影響類也不會影響類的例項,僅僅是爲了程式碼的邏輯性

– 靜態方法可以通過類名或者例項呼叫

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5 
 6     @staticmethod
 7     def say_hi():
 8         print("I'm a static method. I say Hi.")
 9 
10 
11 per1 = Person("Stanley", 22)
12 per1.say_hi()       # 輸出:I'm a static method. I say Hi.
13 Person.say_hi()     # 輸出:I'm a static method. I say Hi.

## 使用property對attribute進行訪問和設定

– 對於一些語言比如Java來說,對於私有屬性的訪問需要使用setter和getter方法

– Python不需要,因為Python所有屬性都是公開的,但是也可以編寫setter和getter限制對屬性的訪問

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.__name = name  # 私有屬性
 4         self.age = age
 5 
 6     def set_name(self, name):
 7         self.__name = name
 8 
 9     def get_name(self):
10         return self.__name
11 
12 
13 per1 = Person("Stanley", 22)
14 print(per1.get_name())      # 輸出:Stanley
15 per1.set_name("Lily")       # 修改屬性值
16 print(per1.get_name())      # 輸出:Lily
17 print(per1.__name)          # 無法直接訪問私有屬性,AttributeError: 'Person' object has no attribute '__name'

– 以上寫法達到了限制訪問的目的

– 但更Pythonic的寫法是使用property()

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.__name = name
 4         self.age = age
 5 
 6     def set_name(self, name):
 7         self.__name = name
 8 
 9     def get_name(self):
10         return self.__name
11 
12     # 使用property函式將getter和setter定義爲了name屬性
13     name = property(get_name, set_name)
14 
15 
16 per1 = Person("Stanley", 22)
17 print(per1.name)        # 訪問屬性時自動呼叫getter方法,輸出:Stanley
18 per1.name = "Lily"      # 設定屬性時自動呼叫setter方法
19 print(per1.name)        # 輸出:Lily
20 print(per1.get_name())  # 也可以顯式的呼叫getter或者setter方法

– 還有一種方法進行訪問限制,那麼就是使用修飾符,並定義兩個同名方法

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.__name = name
 4         self.age = age
 5 
 6     @property       # @property 用於指示getter方法
 7     def name(self): # 兩個同名函式
 8         return self.__name
 9 
10     @name.setter    # @name.setter 用於指示setter方法
11     def name(self, name):   # 兩個同名函式
12         if isinstance(name, str):
13             self.__name = name
14         else:
15             raise TypeError("Name must be string!")
16 
17 
18 per1 = Person("Stanley", 22)
19 print(per1.name)    # 輸出:Stanley
20 per1.name = "Lily"
21 print(per1.name)    # 輸出:Lily

– 使用了@property修飾的方法就變成了屬性

 1 import datetime
 2 
 3 
 4 class Person(object):
 5     def __init__(self, name, age):
 6         self.__name = name
 7         self.age = age
 8 
 9     @property
10     def birth_year(self):
11         return datetime.datetime.now().year - self.age
12 
13 
14 per1 = Person("Stanley", 22)
15 print(per1.birth_year)      # 輸出:1996
16 per1.birth_year = 2000      # 沒有指定setter屬性(@birth_year.setter),所以無法從外部對它的值進行設定,這使得這個birth_year這個屬性成為只讀屬性

本文參考:

[美]Bill Lubanovic 《Python語言及其應用》

未經允許不得轉載:頭條楓林網 » Python物件導向–高階(一)