高階特性


在這裡我們來看看Python提供的一些高階功能。

類設計中的核心語法

在此將著眼於Python如何利用類中的操作符。 Python很大程度上是物件和方法呼叫物件,甚至當它隱藏在一些方便的語法中時,它甚至會繼續執行。

>>> var1 = 'Hello'
>>> var2 = ' World!'
>>> var1 + var2
'Hello World!'
>>>
>>> var1.__add__(var2)
'Hello World!'
>>> num1 = 45
>>> num2 = 60
>>> num1.__add__(num2)
105
>>> var3 = ['a', 'b']
>>> var4 = ['hello', ' John']
>>> var3.__add__(var4)
['a', 'b', 'hello', ' John']

所以如果我們必須將魔術方法__add__新增到自己的類中,是不是也可以這麼做。現在,我們可以試著去做。

假設有一個名為Sumlist的類,它有一個建構函式__init__,它將list作為名為my_list的引數。

class SumList(object):
   def __init__(self, my_list):
      self.mylist = my_list
   def __add__(self, other):
     new_list = [ x + y for x, y in zip(self.mylist, other.mylist)]

     return SumList(new_list)

   def __repr__(self):
      return str(self.mylist)

aa = SumList([3,6, 9, 12, 15])

bb = SumList([100, 200, 300, 400, 500])
cc = aa + bb # aa.__add__(bb)
print(cc) # should gives us a list ([103, 206, 309, 412, 515])

執行上面範例程式碼,得到以下結果 -

[103, 206, 309, 412, 515]

但有很多方法是由其他魔術方法內部管理的。 下面是其中的一些,

'abc' in var # var.__contains__('abc')
var == 'abc' # var.__eq__('abc')
var[1] # var.__getitem__(1)
var[1:3] # var.__getslice__(1, 3)
len(var) # var.__len__()
print(var) # var.__repr__()

繼承自內建的型別

類還可以從內建型別繼承,這意味著從任何內建繼承,並利用其中找到的所有功能。

在下面的例子中,從字典繼承,並實現它的一個方法__setitem__。 這個(setitem)在設定字典中的鍵和值時被呼叫。 因為這是一種神奇的方法,所以這將被隱式地呼叫。

class MyDict(dict):

   def __setitem__(self, key, val):
      print('setting a key and value!')
      dict.__setitem__(self, key, val)

dd = MyDict()
dd['a'] = 10
dd['b'] = 20

for key in dd.keys():
   print('{0} = {1}'.format(key, dd[key]))

執行上面範例程式碼,得到以下結果 -

setting a key and value!
setting a key and value!
a = 10
b = 20

繼續前面的例子,下面在處理列表索引時呼叫了兩個名為__getitem____setitem__的魔術方法。

# Mylist inherits from 'list' object but indexes from 1 instead for 0!
class Mylist(list): # inherits from list
   def __getitem__(self, index):
      if index == 0:
         raise IndexError
      if index > 0:
         index = index - 1
         return list.__getitem__(self, index) # this method is called when

# we access a value with subscript like x[1]
   def __setitem__(self, index, value):
      if index == 0:
         raise IndexError
      if index > 0:
      index = index - 1
      list.__setitem__(self, index, value)

x = Mylist(['a', 'b', 'c']) # __init__() inherited from builtin list

print(x) # __repr__() inherited from builtin list

x.append('HELLO'); # append() inherited from builtin list

print(x[1]) # 'a' (Mylist.__getitem__ cutomizes list superclass
               # method. index is 1, but reflects 0!

print (x[4]) # 'HELLO' (index is 4 but reflects 3!

執行上面範例程式碼,得到以下結果 -

['a', 'b', 'c']
a
HELLO

在上面的例子中,在Mylist中設定了三個專案列表,並隱式呼叫__init__方法,當列印元素x時,我們得到三個專案列表(['a','b','c'])。 然後在這個列表中追加另一個元素。 後來列印索引1和索引4。如果看到從(索引-1)中獲得所要求的元素。 我們知道,列表索引從0開始,但這裡索引從1開始(這就是為什麼要得到是列表的第一項)。

命名約定

在這裡,我們將檢視用於變數的名稱,特別是全域性Python程式員使用的私有變數和約定。 儘管變數被指定為私有,但在Python中並沒有隱私,並且這是按設計的。 和任何其他有良好記錄的語言一樣,Python具有它提出的命名和樣式約定,儘管它沒有強制執行它們。 有一個由「Guido van Rossum」編寫的Python風格指南,它描述了最佳實踐和名稱的使用,被稱為PEP8。 這裡是這個連結,https://www.python.org/dev/peps/pep-0008/

PEP代表Python增強提議,並且是一系列在Python社群中分發以討論提議的更改的文件。 例如,建議所有人,

  • 模組名稱 - all_lower_case
  • 類名稱和異常名稱 - CamelCase
  • 全域性和本地名稱 - all_lower_case
  • 函式和方法名稱 - all_lower_case
  • 常數 - ALL_UPPER_CASE

這些只是推薦,如果你喜歡,可以根據需要改變。 但是,由於大多數開發人員遵循這些建議,那麼可能程式碼很容易閱讀。

為什麼遵守慣例?

可以遵循允許獲得的PEP建議,

  • 對絕大多數開發人員更熟悉。
  • 大多數讀者更閱讀程式碼。
  • 將匹配在相同程式碼庫上工作的其他貢獻者的風格。
  • 註解是一名專業軟體開發人員必要的工作。
  • 每個人都會接受。

變數命名 - ‘公共’和’私人’

在Python中,當處理模組和類時,將一些變數或屬性指定為私有。 在Python中,除了在物件內部之外,不存在「私有」範例變數。 私有僅僅意味著它們根本不打算被程式碼的使用者使用,而是打算在內部使用。 一般來說,大多數Python開發人員都遵循一個約定,例如,名稱前面加上一個下劃線。_attrval(下面的範例)應該被視為API或任何Python程式碼的非公開部分,無論它是函式,方法還是資料成員。 以下是遵循的命名約定,

  • 公共屬性或變數(打算由此模組的匯入者或此類的使用者使用)- regular_lower_case
  • 私有屬性或變數(由模組或類內部使用)- _single_leading_underscore
  • 不應被子類化的私有屬性 - __ double_leading_underscore
  • 魔術屬性 - __ double_underscores __(使用它們,不要建立它們)
class GetSet(object):

   instance_count = 0 # public

   __mangled_name = 'no privacy!' # special variable

   def __init__(self, value):
      self._attrval = value # _attrval is for internal use only
      GetSet.instance_count += 1

   @property
   def var(self):
      print('Getting the "var" attribute')
      return self._attrval

   @var.setter
   def var(self, value):
      print('setting the "var" attribute')
      self._attrval = value

   @var.deleter
   def var(self):
      print('deleting the "var" attribute')
      self._attrval = None

cc = GetSet(5)
cc.var = 10 # public name
print(cc._attrval)
print(cc._GetSet__mangled_name)

執行上面範例程式碼,得到以下結果 -

setting the "var" attribute
10
no privacy!