構建塊

在本章中,我們將詳細討論面向對象的術語和編程概念。類只是一個實例的工廠。 該工廠包含描述如何製作實例的藍圖。 一個實例或對象是從該類構造而來的。 在大多數情況下,我們可以有一個以上的類實例。 每個實例都有一組屬性,這些屬性在一個類中定義,因此每個特定類的每個實例都應該具有相同的屬性。

類包:行爲和狀態

一個類將允許將對象的行爲和狀態捆綁在一起。 觀察下圖以更好地理解 -

構建塊

討論類包時,以下幾點需要注意 -

  • 行爲(behavior)與函數相同 - 它是一段執行某些操作(或實現行爲)的代碼,
  • 狀態(state)與變量相同 - 它是一個在類中存儲值的地方。
  • 當聲明一個類的行爲和狀態時,它是一個類包函數和變量。

類具有方法和屬性

在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類中的參數對象涉及類繼承,將在後面的章節中討論。 傳入上面的代碼表明這個塊是空的,也就是說它是一個空類定義。

讓我們創建一個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使我們能夠以下列方式隱藏對開發人員有利的對象內部工作的複雜性 -

  • 簡化並使得在不知道內部結構的情況下使用對象變得容易理解。
  • 任何更改都可以很容易地管理。

面向對象編程在很大程度上依賴於封裝。術語封裝和抽象(也稱爲數據隱藏)通常用作同義詞。 它們幾乎是同義詞,因爲抽象是通過封裝來實現的。

封裝提供了限制訪問某些對象組件的機制,這意味着對象的內部表示無法從對象定義的外部看到。 訪問這些數據通常是通過特殊方法來實現的 - GettersSetters

這些數據存儲在實例屬性中,可以在類以外的任何位置進行操作。 爲了保護它,只能使用實例方法訪問該數據。 不應允許直接訪問。

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__方法。這將初始化對象。

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'}