python从零开始--33 __len__, __str__, __iter__, __getitem__,__getattr__ , __setattr___call__, __slots__

前面第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













  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值