第42~43讲:课后作业

第42讲:

测试题部分:

0. 自 Python2.2 以后,对类和类型进行了统一,做法就是将 int()、float()、str()、list()、tuple() 这些 BIF 转换为工厂函数。请问所谓的工厂函数,其实是什么原理?
答:工厂函数,其实就是一个类对象。当你调用他们的时候,事实上就是创建一个相应的实例对象。

1 # a 和 b 是工厂函数(类对象) int 的实例对象
2 >>> a = int('123')
3 >>> b = int('345')
4 >>> a + b
5 468

 

1. 当实例对象进行加法操作时,会自动调用什么魔法方法?
答:对象 a 和 b 相加时(a + b),Python 会自动根据对象 a 的 __add__ 魔法方法进行加法操作。

2. 下边代码有问题吗?(运行起来似乎没出错的说^_^)

1 class Foo:
2         def foo(self):
3                 self.foo = "I love FishC.com!"
4                 return self.foo
5 >>> foo = Foo()
6 >>> foo.foo()
7 'I love FishC.com!'

答:这绝对是一个温柔的陷阱,这种BUG比较难以排查,所以一定要注意:类的属性名和方法名绝对不能相同!如果代码这么写,就会有一个难以排查的BUG出现了:

 1 class Foo:
 2         def __init__(self):
 3                 self.foo = "I love FishC.com!"
 4         def foo(self):
 5                 return self.foo
 6 >>> foo = Foo()
 7 >>> foo.foo()
 8 Traceback (most recent call last):
 9   File "<pyshell#21>", line 1, in <module>
10     foo.foo()
11 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__(a, b)
**
__pow__(self, other[, modulo])
<<
__lshift__(self, other)
>>
__rshift__(self, other)
&
__and__(self, other)
^
__xor__(self, other)
|
__or__(self, other)


4. 以下代码说明 Python 支持什么风格?

 1 def calc(a, b, c):
 2         return (a + b) * c
 3 >>> a = calc(1, 2, 3)
 4 >>> b = calc([1, 2, 3], [4, 5, 6], 2)
 5 >>> c = calc('love', 'FishC', 3)
 6 >>> print(a)
 7 9
 8 >>> print(b)
 9 [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
10 >>> print(c)
11 loveFishCloveFishCloveFishC

答:说明 Python 支持鸭子类型(duck typing)风格。
详见:【扩展阅读】鸭子类型(duck typing)

 

动动手部分:

0. 我们都知道在 Python 中,两个字符串相加会自动拼接字符串,但遗憾的是两个字符串相减却抛出异常。因此,现在我们要求定义一个 Nstr 类,支持字符串的相减操作:A – B,从 A 中去除所有 B 的子字符串。
示例:.|}$>

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

答:只需要重载 __sub__ 魔法方法即可。

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

replace方法的功能是字符串替换,这里把字母‘i’全部替换成空字符串了

 

1. 移位操作符是应用于二进制操作数的,现在需要你定义一个新的类 Nstr,也支持移位操作符的运算:

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

答:只需要重载 __lshift__ 和 __rshift__ 魔法方法即可。U;<Lfr^#

1 class Nstr(str):
2     def __lshift__(self, other):
3         return self[other:] + self[:other]
4     def __rshift__(self, other):
5         return self[-other:] + self[:-other]

通过字符串切片和字符串连接符‘+’完成了移位操作。

 

2. 定义一个类 Nstr,当该类的实例对象间发生的加、减、乘、除运算时,将该对象的所有字符串的 ASCII 码之和进行计算:

 1 >>> a = Nstr('FishC')
 2 >>> b = Nstr('love')
 3 >>> a + b
 4 899
 5 >>> a - b
 6 23
 7 >>> a * b
 8 201918
 9 >>> a / b
10 1.052511415525114
11 >>> a // b
12 1

代码清单:u$TOVh?Z

 1 class Nstr(str):
 2     def __init__(self, arg=''):
 3         if isinstance(arg, str):
 4             self.total = 0
 5             for each in arg:
 6                 self.total += ord(each)
 7         else:
 8             print("参数错误!")
 9     def __add__(self, other):
10         return self.total + other.total
11     def __sub__(self, other):
12         return self.total - other.total
13     def __mul__(self, other):
14         return self.total * other.total
15     def __truediv__(self, other):
16         return self.total / other.total
17     def __floordiv__(self, other):
18         return self.total // other.total

当然,你还可以这样做::

1 class Nstr(int):
2     def __new__(cls, arg=0):
3         if isinstance(arg, str):
4             total = 0
5             for each in arg:
6                 total += ord(each)
7             arg = total
8         return int.__new__(cls, arg)

 

 

第43讲

测试题部分:

0. 对象相加(a + b),如果 a 对象有 __add__ 方法,请问 b 对象的 __radd__ 会被调用吗?
答:不会!
实验如下:DH)eE0=

 1 >>> class Nint(int):
 2         def __radd__(self, other):
 3                 print("__radd__ 被调用了!")
 4                 return int.__add__(self, other)
 5 >>> a = Nint(5)
 6 >>> b = Nint(3)
 7 >>> a + b
 8 8
 9 >>> 1 + b
10 __radd__ 被调用了!
11 4


1. Python 什么时候会调用到反运算的魔法方法?
答:例如 a + b,如果 a 对象的 __add__ 方法没有实现或者不支持相应的操作,那么 Python 就会自动调用 b 的 __radd__ 方法。

2. 请问如何在继承的类中调用基类的方法?
答:使用 super() 这个 BIF 函数。

1 class Derived(Base):
2     def meth (self):
3         super(Derived, self).meth()

 

3. 如果我要继承的基类是动态的(有时候是 A,有时候是 B),我应该如何部署我的代码,以便基类可以随意改变。
答:你可以先为基类定义一个别名,在类定义的时候,使用别名代替你要继承的基类。如此,当你想要改变基类的时候,只需要修改给别名赋值的那个语句即可。顺便说一下,当你的资源是视情况而定的时候,这个小技巧很管用。
举个例子:

1 BaseAlias = BaseClass  # 为基类取别名
2 class Derived(BaseAlias):
3     def meth(self):
4         BaseAlias.meth(self)  # 通过别名访问基类
5         ...

 

4. 尝试自己举一个例子说明如何使用类的静态属性。(一定要自己先动手再看答案哦^_^)
答:类的静态属性很简单,在类中直接定义的变量(没有 self.)就是静态属性。引用类的静态属性使用”类名.属性名”的形式。
类的静态属性应用(计算该类被实例化的次数):;

1 class C:
2     count = 0  # 静态属性
3     def __init__(self):
4         C.count = C.count + 1  # 类名.属性名的形式引用
5     def getCount(self):
6         return C.count

 

5. 尝试自己举例说明如何使用类的静态方法,并指出使用类的静态方法有何有点和需要注意的地方?(一定要自己先动手再看答案哦^_^)
答:静态方法是类的特殊方法,静态方法只需要在普通方法的前边加上 @staticmethod 修饰符即可。

1 class C:
2         @staticmethod  # 该修饰符表示 static() 是静态方法
3         def static(arg1, arg2, arg3):
4                 print(arg1, arg2, arg3, arg1 + arg2 + arg3)
5         def nostatic(self):
6                 print("I'm the f**king normal method!")

静态方法最大的优点是:不会绑定到实例对象上,换而言之就是节省开销。

 1 >>> c1 = C()
 2 >>> c2 = C()
 3 # 静态方法只在内存中生成一个,节省开销
 4 >>> c1.static is C.static
 5 True
 6 >>> c1.nostatic is C.nostatic
 7 False
 8 >>> c1.static
 9 <function C.static at 0x03001420>
10 >>> c2.static
11 <function C.static at 0x03001420>
12 >>> C.static
13 <function C.static at 0x03001420>
14 # 普通方法每个实例对象都拥有独立的一个,开销较大
15 >>> c1.nostatic
16 <bound method C.nostatic of <__main__.C object at 0x03010590>>
17 >>> c2.nostatic
18 <bound method C.nostatic of <__main__.C object at 0x032809D0>>
19 >>> C.nostatic
20 <function C.nostatic at 0x0328D2B8>

使用的时候需要注意的地方:静态方法并不需要 self 参数,因此即使是使用对象去访问,self 参数也不会传进去。

1 >>> c1.static(1, 2, 3)
2 1 2 3 6
3 >>> C.static(1, 2, 3)
4 1 2 3 6

 

动动手部分:

0. 定义一个类,当实例化该类的时候,自动判断传入了多少个参数,并显示出来。
答:其实很容易啦,检查下大家之前的知识点有没有记牢固而已。

1 class C:
2         def __init__(self, *args):
3                 if not args:
4                         print("并没有传入参数")
5                 else:
6                         print("传入了 %d 个参数,分别是:" % len(args), end='')
7                         for each in args:
8                                 print(each, end=' ')

 

1. 定义一个单词(Word)类继承自字符串,重写比较操作符(参考自学:Python 魔法方法详解),当两个 Word 类对象进行比较时,根据单词的长度来进行比较大小。
加分要求:实例化时如果传入的是带空格的字符串,则取第一个空格前的单词作为参数。
答:加分要求可以通过重载 __new__ 方法来实现(因为字符串是不可变类型),通过重写 __gt__、__lt__、__ge__、__le__ 方法来定义 Word 类在比较操作中的表现。
注意,我们没有定义 __eq__ 和 __ne__ 方法。这是因为将会产生一些怪异不符合逻辑的结果(比如 Word('FishC') 会等于 Word('Apple'))
代码清单:

 1 class Word(str):
 2 '''存储单词的类,定义比较单词的几种方法'''
 3     def __new__(cls, word):
 4         # 注意我们必须要用到 __new__ 方法,因为 str 是不可变类型
 5         # 所以我们必须在创建的时候将它初始化
 6         if ' ' in word:
 7             print "Value contains spaces. Truncating to first space."
 8             word = word[:word.index(' ')] #单词是第一个空格之前的所有字符
 9         return str.__new__(cls, word)
10     def __gt__(self, other):
11         return len(self) > len(other)
12     def __lt__(self, other):
13         return len(self) < len(other)
14     def __ge__(self, other):
15         return len(self) >= len(other)
16     def __le__(self, other):
17         return len(self) <= len(other)

 

posted @ 2020-08-27 22:28  洛兰123  阅读(188)  评论(0编辑  收藏  举报