1. 程式人生 > >python面向對象編程 -- 基本概念(python3.5)

python面向對象編程 -- 基本概念(python3.5)

模塊 屬性和方法 class example 擴展 clas 3.5 通過 val

面向對象的編程簡要概括就是將要處理的問題抽象為數據和操作的集合,用對其進行封裝。其中數據和操作都稱為類的屬性,它們是一般是不變的。

對類進行實例化生成我們所說的對象,對象有自己的屬性。對象的屬性一般是個性化的,不同的對象可能具有不同的屬性。同一個類的所有對象都共享類的屬性。

對象屬性的查找順序為:對象自身 --> 類 --> 類的祖先類

在python中一切皆對象。

以下我們討論python中類相關的概念和語法。

1、類的定義

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>
  • 類定義必須使用class關鍵字
  • 類名最好使用大駝峰,如ClassName, 這是一種約定
  • 當輸入類定義時,一個新的命名空間產生,在類中定義的所有本地變量都放入這個新的命名空間。
  • 命名空間中的所有變量都是類的屬性。類屬性主要分為兩類,數據屬性(變量)和函數。
  • 語句塊執行完成後,生成一個類對象綁定到ClassName。ClassName的作用域由它的定義的位置決定。

python中一切皆對象,類也是對象,它是type類的實例。可以用 obj.__class__來查看一個對象是由那個類實例化而來的。

類中可以使用任何合法的語句,不過實際應用中,主要是賦值語句和函數定義。

2、類對象

下面的代碼定義了一個 Chinese 類:

class Chinese:
    """A sample example class"""
    nationality = China

    def display(self):
        print(self, I am Chinese!, sep=, )

for name, value in sorted(Chinese.__dict__.items(), key=lambda x: x[0]):
    print(name, ==>, value)

類定義會生成一個新的命名空間,其中包含了所有在類本地定義的變量,也就是類的屬性。這些屬性可以在類的 __dict__

屬性中查看,可以將其近似等價於類的命名空間。其中 __name__形式的屬性為特殊屬性。

print(type(Chinese.__dict__))   # <class ‘mappingproxy‘>
for name, value in sorted(Chinese.__dict__.items(), key=lambda x: x[0]):
    print(name, ==>, value)

# __dict__ ==> <attribute ‘__dict__‘ of ‘Chinese‘ objects>
# __doc__ ==> A sample example class
# __module__ ==> __main__
# __weakref__ ==> <attribute ‘__weakref__‘ of ‘Chinese‘ objects>
# display ==> <function Chinese.display at 0x1020759d8>
# nationality ==> China
  • __dict__ 在類定義的時候自動生成。
  • __doc__ 類定義的時候自動生成。值為類定義語句塊內第一行,類的文檔字符串,由三引號引用。如果沒有值為None。
  • __module__ 類定義所在的模塊,自動添加的屬性。如果是正在執行的模塊,值為 __main__
  • __weakref__
  • display 和 nationality 用戶定義屬性

python中屬性的引用都是通過obj.name 的形式,name為對象 obj 的屬性。類中的所有屬性都可以通過obj.name 的形式引用。比如該類的兩個用戶自定義屬性:數據屬性 nationality 和 函數 display:

print(Chinese.nationality)  # China
print(Chinese.display)      # <function Chinese.display at 0x1021759d8>
Chinese.display(1)          # 1, I am Chinese!

通過類引用函數並對其進行調用,與普通的函數調用沒有差異。不過,在類中定義的函數一般是給實例使用的,不建議直接通過類引用,下面會進一步說明。

3、類實例化

類的實例化使用和函數調用類似的語法。

xm = Chinese()

上面的語句創建了一個新的Chinese類的實例,並將其賦值給 xm 變量。實例有自己的命名空間:

print(xm.__dict__)  # {}

可以看到 xm.__dict__ 的返回值是一個空字典,實例 xm沒有自己的屬性。可以直接通過 xm.name = ‘xiaoming‘ 的形式為實例添加屬性。也可以通過 del xm.name 刪除該屬性:

xm.name = xiaoming
print(xm.__dict__)      # {‘name‘: ‘xiaoming‘}
del xm.name
print(xm.__dict__)      # {}

如果類的所有實例都具有某些屬性,只是屬性的值不同,可以通過定義實例的初始化方法,在方法中為實例添加屬性。

3.1 初始化方法

類的實例化過程:

  1. 實例化出一個對象
  2. 自動調用特殊方法 __init__對產生的對象初始化。如果類中沒有定義會在父類(父類的概念之後說明)中查找該方法。

我們稱__init__方法為實例的初始化方法。

一般在實例初始化方法中會定義實例的個性化屬性,這些屬性是實例私有的,可以通過實例的__dict__屬性查看。實例的屬性不會影響類的屬性,換句話說類不能引用實例的屬性。

下面是對Chinese類的擴展:

class Chinese:
    """A sample example class"""
    nationality = China

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def display(self):
        print(self, I am Chinese!, sep=, )


xm = Chinese(xiaoming, 18, male)
print(xm.__dict__)      # {‘name‘: ‘xiaoming‘, ‘gender‘: ‘male‘, ‘age‘: 18}
print(Chinese.__dict__.keys())
# dict_keys([‘__weakref__‘, ‘__module__‘, ‘__dict__‘, ‘__init__‘, ‘nationality‘, ‘display‘, ‘__doc__‘])

self指代引用該方法的實例(具體實現在4.1介紹),實例化操作的參數列表化傳遞給__init__方法,Chinese(‘xiaoming‘, 18, ‘male‘) --> xm.__init__(‘xiaoming‘, 18, ‘male‘)

3.2 實例的類

查看對象的類:

  • obj.__class__
  • type(obj)
print(xm.__class__)
print(type(xm))
# <class ‘__main__.Chinese‘>
# <class ‘__main__.Chinese‘>

判斷一個對象是不是某個類的實例:isinstance(obj, ClassName)

print(isinstance(xm, Chinese))
# True

4 實例

實例可以操作兩種屬性:數據屬性和方法

print(xm.name, xm.age, xm.gender, sep=, )   # xiaoming, 18, male
print(xm.nationality, xm.display, sep=, )   
# China, <bound method Chinese.display of <__main__.Chinese object at 0x10217c898>>

有上面的代碼可以看到實例不僅可以引用自己的屬性,也可以引用類的屬性。類屬性可以被它的實例共享

值得註意的是,實例對類的函數的引用。其返回值並不是一個函數對象而是一個"bound method"綁定的方法對象。嘗試調用這個對象:

1 xm.display(1)
2 # Traceback (most recent call last):
3 #   File "test.py", line 28, in <module>
4 #     xm.display(1)
5 # TypeError: display() takes 1 positional argument but 2 were given
6 xm.display()            # <__main__.Chinese object at 0x10217c7f0>, I am Chinese!

display函數要求傳入一個位置參數,第1行給方法傳入一個參數1,調用出錯,錯誤信息告訴我們多傳入了一個位置參數。

再次調用,不傳參,竟然能夠正常調用了!why?函數打印了傳入的值,"<__main__.Chinese object at 0x10217c7f0>",表明出入的是一個Chinese對象,在本例中只能是 xm。這是怎麽實現的呢?

4.1 實例方法

print(Chinese.display)
print(xm.display)
print(Chinese.display is xm.display)

# <function Chinese.display at 0x1021759d8>
# <bound method Chinese.display of <__main__.Chinese object at 0x10217c7f0>>
# False 

分別通過類和實例引用display屬性,返回的並不是同一個對象:通過類引用返回一個函數對象;通過實例引用返回一個"bound method"綁定的方法對象。

  • 方法:屬於某個對象的函數。當通過對象(實例)引用一個函數對象,函數和實例綁定返回一個方法對象。可以通過方法對象的__self__ 和 __func__ 屬性查看對應的實例和方法
    method = xm.display
    print(method.__self__)      # <__main__.Chinese object at 0x10217c7f0>
    print(method.__func__)      # <function Chinese.display at 0x1021759d8> 

  • 方法對象是可調用的,方法的調用即其對應的函數調用,特殊之處在於會將其實例作為第一個參數傳給函數,等價形式為:method(*args, **kwargs) ==> method.__func__(method.__self__, **args, **kwargs)。在本例中xm.display() 等價於Chinese.display(xm)。
  • 在類中定義的函數一般至少有一個位置參數,第一個位置參數一般用‘self‘標識,指代引用函數的實例。
  • 類中定義的函數對應到實例即為實例的方法。類中的函數一般都是通過實例引用的,為了便於描述下文中我們可能直接稱類中的函數為方法。

簡要概括來說就是,方法的第一個參數由python自動填充以引用實例對象

以上是相關的基本概念,下一篇討論類的封裝、繼承相關的概念。

python面向對象編程 -- 基本概念(python3.5)