翻譯:《實用的Python程式設計》04_03_Special_methods

codists發表於2021-03-09

目錄 | 上一節 (4.2 繼承) | 下一節 (4.4 異常)

4.3 特殊方法

可以通過特殊方法(或者稱為"魔術"方法(magic method))自定義 Python 行為的各部分。本節介紹特殊方法的思想。此外,還將討論動態屬性訪問和繫結方法。

簡介

類可以定義特殊方法。特殊方法對於 Python 直譯器而言具有特殊的意義。特殊方法總是以雙下劃線 __ 開頭和結尾,例如 __init__

class Stock(object):
    def __init__(self):
        ...
    def __repr__(self):
        ...

雖然有很多特殊方法,但是我們只研究幾個具體的例子。

字串轉換的特殊方法

物件有兩種字串表示形式。

>>> from datetime import date
>>> d = date(2012, 12, 21)
>>> print(d)
2012-12-21
>>> d
datetime.date(2012, 12, 21)
>>>

str() 函式用於建立格式良好的、可列印的輸出:

>>> str(d)
'2012-12-21'
>>>

repr() 函式用於建立詳細的、面向程式設計師的輸出。

>>> repr(d)
'datetime.date(2012, 12, 21)'
>>>

str()repr() 函式都是使用類中定義的特殊方法生成要顯示的字串。

class Date(object):
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    # Used with `str()`
    def __str__(self):
        return f'{self.year}-{self.month}-{self.day}'

    # Used with `repr()`
    def __repr__(self):
        return f'Date({self.year},{self.month},{self.day})'

注意:按照慣例,__repr__() 返回一個字串,當該字串被傳遞給 eval() 函式,將會重新建立底層物件(譯註:eval(repr(obj)) == obj)。如果不行,則使用某種易於閱讀的表現形式。

數學操作的特殊方法

數學運算子涉及的特殊方法如下:

a + b       a.__add__(b)
a - b       a.__sub__(b)
a * b       a.__mul__(b)
a / b       a.__truediv__(b)
a // b      a.__floordiv__(b)
a % b       a.__mod__(b)
a << b      a.__lshift__(b)
a >> b      a.__rshift__(b)
a & b       a.__and__(b)
a | b       a.__or__(b)
a ^ b       a.__xor__(b)
a ** b      a.__pow__(b)
-a          a.__neg__()
~a          a.__invert__()
abs(a)      a.__abs__()

元素訪問的特殊方法

這些是實現容器的特殊方法:

len(x)      x.__len__()
x[a]        x.__getitem__(a)
x[a] = v    x.__setitem__(a,v)
del x[a]    x.__delitem__(a)

你可以在類中使用這些特殊方法。

class Sequence:
    def __len__(self):
        ...
    def __getitem__(self,a):
        ...
    def __setitem__(self,a,v):
        ...
    def __delitem__(self,a):
        ...

方法呼叫

呼叫方法有兩個步驟。

​ 1、查詢:. 運算子

​ 2、方法呼叫: () 運算子

>>> s = Stock('GOOG',100,490.10)
>>> c = s.cost  # Lookup
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c()         # Method call
49010.0
>>>

繫結方法

尚未被函式呼叫運算子 () 呼叫的方法稱為繫結方法( 譯註:bound method,如果直譯應該譯作“繫結的方法”,但是,就像“類方法”一樣,可以省略“的”這個字,譯為“繫結方法”,繫結在這裡不是動詞,而應理解為形容詞“繫結的”)。它對自己生成的例項進行操作:

>>> s = Stock('GOOG', 100, 490.10)
>>> s
<Stock object at 0x590d0>
>>> c = s.cost
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c()
49010.0
>>>

如果使用繫結方法時有些大意,那麼容易導致錯誤。示例:

>>> s = Stock('GOOG', 100, 490.10)
>>> print('Cost : %0.2f' % s.cost)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: float argument required
>>>

或者:

f = open(filename, 'w')
...
f.close     # Oops, Didn't do anything at all. `f` still open.

在這兩種情形中,錯誤都是由忘記尾部括號引起的。例如:s.cost() or f.close()

屬性訪問

還有一種訪問、操作和管理屬性的替代方法。

getattr(obj, 'name')          # Same as obj.name
setattr(obj, 'name', value)   # Same as obj.name = value
delattr(obj, 'name')          # Same as del obj.name
hasattr(obj, 'name')          # Tests if attribute exists

示例:

if hasattr(obj, 'x'):
    x = getattr(obj, 'x'):
else:
    x = None

注意: getattr() 函式的預設引數非常有用。

x = getattr(obj, 'x', None)

練習

練習 4.9:更好的輸出

請修改 stock.py 檔案中定義的 Stock 物件,以便 __repr__() 方法生成更有用的輸出。示例:

>>> goog = Stock('GOOG', 100, 490.1)
>>> goog
Stock('GOOG', 100, 490.1)
>>>

修改完成後,請檢視讀取股票投資組合時會發生什麼,以及生成什麼樣的結果。示例:

>>> import report
>>> portfolio = report.read_portfolio('Data/portfolio.csv')
>>> portfolio
... see what the output is ...
>>>

練習 4.10:使用 getattr() 的例子

getattr() 是讀取屬性的另一種機制。可以使用它編寫極其靈活的程式碼。首先,請嘗試以下示例:

>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.1)
>>> columns = ['name', 'shares']
>>> for colname in columns:
        print(colname, '=', getattr(s, colname))

name = GOOG
shares = 100
>>>

仔細觀察會發現輸出資料完全由 columns 變數中列出的屬性名決定。

tableformat.py 檔案中,使用該思想將其擴充套件為通用函式 print_table()print_table()列印一個表格,顯示使用者指定的任意物件的屬性。與早期的 print_report() 函式一樣,print_table() 方法還應接受一個 TableFormatter 例項來控制輸出格式。它們的工作方式如下:

>>> import report
>>> portfolio = report.read_portfolio('Data/portfolio.csv')
>>> from tableformat import create_formatter, print_table
>>> formatter = create_formatter('txt')
>>> print_table(portfolio, ['name','shares'], formatter)
      name     shares
---------- ----------
        AA        100
       IBM         50
       CAT        150
      MSFT        200
        GE         95
      MSFT         50
       IBM        100

>>> print_table(portfolio, ['name','shares','price'], formatter)
      name     shares      price
---------- ---------- ----------
        AA        100       32.2
       IBM         50       91.1
       CAT        150      83.44
      MSFT        200      51.23
        GE         95      40.37
      MSFT         50       65.1
       IBM        100      70.44
>>>

目錄 | 上一節 (4.2 繼承) | 下一節 (4.4 異常)

注:完整翻譯見 https://github.com/codists/practical-python-zh

相關文章