1. 程式人生 > 實用技巧 >第042講:魔法方法:算術運算1

第042講:魔法方法:算術運算1

課題筆記:

啥是工廠函式?

len()是一個BIF,作用是返回引數的長度。type(len)的話返回。

如果說type(int)則他是返回一個class “type”。就如同

C定義完了後就是一個類物件。所以所謂的“工廠函式”就是類物件。

int(‘123’),在以前就是呼叫int函式,將引數轉化為123這個整形。而現在不一樣了,它是例項化int的這個物件,然後返回一個它示例後的物件。例如:

>>> a = int('123')
>>> b = int('456')
>>> a +b
579

原來物件是可以進行計算的。

Python的魔法方法還提供了讓你自定義物件的數值處理,通過對我們這些魔法方法的一個重寫,你可以自定義任何物件間的算術運算。

例子:

>>> class New_int(int):
    def __add__(self, other):
        return int.__sub__(self, other)
    def __sub__(self, other):
        return int.__add__(self, other)

    
>>> a = New_int(3)
>>> b = New_int(5)
>>> a + b -2 >>> a - b 8

我這邊 可以不return int的魔法方法嗎?

>>> class Try_int(int):
    def __add__(self, other):
        return self + other
    def __sub__(self, other):
        return self - other

    
>>> a = Try_int(3)
>>> b = Try_int(5)
>>> a + b
Traceback (most recent call last):
  File 
"<pyshell#34>", line 1, in <module> a + b File "<pyshell#31>", line 3, in __add__ return self + other File "<pyshell#31>", line 3, in __add__ return self + other File "<pyshell#31>", line 3, in __add__ return self + other [Previous line repeated 1022 more times] RecursionError: maximum recursion depth exceeded >>>

結果出現了無限遞迴!去到了遞迴的最高層次就出來了,自動退出了。為什麼會這樣子呢?

當a+b的時候出現加法,自動先呼叫前者a的一個add,先呼叫一個add 的一個魔法方法;那呼叫add 的話返回的是什麼?返回的是self加上other,self是示例物件繫結的一個方式(繫結進來的方式),那麼這個self就是綁定了這個a進來,other就是加法右邊的數(也就是b);那return的又是a+b,這樣就出現無限遞迴。

後面會講到關於屬性的魔法方法,更要注意這些情況了。

可以這樣改:

>>> class Try_int(int):
    def __add__(self, other):
        return int(self) + int(other) #也可以不用int這個other
    def __sub__(self, other):
        return int(self) - int(other)

    
>>> a = Try_int(3)
>>> b = Try_int(5)
>>> a + b
8
>>> 

測試題:

0. 自 Python2.2 以後,對類和型別進行了統一,做法就是將 int()、float()、str()、list()、tuple() 這些 BIF 轉換為工廠函式。請問所謂的工廠函式,其實是什麼原理?

工廠函式就是將引數例項化成物件,然後呼叫其中的魔法方法

答:工廠函式,其實就是一個類物件。當你呼叫他們的時候,事實上就是建立一個相應的例項物件

# a 和 b 是工廠函式(類物件) int 的例項物件
>>> a = int('123')
>>> b = int('345')
>>> a + b
468

1. 當例項物件進行加法操作時,會自動呼叫什麼魔法方法?

自動呼叫__add__(self, other)

答:物件 a 和 b 相加時(a + b),Python 會自動根據物件 a 的 __add__ 魔法方法進行加法操作。

2. 下邊程式碼有問題嗎?(執行起來似乎沒出錯的說^_^)

class Foo:
        def foo(self):
                self.foo = "I love FishC.com!"
                return self.foo

>>> foo = Foo()
>>> foo.foo()
'I love FishC.com!'

foo是Foo示例化的一個物件,foo.foo()就是呼叫Foo類裡面的foo這個函式? self就是foo, self.foo就是物件foo的foo屬性等於一串字串,foo函式最後返回這個屬性

答:這絕對是一個溫柔的陷阱,這種BUG比較難以排查,所以一定要注意:類的屬性名方法名絕對不能相同!如果程式碼這麼寫,就會有一個難以排查的BUG出現了:

class Foo:
        def __init__(self):
                self.foo = "I love FishC.com!"
        def foo(self):
                return self.foo

>>> foo = Foo()
>>> foo.foo()
Traceback (most recent call last):
  File "<pyshell#21>", line 1, in <module>
    foo.foo()
TypeError: 'str' object is not callable

3. 寫出下列算術運算子對應的魔法方法:

運算子 對應的魔法方法
+ __add__(self, other)
- __sub__(self, other)
* __mul__(self, other)
/ __truediv__(self, other)
// __floordiv__(self, other)
% __mod__(self, other)
divmod(a, b) __divmod__(self, other)
** __pow__(self, other[,modulo])
<< __lshift__(self, other)
>> __rshift__(self, other)
& __and___(self, other)
^ __xor___(self, other)
| __or___(self, other)

4. 以下程式碼說明 Python 支援什麼風格?

def calc(a, b, c):
        return (a + b) * c

>>> a = calc(1, 2, 3)
>>> b = calc([1, 2, 3], [4, 5, 6], 2)
>>> c = calc('love', 'FishC', 3)
>>> print(a)
9
>>> print(b)
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
>>> print(c)
loveFishCloveFishCloveFishC

物件間可以進行相加

答:說明 Python 支援鴨子型別(duck typing)風格。詳見:【擴充套件閱讀】鴨子型別(duck typing)

動動手:

0. 我們都知道在 Python 中,兩個字串相加會自動拼接字串,但遺憾的是兩個字串相減卻丟擲異常。因此,現在我們要求定義一個 Nstr 類,支援字串的相減操作:A – B,從 A 中去除所有 B 的子字串。

示例:

>>> a = Nstr('I love FishC.com!iiiiiiii')
>>> b = Nstr('i')
>>> a - b
'I love FshC.com!'

自己寫的:

class Nstr(str):
    def __sub__(self, other):
        return str.split(self,other)

答案:

class Nstr(str):
    def __sub__(self, other):
        return self.replace(other, '')

1. 移位操作符是應用於二進位制運算元的,現在需要你定義一個新的類 Nstr,也支援移位操作符的運算:

>>> a = Nstr('I love FishC.com!')
>>> a << 3
'ove FishC.com!I l'
>>> a >> 3
'om!I love FishC.c'

答:只需要過載 __lshift__ 和 __rshift__ 魔法方法即可。

class Nstr(str):
    def __lshift__(self, other):
        return self[other:] + self[:other]

    def __rshift__(self, other):
        return self[-other:] + self[:-other]

2. 定義一個類 Nstr,當該類的例項物件間發生的加、減、乘、除運算時,將該物件的所有字串的 ASCII 碼之和進行計算:

>>> a = Nstr('FishC')
>>> b = Nstr('love')
>>> a + b
899
>>> a - b
23
>>> a * b
201918
>>> a / b
1.052511415525114
>>> a // b
1

程式碼清單:

class Nstr:
    def __init__(self, arg=''):
        if isinstance(arg, str):
            self.total = 0
            for each in arg:
                self.total += ord(each)
        else:
            print("引數錯誤!")

    def __add__(self, other):
        return self.total + other.total

    def __sub__(self, other):
        return self.total - other.total

    def __mul__(self, other):
        return self.total * other.total

    def __truediv__(self, other):
        return self.total / other.total

    def __floordiv__(self, other):
        return self.total // other.total

也可以這樣寫:

class Nstr(int):
    def __new__(cls, arg=0):
        if isinstance(arg, str):
            total = 0
            for each in arg:
                total += ord(each)
            arg = total
        return int.__new__(cls, arg)

第二種方法是用了繼承了int 的屬性,利用new在之前就把字串先轉成了ASCII碼,然後用int的屬性,int型別是有加減乘除這些的。