第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类型是有加减乘除这些的。

 

posted @ 2020-07-26 16:11  廖海清  阅读(293)  评论(0编辑  收藏  举报