类的专有方法

#前面已经了解了类的访问权限、私有变量和私有方法,除了自定义私有变量和方法外,Python类还可以定义专有方法。专有方法是在特殊情况下或使用特殊语法时由Python调用的,而不是像普通方法一样在代码中直接调用。
#看到形如__xxx__的变量或函数名就要注意,这在Python中是有特殊用途的。
#__init__我们知道怎么用了,Python的class中有许多这种有特殊用途的函数,可以帮助沃恩定制类。下面介绍这种特殊类型的函数定制类的方法。
 
1、__str__
#介绍之前,我们先定义一个Student类,定义如下:
1 #!/usr/bin/python3
2 #-*-coding:UTF-8-*-
3 #类的专有方法
4 
5 class Student(object):
6     def __init__(self,name):
7         self.name=name
8 
9 print(Student('xiaoming'))
#执行结果如下:
1 D:Pythonworkspacedatatime20171212>python __str__.py
2 <__main__.Student object at 0x0274A450>
#执行结果输出一堆字符串,一般人看不懂,没什么可用性,也不好看。怎样才能输出的好看呢?
#只需要我们定义好__str__()方法,返回一个好看的字符串就可以了。重新定义上面的实例:
 1 #!/usr/bin/python3
 2 #-*-coding:UTF-8-*-
 3 #类的专有方法
 4 
 5 class Student(object):
 6     def __init__(self,name):
 7         self.name=name
 8 
 9     def __str__(self):
10         return '学生名称:%s'%self.name 
11 
12 print(Student('xiaoming'))
#执行结果为:
1 D:Pythonworkspacedatatime20171212>python __str__.py
2 学生名称:xiaoming
#由执行结果看到,这样输出的实例不但好看,而且是我们想要的。
#如果在交互模式下输入如下:
 1 #!/usr/bin/python3
 2 #-*-coding:UTF-8-*-
 3 #类的专有方法
 4 
 5 class Student(object):
 6     def __init__(self,name):
 7         self.name=name
 8 
 9 
10 
11 #print(Student('xiaoming'))
12 s=Student('xiaoming')
13 print(s)
#执行结果为:
1 D:Pythonworkspacedatatime20171212>python __str__.py
2 <__main__.Student object at 0x0331A450>
#由执行结果看到,输出的实例还跟之前一样,不容易识别。
#这是因为直接显示变量调用的不是__str__(),而是__repr__,两者的区别在于__str__()返回用户看到的字符串,而__repr__()返回的程序开发者看到的字符串。也就是说,__repr__()是为调试服务的。
#解决的办法是在定义个__repr__。通常,__str__()和__repr__()代码是一样的,所以有一个偷懒的写法:
 1 #!/usr/bin/python3
 2 #-*-coding:UTF-8-*-
 3 #类的专有方法
 4 
 5 class Student(object):
 6     def __init__(self,name):
 7         self.name=name
 8 
 9     def __str__(self):
10         return '学生名称:%s'%self.name 
11 
12     __repr__=__str__
13 
14 #print(Student('xiaoming'))
15 s=Student('xiaoming')
16 print(s)
#执行结果为:
1 D:Pythonworkspacedatatime20171212>python __str__.py
2 学生名称:xiaoming
#可以看到,已经得到满意的结果了。
 
2、__iter__
#如果想要将一个类用于for..in循环,类似list或tuple一样,就必须实现一个__iter__()方法。该方法返回一个迭代对象,Python的for循环会不断调用该迭代对象的__next__()方法,获得循环的下一个值,知道遇到StopIteration错误时退出循环。
#我们以斐波那契数列为例,写一个可以作用于for循环的Fib类:
 1 #!/usr/bin/python3
 2 #-*-coding:UTF-8-*-
 3 #__iter__
 4 
 5 class Fib(object):
 6     def __init__(self):
 7         self.a,self.b=0,1 #初始化两个计数器a、b
 8 
 9     def __iter__(self):
10         return self #实例本身就是迭代对象,返回自己
11 
12     def __next__(seflf):
13         self.a,self.b=self.b,self.a+self.b #计算下一个值
14         if self.a>100000:  #退出循环的条件
15             raise StopIteration();
16         return self.a #返回下一个值
17         #下面我们把Fib实例作用于for循环。
18 for n in Fib():
19        print(n)
#执行结果如下:
 1 D:Pythonworkspacedatatime20171213>python __iter__.py
 2 1
 3 1
 4 2
 5 3
 6 5
 7 8
 8 13
 9 21
10 34
11 55
12 89
13 144
14 233
15 377
16 610
17 987
18 1597
19 2584
20 4181
21 6765
22 10946
23 17711
24 28657
25 46368
26 75025
3、__getitem__
#Fib实例虽然能够作用于for循环,和list有点像,但是不能将它当成list使用。比如取第3个元素:
 1 #!/usr/bin/python3
 2 #-*-coding:UTF-8-*-
 3 #__iter__
 4 
 5 class Fib(object):
 6     def __init__(self):
 7         self.a,self.b=0,1 #初始化两个计数器a,b
 8 
 9     def __iter__(self):
10         return self #实例化本身就是迭代对象,因此返回自己
11 
12     def __next__(self):
13         self.a,self.b=self.b,self.a+self.b #计算下一个值
14         if self.a>100000: #退出循环的条件
15             raise StopIteration();
16         return self.a #返回下一个值
17 for n in Fib()[3]:
18     print(n)
#执行结果如下:
1 D:Pythonworkspacedatatime20171213>python __iter__.py
2 Traceback (most recent call last):
3   File "__iter__.py", line 17, in <module>
4     for n in Fib()[3]:
5 TypeError: 'Fib' object does not support indexing
#由执行结果看到,取元素时报错了。该怎么办?
#要像list一样按照下标取元素,需要实现__getitem__()方法,代码如下:
 1 #!/usr/bin/python3
 2 #-*-coding:UTF-8-*-
 3 #__getitem__
 4 
 5 class Fib(object):
 6     def __getitem__(self,n):
 7         a,b=1,1
 8         for x in range(n):
 9             a,b=b,a+b
10         return a
#下面尝试取得数列的值:
1 fib=Fib()
2 print(fib[3])
3 print(fib[8])
#执行结果如下:
1 D:Pythonworkspacedatatime20171213>python __getitem__.py
2 3
3 34
#由执行结果看到,可以成功获取对应数列的值了。
 
4、__getattr__
#正常情况下,调用类的方法或属性时,如果类的方法或属性不存在就会报错。比如定义Student类:
1 #!/usr/bin/python3
2 #-*-coding:UTF-8-*-
3 #__getattr__
4 
5 class Student(object):
6     def __init__(self,name):
7         self.name='xiaoming'
#对于上面的代码,调用name属性不会有任何问题,但是调用不存在的score属性就会报错。执行以下代码:
1 stu=Student('xiaoming')
2 print(stu.name)
3 print(stu.score)
#执行结果如下:
1 D:Pythonworkspacedatatime20171213>python __getattr__.py
2 xiaoming
3 Traceback (most recent call last):
4   File "__getattr__.py", line 11, in <module>
5     print(stu.score)
6 AttributeError: 'Student' object has no attribute 'score'
#由输出结果看到,错误信息告诉我们没有找到score属性。对于这种情况,该怎么解决呢?
#要避免这个错误,除了可以添加一个score属性外,Python还提供了另一种机制,就是写一个__getattr__()方法,动态返回一个属性。上面的代码修改如下:
 1 #!/usr/bin/python3
 2 #-*-coding:UTF-8-*-
 3 #__getattr__
 4 
 5 class Student(object):
 6     def __init__(self,name):
 7         self.name='xiaoming'
 8 
 9     def __getattr__(self,attr):
10         if attr=='score':
11             return 96
12 
13 stu=Student('xiaoming')
14 print(stu.name)
15 print(stu.score)
#当调用不存在的属性时(如score),Python解释器就会调用__getattr__(self,'score')尝试获取属性,这样就有机会返回score的值。执行结果如下:
1 D:Pythonworkspacedatatime20171213>python __getattr__.py
2 xiaoming
3 96
#由输出结果看到,可以正确输出不存在的属性的值了。
#注意,只有在没有找到属性的情况下才调用__getattr__,已有的属性(如name),不会在__getattr__中查找。此外,如果所有调用都会返回None(如stu.abc),就是定义的__getattr__,哦人返回None。
 
5、__call__
#一个对象实例可以有自己的属性和方法,调用实例的方法时使用instance.method()调用。能不能直接在实例本身调用,答案是可以的。
#任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用,例如:
1 #!/usr/bin/python3
2 #-*-coding:UTF-8-*-
3 #__call__
4 
5 class Student(object):
6     def __init__(self,name):
7         self.name=name
8     def __call__(self):
9         print('名称:%s'%self.name)
#执行如下操作:
1 stu=Student('xiaoming')
2 stu()
#执行结果如下:
1 D:Pythonworkspacedatatime20171213>python __call__.py
2 名称:xiaoming
#由输出结果看到,可以直接对实例进行调用并得到结果。
#__call__()还可以定义参数。对实例进行直接调用就像对一个函数调用一样,完全可以把对象看成函数,把函数看成对象,因为这两者本来就是有根本的区别。
#如果把对象看成函数,函数本身就可以在运行期间动态创建出来,因为类的实例都是运行期间创建出来的。
#怎判断一个变量是对象还是函数呢?
#很多时候判断一个对象能否被调用,可以使用Callable()函数,比如函数和上面定义带有__call__()的类实例。输入如下:
1 print(callable(Student('xiaoqiang')))
2 print(callable(max))
3 print(callable([1,2,3]))
4 print(callable(None))
5 print(callable('a'))
#执行结果如下:
1 D:Pythonworkspacedatatime20171213>python __call__.py
2 True
3 True
4 False
5 False
6 False
#由输出结果看到,通过callable()函数可以判断一个对象是否为’可调用‘对象。
原文地址:https://www.cnblogs.com/DLHe/p/8065449.html