前面第26篇笔记中,已经记录过几个python中的特殊变量: __dict__, __doc__, __name__, __module__和 __bases__,我们可以用它们获取意向类的属性。
其实python中还有一些重要的特殊方法,它们是 __len__, __str__, __iter__, __getattr__ , __call__, __slots__,下面一一举例说明。
__len()__ :
len()方法我们已经很熟悉了,len(object)可以返回对象的长度属性,比如返回list的长度等。但如果我们定义了一个class类,对其对象使用len()方法,就会报错:
class Classroom(object):
def __init__(self, classroom_name):
self.classroom_name = classroom_name
self.students_list = []
@property
def name(self):
return self.classroom_name
def add_student(self, student_name):
self.students_list.append(student_name)
if __name__ == "__main__":
c1 = Classroom("一年级三班")
c1.add_student("李好")
c1.add_student("张催")
c1.add_student("宋江")
c1.add_student("于谦")
c1.add_student("郭德纲")
print("目前{}的学生一共有{}名。".format(c1.name, len(c1)))
Traceback (most recent call last):
File "D:/pythonProjects/LXF/面向对象高级编程/len.py", line 33, in <module>
print("目前{}的学生一共有{}名。".format(c1.name, len(c1)))
TypeError: object of type 'Classroom' has no len()
解决办法就是给其加上__len__()方法,如下:
class Classroom(object):
def __init__(self, classroom_name):
self.classroom_name = classroom_name
self.students_list = []
@property
def name(self):
return self.classroom_name
def add_student(self, student_name):
self.students_list.append(student_name)
def __len__(self):
return len(self.students_list)
if __name__ == "__main__":
c1 = Classroom("一年级三班")
c1.add_student("李好")
c1.add_student("张催")
c1.add_student("宋江")
c1.add_student("于谦")
c1.add_student("郭德纲")
print("目前{}的学生一共有{}名。".format(c1.name, len(c1)))
目前一年级三班的学生一共有5名。
__str__:
我们经常用print(xxxx)方法打印字符串,数值或元组列表等,也可以用print(obj)打印对象地址,例如:
class Classroom(object):
def __init__(self, classroom_name):
self.classroom_name = classroom_name
self.students_list = []
@property
def name(self):
return self.classroom_name
def add_student(self, student_name):
self.students_list.append(student_name)
if __name__ == "__main__":
c1 = Classroom("一年级三班")
c1.add_student("李好")
c1.add_student("张催")
c1.add_student("宋江")
c1.add_student("于谦")
c1.add_student("郭德纲")
print(c1)
<__main__.Classroom object at 0x0000000000B4BBA8>
但如果我们希望能打印些更给力的信息,就需要重写__str__方法:
class Classroom(object):
def __init__(self, classroom_name):
self.classroom_name = classroom_name
self.students_list = []
@property
def name(self):
return self.classroom_name
def add_student(self, student_name):
self.students_list.append(student_name)
def __str__(self): # 返回值必需是一个字符串
# info_list = [c1.name] + self.students_list
str_return = c1.name + ":"
for i in self.students_list:
str_return += " "
str_return += i
return str_return
if __name__ == "__main__":
c1 = Classroom("一年级三班")
c1.add_student("李好")
c1.add_student("张催")
c1.add_student("宋江")
c1.add_student("于谦")
c1.add_student("郭德纲")
print(c1)
一年级三班: 李好 张催 宋江 于谦 郭德纲
__iter__ __next__:
如果希望自己定制的类支持 for...in循环,就要实现__iter__ 和 __next__; 这一块我也没有研究明白,但感觉下来 __iter__主要作用是让类变成一个iterator,支持for...in; __next__则是每次迭代要调用的函数,一定要加上StopIteration的处理。
class Classroom(object):
def __init__(self, classroom_name):
self.classroom_name = classroom_name
self.students_list = []
self.next_count = -1
@property
def name(self):
return self.classroom_name
def add_student(self, student_name):
self.students_list.append(student_name)
def __iter__(self): # 返回self就可以
return self
def __len__(self):
return len(self.students_list)
def __next__(self):
if self.next_count == len(self.students_list)-1 or len(self.students_list) == 0:
raise StopIteration # 一定要加退出的机制,这里抛出StopIteration即可,for....in会自动处理
else:
self.next_count += 1 # 每次迭代循环会调用一次__next__(self):
return self.students_list[self.next_count]
if __name__ == "__main__":
c1 = Classroom("一年级三班")
c1.add_student("李好")
c1.add_student("张催")
c1.add_student("宋江")
c1.add_student("于谦")
c1.add_student("郭德纲")
for i in c1: # Classroom对象已经支持for ...in了
print(i)
李好
张催
宋江
于谦
郭德纲
另外也可以直接用next(object)的方式调用:
for i in range(len(c1)):
print(next(c1))
李好
张催
宋江
于谦
郭德纲
__getitem__:
List[5], List[3:6]这种用法有时候非常方便,如果要让自己的类支持这种切片,需要实现__getitem__()方法。
__getitem__(self, n)的第二个参数,可能是一个索引(整数),也可能是一个切片,需要分别处理,代码实现方式类似下面的例子。
class Classroom(object):
def __init__(self, classroom_name):
self.classroom_name = classroom_name
self.students_list = []
self.next_count = -1
@property
def name(self):
return self.classroom_name
def add_student(self, student_name):
self.students_list.append(student_name)
def __getitem__(self, n):
try:
if isinstance(n,int): # 如果传入的是一个整数
return self.students_list[n]
if isinstance(n, slice): # 如果传入的是一个切片
start = n.start
stop = n.stop
l = []
for x in range(start, stop):
l.append(self.students_list[x])
return l
except IndexError:
return "输入下标的数据不存在"
if __name__ == "__main__":
c1 = Classroom("一年级三班")
c1.add_student("李好")
c1.add_student("张催")
c1.add_student("宋江")
c1.add_student("于谦")
c1.add_student("郭德纲")
print(c1[2])
print(c1[2:4])
宋江
['宋江', '于谦']
__getattr__, __setattr__:
如果用户调用一个不存在的属性或方法,可以用__getattr__拦截和处理。 但这个方法的实际用途,我还没有理解,暂时先不举例,等后续再补写。
TBD
__call__:
把实例对象当函数用,就需要实现这个方法,但我没搞清楚,这个到底能起个什么作用,能带来什么额外的好处,等体会到后,再来补写这一块。
TBD