类的专有方法
#前面已经了解了类的访问权限、私有变量和私有方法,除了自定义私有变量和方法外,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:\Python\workspace\datatime\20171212>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:\Python\workspace\datatime\20171212>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:\Python\workspace\datatime\20171212>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:\Python\workspace\datatime\20171212>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:\Python\workspace\datatime\20171213>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:\Python\workspace\datatime\20171213>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:\Python\workspace\datatime\20171213>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:\Python\workspace\datatime\20171213>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:\Python\workspace\datatime\20171213>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:\Python\workspace\datatime\20171213>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:\Python\workspace\datatime\20171213>python __call__.py 2 True 3 True 4 False 5 False 6 False
#由输出结果看到,通过callable()函数可以判断一个对象是否为’可调用‘对象。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步