美文网首页
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