第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)