面向物件 Python - 構建模組



在本章中,我們將詳細討論面向物件的術語和程式設計概念。類只是一個例項的工廠。這個工廠包含藍圖,描述瞭如何建立例項。例項或物件是從類構建的。在大多數情況下,我們可以擁有一個類的一個以上例項。每個例項都有一組屬性,這些屬性在類中定義,因此特定類的每個例項都預期具有相同的屬性。

類捆綁:行為和狀態

類允許您將物件的行為狀態捆綁在一起。觀察以下圖表以更好地理解 -

Bundles

在討論類捆綁時,以下幾點值得注意 -

  • 單詞行為函式相同 - 它是一段執行某些操作(或實現行為)的程式碼。

  • 單詞狀態變數相同 - 它是用於在類中儲存值的位置。

  • 當我們將類的行為和狀態一起斷言時,這意味著類打包了函式和變數。

類具有方法和屬性

在 Python 中,建立方法定義了類行為。方法一詞是在類中定義的函式的 OOP 名稱。總結一下 -

  • 類函式 - 是方法的同義詞

  • 類變數 - 是名稱屬性的同義詞。

  • - 具有精確行為的例項的藍圖。

  • 物件 - 類的例項之一,執行在類中定義的功能。

  • 型別 - 指示例項所屬的類

  • 屬性 - 任何物件值:object.attribute

  • 方法 - 在類中定義的“可呼叫屬性”

例如,觀察以下程式碼片段 -

var = “Hello, John”
print( type (var)) # ‘str’> or <class 'str'>
print(var.upper()) # upper() method is called, HELLO, JOHN

建立和例項化

以下程式碼展示瞭如何建立我們的第一個類及其例項。

class MyClass(object):
   pass
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj)

在這裡,我們建立了一個名為MyClass的類,它不執行任何任務。MyClass類中的引數object涉及類繼承,將在後面的章節中討論。上面程式碼中的pass表示此塊為空,即它是空的類定義。

讓我們建立一個MyClass()類的例項this_obj並列印它,如下所示 -

<__main__.MyClass object at 0x03B08E10>
<__main__.MyClass object at 0x0369D390>

在這裡,我們建立了MyClass的例項。十六進位制程式碼指的是儲存物件的地址。另一個例項指向另一個地址。

現在讓我們在類MyClass()中定義一個變數,並從該類的例項中獲取該變數,如下面的程式碼所示 -

class MyClass(object):
   var = 9

# Create first instance of MyClass
this_obj = MyClass()
print(this_obj.var)

# Another instance of MyClass

that_obj = MyClass()
print (that_obj.var)

輸出

當您執行上面給出的程式碼時,您可以觀察到以下輸出 -

9
9

由於例項知道它是從哪個類例項化的,因此當請求從例項獲取屬性時,例項會在該例項和類中查詢屬性。這稱為屬性查詢

例項方法

在類中定義的函式稱為方法。例項方法需要一個例項才能呼叫它,並且不需要裝飾器。建立例項方法時,第一個引數始終是self。雖然我們可以用任何其他名稱呼叫它(self),但建議使用self,因為它是命名約定。

class MyClass(object):
   var = 9
   def firstM(self):
      print("hello, World")
obj = MyClass()
print(obj.var)
obj.firstM()

輸出

當您執行上面給出的程式碼時,您可以觀察到以下輸出 -

9
hello, World

請注意,在上一個程式中,我們定義了一個以self作為引數的方法。但我們不能呼叫該方法,因為我們沒有宣告任何引數。

class MyClass(object):
   def firstM(self):
      print("hello, World")
      print(self)
obj = MyClass()
obj.firstM()
print(obj)

輸出

當您執行上面給出的程式碼時,您可以觀察到以下輸出 -

hello, World
<__main__.MyClass object at 0x036A8E10>
<__main__.MyClass object at 0x036A8E10>

封裝

封裝是 OOP 的基礎之一。OOP 使我們能夠隱藏物件內部工作原理的複雜性,這對開發人員以下列方式有利 -

  • 簡化並易於理解,無需瞭解內部細節即可使用物件。

  • 任何更改都可以輕鬆管理。

面向物件程式設計嚴重依賴封裝。封裝和抽象(也稱為資料隱藏)通常用作同義詞。它們幾乎是同義詞,因為抽象是透過封裝實現的。

封裝為我們提供了限制對某些物件元件的訪問的機制,這意味著無法從物件定義外部看到物件的內部表示。對這些資料的訪問通常是透過特殊方法實現的 - GetterSetter

這些資料儲存在例項屬性中,並且可以從類外部的任何位置進行操作。為了保護它,這些資料只能使用例項方法訪問。不應允許直接訪問。

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age

zack = MyClass()
zack.setAge(45)
print(zack.getAge())

zack.setAge("Fourty Five")
print(zack.getAge())

輸出

當您執行上面給出的程式碼時,您可以觀察到以下輸出 -

45
Fourty Five

只有在資料正確且有效時才應儲存資料,使用異常處理結構。正如我們上面看到的,對使用者輸入到 setAge() 方法沒有任何限制。它可以是字串、數字或列表。因此,我們需要檢查上面的程式碼以確保儲存資料的正確性。

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())

Init 建構函式

__init__ 方法在類物件例項化後立即隱式呼叫。這將初始化物件。

x = MyClass()

上面顯示的程式碼行將建立一個新例項並將此物件分配給區域性變數 x。

例項化操作,即呼叫類物件,會建立一個空物件。許多類喜歡建立具有自定義為特定初始狀態的例項的物件。因此,類可以定義一個名為“__init__()”的特殊方法,如下所示 -

def __init__(self):
   self.data = []

Python 在例項化期間呼叫 __init__ 以定義在例項化類時應出現的附加屬性,這可能是為該物件設定一些初始值或執行例項化時所需的例程。因此,在這個例子中,可以透過以下方式獲得一個新的初始化例項 -

x = MyClass()

__init__() 方法可以具有一個或多個引數,以獲得更大的靈活性。init 代表初始化,因為它初始化例項的屬性。它被稱為類的建構函式。

class myclass(object):
   def __init__(self,aaa, bbb):
      self.a = aaa
      self.b = bbb

x = myclass(4.5, 3)
print(x.a, x.b)

輸出

4.5 3

類屬性

在類中定義的屬性稱為“類屬性”,在函式中定義的屬性稱為“例項屬性”。在定義時,這些屬性沒有以 self 為字首,因為它們是類的屬性,而不是特定例項的屬性。

類屬性可以由類本身(className.attributeName)以及類的例項(inst.attributeName)訪問。因此,例項可以訪問例項屬性和類屬性。

>>> class myclass():
   age = 21
>>> myclass.age
21
>>> x = myclass()
>>> x.age
21
>>>

即使這不是破壞封裝的好方法,類屬性也可以在例項中被覆蓋。

Python 中有一個屬性查詢路徑。第一個是在類中定義的方法,然後是它上面的類。

>>> class myclass(object):
   classy = 'class value'
>>> dd = myclass()
>>> print (dd.classy) # This should return the string 'class value'
class value
>>>
>>> dd.classy = "Instance Value"
>>> print(dd.classy) # Return the string "Instance Value"
Instance Value
>>>
>>> # This will delete the value set for 'dd.classy' in the instance.
>>> del dd.classy
>>> >>> # Since the overriding attribute was deleted, this will print 'class
value'.

>>> print(dd.classy)
class value
>>>

我們正在例項 dd 中覆蓋“classy”類屬性。當它被覆蓋時,Python 直譯器讀取覆蓋的值。但是,一旦使用“del”刪除新值,該例項中就不再存在覆蓋的值,因此查詢向上移到上一級並從類中獲取它。

使用類和例項資料

在本節中,讓我們瞭解類資料如何與例項資料相關聯。我們可以在類或例項中儲存資料。當我們設計一個類時,我們決定哪些資料屬於例項,哪些資料應該儲存到整個類中。

例項可以訪問類資料。如果我們建立多個例項,則這些例項可以訪問其各自的屬性值以及整個類資料。

因此,類資料是在所有例項之間共享的資料。觀察下面給出的程式碼以更好地理解 -

class InstanceCounter(object):
   count = 0 # class attribute, will be accessible to all instances
   def __init__(self, val):
      self.val = val
      InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name
# In above line, class ('InstanceCounter') act as an object
   def set_val(self, newval):
      self.val = newval

   def get_val(self):
      return self.val

   def get_count(self):
      return InstanceCounter.count
a = InstanceCounter(9)
b = InstanceCounter(18)
c = InstanceCounter(27)

for obj in (a, b, c):
   print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27)
   print ('count: %s' %(obj.get_count())) # always 3

輸出

val of obj: 9
count: 3
val of obj: 18
count: 3
val of obj: 27
count: 3

簡而言之,類屬性對於類的所有例項都是相同的,而例項屬性對於每個例項都是特定的。對於兩個不同的例項,我們將有兩個不同的例項屬性。

class myClass:
   class_attribute = 99

   def class_method(self):
      self.instance_attribute = 'I am instance attribute'

print (myClass.__dict__)

輸出

當您執行上面給出的程式碼時,您可以觀察到以下輸出 -

{'__module__': '__main__', 'class_attribute': 99, 'class_method': , '__dict__': , '__weakref__': , '__doc__': None}

例項屬性myClass.__dict__如下所示 -

>>> a = myClass()
>>> a.class_method()
>>> print(a.__dict__)
{'instance_attribute': 'I am instance attribute'}
廣告