美文网首页
python学习笔记-tip42(定制类-内置方法的使用)

python学习笔记-tip42(定制类-内置方法的使用)

作者: 黑键手记 | 来源:发表于2018-09-27 09:04 被阅读12次

    引言

    我们在写java类时,通常会有toString() equals() compareTo()等等方法的重写,只要将这些方法重写,那么当我们调用类的对应方法时,类就会按照我们设定的方式去做出表现。

    同样,python作为一个动态高级语言,他当然也是可以实现的。

    前面我们在介绍获取对象信息的时候,我们使用到了dir()方法,这个方法可以获取对象的所有属性和方法,然后展示到一个字符串list里面去。

    展示完之后,我们发现了很多__xxx__类型的方法或者属性,这些类型的方法熟悉是python预留的,有特殊用途的方法和属性。

    今天就给大家介绍几个功能和java中的toString()很类似的方法,这些方法或属性可以帮助我们“定制类”

    __str__它像极了toString()

    我们定义一个类,然后打印他的类型时,他会打印很长的一段信息,如果我们想改变这个信息的话,我们需要重写__str__方法
    如下图所示:


    通过学生类和教师类的对比,说明了__str__方法的用处

    __iter__方法可以让一个类可以被for...in...处理

    class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b
    
    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己
    
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
    

    调试结果

    >>> for n in Fib():
          ...     print(n)
          ...
          1
          1
          2
          3
          5
          ...
          46368
          75025
    

    这里不再介绍,后续需要再做讲解

    __getitem__方法可以通过索引取出元素

      >>> Fib()[5]
      Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      TypeError: 'Fib' object does not support indexing
    

    要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

      class Fib(object):
          def __getitem__(self, n):
              a, b = 1, 1
              for x in range(n):
              a, b = b, a + b
              return a
    

    现在,就可以按下标访问数列的任意一项了:

      >>> f = Fib()
      >>> f[0]
      1
      >>> f[1]
      1
      >>> f[2]
      2
      >>> f[3]
      3
      >>> f[10]
      89
      >>> f[100]
      573147844013817084101
    

    但是list有个神奇的切片方法:

      >>> list(range(100))[5:10]
        [5, 6, 7, 8, 9]
    

    对于Fib却报错。原因是getitem()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

    class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
    

    现在试试Fib的切片:

    >>> f = Fib()
     >>> f[0:5]
    [1, 1, 2, 3, 5]
    >>> f[:10]
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    

    但是没有对step参数作处理:

    >>> f[:10:2]
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    

    也没有对负数作处理,所以,要正确实现一个getitem()还是有很多工作要做的。

    此外,如果把对象看成dict,getitem()的参数也可能是一个可以作key的object,例如str。

    与之对应的是setitem()方法,把对象视作list或dict来对集合赋值。最后,还有一个delitem()方法,用于删除某个元素。

    总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

    下面着重说一下__getattr__方法

    一般情况下,我们如果访问到类里一个没有被定义过的属性或者方法的时候,就会直接报错
    我们解决的方法有两种

    • 定义这个属性或者方法
    • 重写__getattr__方法
      第一种就不多说了,我们直接说下第二种
      __getattr__这个方法会在调用对象属性的时候直接被调用起来,所以可以通过重写该方法去解决问题

    实现调用属性不报错

    实现调用方法不报错

    需要特别注意的是:

    • 大家有没有注意到 我们有name的属性,所以直接通过实例对象调用name是可以访问到的,只有访问不到的属性或者方法才会调用__getattr__这个方法的
    • 此外,如果使用了__getattr__那么如果在方法逻辑里面没有写返回值,那么调用一个不存在的方法或者属性的话,是默认返回None的,不会报错,如

    不过如果我们需要报错的话,那么就需要修改__getattr__方法,让他默认报错

    总结一下__getattr__

    他其实是帮助我们

    「把一个类的所有属性和方法全部动态化处理了,不需要任何特殊手段」

    那么我们就可以针对完全动态的情况做调用“

    下面看一个重要例子,上面的内容都可以不看,但必须看这里哦

    现在很多网站都搞REST api
    比如新浪微博、豆瓣啥的,调用API的URL类似:

    • http://api.server/user/friends
    • http://api.server/user/timeline/list
      如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。
      利用完全动态的__getattr__,我们可以写出一个链式调用:

      若想实现逻辑,只需要在里面依次判断处理即可。

    总结

    这些python特意为我们提供的方法,可以让我们方便的将一个类构造成我们想要的样子
    __getattr__需要理解,其他的只需看懂即可。

    相关文章

      网友评论

          本文标题:python学习笔记-tip42(定制类-内置方法的使用)

          本文链接:https://www.haomeiwen.com/subject/ajusoftx.html