構建塊


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

類包:行為和狀態

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

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

  • 行為(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'}