1. 程式人生 > >零基礎學習 Python 之類的例項

零基礎學習 Python 之類的例項

寫在之前

昨天寫了類屬性,作為不分家的小夥伴,今天當然是來說說 “例項”。我在之前的文章中說過,類是物件的定義,例項才是真實的東西。比如 “人” 是一個類,但是 “人” 終究不是具體的某個會喘氣的,只有 “rocky” 才是具體的東西,但他是具有 “人” 這個類所定義的屬性和方法。“rocky” 就是 “人” 這個類的例項。

建立例項

建立例項並不是很難的事情,只需要呼叫類就可以實現:

>>> class man():
...     sex = '男'
...
>>> rocky = man()
>>> rocky
<__main__.man instance at 0x00000000004F3688>

如果不是用很嚴格的說法的話,上面的這個例子就是建立了一個例項 rocky。這裡有一點需要我們注意的是,呼叫類的方法和呼叫類的函式類似,如果僅僅是寫 man() 的話,則是建立了一個例項:

>>> man()
<__main__.man instance at 0x0000000002577D88>

而 rocky = man() 本質上是將變數 rocky 與例項物件 man() 建立引用關係,這種關係就如同我們在剛開始的時候學的賦值語句 x = 1 是同樣的效果。

那麼對於一個例項來說這個建立的過程是怎麼進行的呢?我們繼續來看:

class Person:
  """
  具有通常類的結構的 Person 類
  """
  def __init__(self,name):
      self.name = name

  def get_name(self):
      return self.name

  def get_sex(self,sex):
      per_sex = {}
      per_sex[self.name] = sex
      return per_sex

例項我們用 boy = Person(‘rocky’) ,當然了,在這裡你可以建立很多個例項,還記得那句話麼:類是例項的工廠。

當我們建立完例項,接下來就是呼叫類,當類被呼叫以後,先是建立一個例項物件,然後檢查是否有 init(),如果有的話就呼叫這個方法,並且將例項物件作為第一個引數 self 傳進去,如果沒有的話,就只是返回例項物件。

我之前也說過,init() 作為一個方法是比較特殊的,在它裡面,一般是規定一些屬性或者做一些初始化,讓類具有一些基本的屬性,但是它沒有 return 語句,這是 init() 區別於一般方法的地方:

>>> class fun:
...    def __init__(self):
...            print('this is init()')
...            return 1
...
>>> f = fun()
this is init()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: __init__() should return None

上面的執行結果出現了異常,並且明確說明了 “init() should return None”,所以不能有 return,如果非要帶上的話,只能是 return None,索性就不要寫了。

由此可知對於 init() ,除了第一個引數必須是 self,還要求不能有 return 語句,其他方面和普通函式就沒有什麼區別了。比如引數和裡面的屬性,你就可以像下面這樣來做:

>>> class Person:
...     def __init__(self,name,sex = '男',age = 10):
...             self.name = name
...             self.sex = sex
...             self.age = age
...

例項我們建立好了以後,我們接下來就要研究例項的內容,首先來看的是例項屬性。

例項屬性

和類屬性相似,例項所具有的屬性叫做 “例項屬性”:

>>> class A:
...     x = 1
...
>>> f = A()

類已經有了一個屬性 A.x = 1,那麼由類所建立的例項也應當具有這個屬性:

>>> f.x
1

除了 f.x 這個屬性以外,例項也具有其它的屬性和方法,我們依然用 dir 方法來看:

>>> dir(f)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x']

例項屬性和類屬性最主要的不同是在於,例項屬性可以隨意的更改:

>>> f.x += 10
>>> f.x
11

上面就是把例項屬性修改了,但是類屬性並沒有因為例項屬性的修改而發生變化,正如我們在前幾天的文章中所說的那樣,類屬性是與類捆綁的,不受例項的影響。

>>> A.x
1

上述的結果正好印證了這一點 – 類屬性不因例項屬性改變而改變。既然如此,那麼 f.x += 10 又改變了什麼呢?

其實就是例項 f 又重新建立了一個新的屬性,但是這個新的屬性和原先舊的屬性是一個名字,都是 f.x,所以相當於原先舊的屬性被 “掩蓋”了,只能訪問到新的屬性,所以值是 11。

>>> f.x
11
>>> del f.x
>>> f.x
1

由上面的例子可以看出,既然新的 f.x “掩蓋”了舊的 f.x,只要把新的 f.x 刪除,舊的 f.x 就可以顯現出來。

例項的改變不會影響到類,但是類屬性可以影響到例項屬性,因為例項就是通過呼叫類來建立的:

>>> A.x += 10
>>> A.x
11
>>> f.x
11

如果是同一個屬性 x,那麼例項屬性跟著類屬性的改變而改變,當然,這個是針對於像字串這種不可變物件而言的,對於類中如果引用的是可變物件的資料,則情形會有所不同,因為可變物件的資料是可以原地進行修改的:

>>> class B:
...     y = [1,2,3,4]
...
>>> B.y #類屬性
[1, 2, 3, 4]
>>> f = B()
>>> f.y #例項屬性
[1, 2, 3, 4]
>>> B.y.append('5')
>>> B.y
[1, 2, 3, 4, '5']
>>> f.y
[1, 2, 3, 4, '5']
>>> f.y.append('66')
>>> B.y
[1, 2, 3, 4, '5', '66']
>>> f.y
[1, 2, 3, 4, '5', '66']

通過上面的程式碼我們可以看出,當類中的變數引用的是可變物件的時候,類屬性和例項屬性都能夠直接修改這個物件,從而增加另一方的值。

還有一點我們已經知道了增加一個類屬性,相應的例項屬性也會增加,但是反過來就不成立了:

>>> B.x = 'aa'
>>> f.x
'aa'
>>> f.z = 'abcd'
>>> f.z
'abcd'
>>> B.z
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
AttributeError: class B has no attribute 'z'

可以看出類並沒有接納例項例項增加的屬性。

寫在之後

更多內容,歡迎關注公眾號「Python空間」,期待和你的交流。
在這裡插入圖片描述