零基础入门学Python(十二)—— 魔法方法(上)

零基础入门学Python(十二)—— 魔法方法(上),第1张

概述零基础入门学Python系列内容的学习目录→\rightarrow→零基础入门学Python系列内容汇总。魔法方法(上)1.构造和析构1.1__init__(self[,...])1.2__new__(cls[,

零基础入门学Python系列内容的学习目录 → \rightarrow →零基础入门学Python系列内容汇总。

魔法方法(上)1. 构造和析构1.1 _ _ init _ _(self[, ...])1.2 _ _ new _ _(cls[, ...])1.3 _ _ del _ _(self)2. 算术运算2.1 算术 *** 作符2.2 反运算2.3 增量赋值运算2.4 一元 *** 作符3. 简单定制4. 属性访问5. 描述符6. 定制序列7. 迭代器8. 生成器

  需要学习的基础知识有:构造和析构、算术运算、简单定制、属性访问、描述符、定制序列、迭代器、生成器等。因本部分内容较多,故分为上下两个篇章。
    1、2、3部分内容见零基础入门学Python(十二)—— 魔法方法(上)
    4、5、6、7、8部分内容见零基础入门学Python(十二)—— 魔法方法(下)

1. 构造和析构

  关于魔法方法的几点说明:

魔法方法总是被双下划线包围,例如_ _init_ _();魔法方法是面向对象的Python的一切;魔法方法的“魔力”体现在它们总能够在适当的时候被调用。1.1 _ _ init _ _(self[, …])

  _ _init_ _()方法相当于其他面向对象编程语言的构造方法,也就是类在实例化成对象的时候首先会调用的一个方法。
  举个例子:

class Rectangle:    """    定义一个矩形类,    需要长和宽两个参数,    拥有计算周长和面积两个方法,    需要对象在初始化的时候拥有“长”和“宽”两个参数,    因此需要重写__init__()方法    """        def __init__(self, x, y):        self.x = x        self.y = y            def getPeri(self):        return(self.x + self.y) * 2    def getArea(self):        return self.x *self.y

  运行上面代码:
  >>> rect = Rectangle(3, 4)
  >>> rect.getPeri()
  14
  >>> rect.getArea()
  12

  这里需要注意的是,_ _init_ _()方法的返回值一定是None,不能是其他:

  example1: >>> class A:
           def _ _ init _ _(self):
              return " A for A-Cup"

        >>> cup = A()
        Traceback (most recent call last):
         file “<pyshell#9>”, line 1, in < module >
          cup = A()
        TypeError: _ _ init _ _() should return None, not ‘str’

  所以一般在需要进行初始化的时候才重写_ _ init _ _()方法,其实,这个_ _ init _ _()并不是实例化对象时第一个被调用的魔法方法。

1.2 _ _ new _ _(cls[, …])

  关于_ _new_ _(cls[, ...])方法:

_ _new_ _()才是在一个对象实例化的时候所调用的第一个方法,它跟其他魔法方法不同,它的第一个参数不是self而是这个类(cls),而其他的参数会直接传递给_ _init_ _()方法的。_ _new_ _()方法需要返回一个实例对象,通常是cls这个类实例化的对象,当然也可以返回其他对象。_ _new_ _()方法平时很少去重写它,一般让Python用默认的方案执行就可以了。但是当其继承一个不可变的类型的时候需要重写这个魔法方法。
class CapStr(str):    def __new__(cls, string):        string = string.upper()        return str.__new__(cls, string)

  运行上面代码:
  >>> a = CapStr(“Hello,Python!”)
  >>> a
  ‘HELLO,PYTHON!’

1.3 _ _ del _ _(self)

  如果说_ _init_ _()_ _new_ _()方法是对象的构造器的话,那么Python也提供了一个析构器,叫作_ _del_ _()方法。当对象将要被销毁的时候,这个方法就会被调用。
  但一定要注意的是,并非del x就相当于自动调用x._ _del_ _()_ _del_ _()方法是当垃圾回收机制回收这个对象的时候调用的。举个例子:

  example1: >>> class C:
           def _ _ init _ _ (self):
              print(“我是_ _ init _ _ 方法,我被调用了…”)
           def  _ _ del _ _ (self):
              print(“我是_ _ del _ _方法,我被调用了…”)

        >>> c1 = C()
        我是_ _ init _ _ 方法,我被调用了…
       >>> c2 = c1
       >>> c3 = c2
       >>> del c1
       >>> del c2
       >>> del c3
       我是_ _ del _ _方法,我被调用了…

2. 算术运算

  Python2.2以后,对类和类型进行了统一,做法就是将int ()float()str()List()tuple()这些BIF转换为工厂函数。

  >>>type(int)
  <class ‘type’>
  >>>type(len)
  <class ‘builtin_function_or_method’>
  >>>type(dir)
  <class ‘builtin_function_or_method’>
  >>>type(List)
  <class ‘type’>

  普通的BIF应该是<class 'builtin_function_or_method'>,而工厂函数则是<class 'type'>。如果定义一个类:

  example1: >>> class C:
           pass

        >>>type( C )
        <class ‘type’>

  类对象的类型也是type类型,其实所谓的工厂函数就是一个类对象。当你调用它们的时候,事实上就是创建一个相应的实例对象:

  example2: >>> a = int(“123”)
        >>> b = int(“456”)
        >>> a + b
        579

  通过上述例子,我们可以发现对象是可以进行计算的。当求a+b等于多少的时候,事实上Python就是在将两个对象进行相加 *** 作。
  Python的魔法方法还提供了自定义对象的数值处理,通过对下面这些魔法方法的重写,可以自定义任何对象间的算术运算。

2.1 算术 *** 作符

  表1 列举了算数运算相关的魔法方法。

表1 算数运算相关的魔法方法
魔法方法含义
_ _ add _ _(self, other)定义加法的行为:+
_ _ sub _ _(self, other)定义减法的行为:-
_ _ mul _ _(self, other)定义乘法的行为:*
_ _ truediv _ _(self, other)定义真除法的行为:/
_ _ floordiv _ _(self, other)定义整数除法的行为://
_ _ mod _ _(self, other)定义取模算法的行为:%
_ _ divmod _ _(self, other)定义当被divmod()调用时的行为
_ _ pow _ _ (self, other[, modulo])定义当被power()调用或**运算时的行为
_ _ lshift _ _ (self, other)定义按位左移位的行为:<<
_ _ rshift _ _ (self, other)定义按位右移位的行为:>>
_ _ and _ _ (self, other)定义按位与 *** 作的行为:&
_ _ xor _ _ (self, other)定义按位异或 *** 作的行为:^
_ _ or _ _ (self, other)定义按位或 *** 作的行为:竖线

  举个例子,下面定义一个比较特立独行的类:

  example1: >>> class New_int(int):
           def _ _ add _ _(self, other):
             return int. _ _ sub _ _(self, other)
           def _ _ sub _ _(self, other):
             return int. _ _ add _ _(self, other)

        >>>a = New_int(3)
        >>>b = New_int(5)
        >>>a + b
        -2
        >>>a - b
        8

  如果想自己写代码,不想通过调用Python默认的方案:

  example2: >>> class Try_int(int):
           def _ _ add _ _(self, other):
             return self + other
           def _ _ sub _ _(self, other):
             return self - other

       >>> a = Try_int(1)
       >>> b = Try_int(3)
       >>> a + b
       Traceback (most recent call last):
         file “<pyshell#44>”, line 1, in < module >
          a + b
         file “<pyshell#41>”, line 3, in _ _ add _ _
          return self + other
         file “<pyshell#41>”, line 3, in _ _ add _ _
          return self + other
         file “<pyshell#41>”, line 3, in _ _ add _ _
          return self + other
         [PrevIoUs line repeated 990 more times]
       RecursionError: maximum recursion depth exceeded

  为什么会陷入无限递归呢?问题就出在这里:
  def _ _ add _ _(self, other):
    return self + other
  当对象涉及加法 *** 作时,自动调用魔法方法_ _add_ _(),上边的魔法方法写的是return self + other,也就是返回对象本身加另外一个对象,就会又自动触发调用_ _add_ _()方法,这样就形成了无限递归。所以,应该为下面这样:

  example3: >>> class New_int(int):
           def _ _ add _ _(self, other):
             return int(self) + int(other)
           def _ _ sub _ _(self, other):
             return int(self) - int(other)

       >>> a = New_int(1)
       >>> b = New_int(3)
       >>> a + b
       4

  我们通过对指定魔法方法的重写,完全可以让Python根据我们的意愿去执行结果。

  example4: >>> class int(int):
           def _ _ add _ _(self, other):
             return int. _ _ sub _ _(self, other)

       >>> a = int(‘5’)
       >>> b = int(‘3’)
       >>> a + b
       2

  通过上面的例子我们可以发现,随着对Python学习的深入,Pyhton允许我们做的事情就更多、更灵活!

2.2 反运算

  表2列举了反运算相关的魔法方法。

表2 反运算相关的魔法方法
魔法方法含义
_ _ radd _ _(self, other)定义加法的行为:+(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rsub _ _(self, other)定义减法的行为:-(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rmul _ _(self, other)定义乘法的行为:*(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rtruediv _ _(self, other)定义真除法的行为:/(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rfloordiv _ _(self, other)定义整数除法的行为://(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rmod _ _(self, other)定义取模算法的行为:%(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rdivmod _ _(self, other)定义当被divmod()调用时的行为(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rpow _ _ (self, other[, modulo])定义当被power()调用或**运算时的行为(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rlshift _ _ (self, other)定义按位左移位的行为:<<(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rrshift _ _ (self, other)定义按位右移位的行为:>>(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rand _ _ (self, other)定义按位与 *** 作的行为:&(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ rxor _ _ (self, other)定义按位异或 *** 作的行为:^(当左 *** 作数不支持相应的 *** 作时被调用)
_ _ ror _ _ (self, other)定义按位或 *** 作的行为:竖线(当左 *** 作数不支持相应的 *** 作时被调用)

  不难发现,这里的反运算魔法方法跟之前的算术运算符保持相对应,不同之处就是反运算的魔法方法多了一个“r”,例如,_ _ add _ _()就对应_ _ radd _ _()。举个例子:

  example1: >>> a + b
        # 如果a对象的_ _ add _ _ ()方法没有实现或者不支持相应的 *** 作,那么Python就会自动调用b的_ _ radd _ _ ()方法

  example2: >>> class Nint(int):
           def _ _ radd _ _(self, other):
             return int. _ _ sub _ _(other, self)

       >>> a = Nint(5)
       >>> b = Nint(3)
       >>> a + b
       8
       # 由于a对象默认有_ _ add _ _ ()方法,所以b的_ _ radd _ _ ()没有执行
       >>> 1 + b
       -2

  关于反运算,要注意一点:对于a + bb_ _radd_ _(self, other)selfb对象,othera对象。看如下一个例子:

  example3: >>> class Nint(int):
           def _ _ rsub _ _(self, other):
             return int. _ _ sub _ _(self, other)

       >>> a = Nint(5)
       >>> 3 - a
       2

  所以对于注重 *** 作数顺序的运算符(例如减法、除法、移位),在重写反运算魔法方法的候,就一定要注意顺序问题了。

2.3 增量赋值运算

  Python也有大量的魔术方法可以来定制增量赋值语句,增量赋值其实就是一种偷懒的形式,它将 *** 作符与赋值来结合起来。例如:

  example1: >>> a = a + b
        # 写成增量赋值的形式就是:
       >>> a += b

2.4 一元 *** 作符

  一元 *** 作符就是只有一个 *** 作数的意思;像a+b这样,加号左右有ab两个 *** 作数叫作二元 *** 作符。
  Python支持的一元 *** 作符主要有_ _neg_ _()(表示正号行为),_ _pos_ _()(定义负号行为),_ _abs_ _()(定义当被abs()调用时的行为,就是取绝对值的意思),还有一个_ _invert_ _()(定义按位取反的行为)。

3. 简单定制

  基本要求:

定制一个计时器的类。startstop方法代表启动计时和停止计时。假设计时器对象t1print(t1)和直接调用t1均显示结果。当计时器未启动或已经停止计时,调用stop方法会给予温馨的提示。两个计时器对象可以进行相加:t1 + t2。只能使用提供的有限资源完成。

  这里需要限定只能使用哪些资源,因为Python的模块是非常多的。

  需要下面的资源:

使用time模块的localtime方法获取时间。time.localtime返回struct_time的时间格式。表现类:_ _str_ _ ()_ _repr_ _()魔法方法。

  example1: >>> class A:
           def _ _ str _ _ (self):
             return “Hello, Python!”

       >>> a = A()
       >>> print(a)
       Hello, Python!
       >>> a
       <_ _ main _ _.A object at 0x000001FE4E96CF60>

  example2: >>> class B:
           def _ _ repr _ _ (self):
             return “Hello, Python!”

       >>> b = B()
       >>> b
       Hello, Python!

  开始来编写代码:

import time as tclass MyTimer:    # 开始计时    def start(self):        self.start = t.localtime()        print("计时开始…")    # 停止计时    def stop(self):        self.stop = t.localtime()        print("计时结束!")

  写好基础之后,进行计算。.localtine()返回的是一个时间元组的结构,只需要前边6个元素,然后将stop的元素依次减去start对应的元素,将差值存放在一个新的列表里:

    #停止计时    def stop(self):        self.stop = t.localtime()        self._calc()        print("计时结束!")     # 内部方法,计算运行时间    def _calc(self):        self.lasted=[]        self.prompt = "总共运行了"        for index in range(6):            self.lasted.append(self.stop[index]- self.start[index])            self.prompt += str(self.lasted[index])        print(self.prompt)

  运行程序:
  >>> t1 = MyTimer()
  >>> t1.start()
  计时开始…
  >>> t1.stop()
  总共运行了00001-52
  计时结束!

  已经基本实现计时功能了,接下来需要完成"print(t1)和直接调用t1均显示结果",那就要通过重写_ _str_ _ ()_ _repr_ _()魔法方法来实现:

    def __str__(self):        return self.prompt    __repr__ = __str__

  运行程序:
  >>> t1 = MyTimer()
  >>> t1.start()
  计时开始…
  >>> t1.stop()
  计时结束!
  >>> t1
  总共运行了000005

  上面的程序如下运行的时候会出现问题:

  >>> t1 = MyTimer()
  >>> t1
  Traceback (most recent call last):
    file “<pyshell#28>”, line 1, in < module >
     t1
    file “D:\programming\python37\lib\IDlelib\rpc.py”, line 617, in displayhook
     text = repr(value)
    file “C:\Users\administrator\Desktop\1.py”, line 5, in _ _ str _ _
     return self.prompt
  AttributeError: ‘MyTimer’ object has no attribute ‘prompt’

  当直接执行t1的时候,Python会调用_ _str_ _()魔法方法,但运行程序会说这个类没有prompt属性。这是因为我们没有执行stop()方法,_calc()方法就没有被调用到,所以也就没有prompt属性的定义了,这时就需要我们使用_ _init_ _()魔法方法了。

    def __init__(self):        self.prompt = "未开始计时!"        self.lasted = []        self.start = 0        self.stop = 0

  运行程序:
  >>> t1 = MyTimer()
  >>> t1
  未开始计时!
  >>> t1.start()
  Traceback (most recent call last):
    file “<pyshell#31>”, line 1, in < module >
     t1.start()
  TypeError: ‘int’ object is not callable

  运行t1.start()又出错了,Python 这里抛出了一个异常:TypeError: ‘int’ object is not callable。这是说Python认为start是一个整型变量,而不是一个方法。因为在_ _init_ _()方法里,我们也命名了一个叫作self.start的变量,如果类中的方法名和属性同名,属性会覆盖方法。所以我们需要把所有的self.startself.end都改为self.beginself.end
  我们再将显示时间000005改为“年月日小时分钟秒”去显示,所以程序修改为:

    def __init__(self):        self.unit = ['年', '月', '日', '小时', '分钟', '秒']        self.prompt = "未开始计时!"        self.lasted = []        self.begin = 0        self.end = 0    # 计算运行时间    def _calc(self):        self.lasted=[]        self.prompt = "总共运行了"        for index in range(6):            self.lasted.append(self.end[index]- self.begin[index])            if self.lasted[index]:                self.prompt += (str(self.lasted[index]) + self.unit[index])        

  运行程序:
  >>> t1=MyTimer()
  >>> t1.start()
  计时开始…
  >>> t1.stop()
  计时结束!
  >>> t1
  总共运行了4秒

  然后再在适当的地方增加温馨提示:

    # 开始计时    def start(self):        self.begin = t.localtime()        self.prompt = "提示:请先调用stop()结束计时!"        print("计时开始…")    #停止计时    def stop(self):        if not self.begin:            print("提示:请先调用start()开始计时!")        else:            self.end = t.localtime()            self._calc()            print("计时结束!")    # 计算运行时间    def _calc(self):        self.lasted=[]        self.prompt = "总共运行了"        for index in range(6):            self.lasted.append(self.end[index] - self.begin[index])            if self.lasted[index]:                self.prompt += (str(self.lasted[index]) + self.unit[index])          # 为下一轮计算初始化变量        self.begin = 0        self.end = 0     

  最后,再重写一个魔法方法_ _add_ _(),让两个计时器对象相加会自动返回时间的和。

    def __add__(self, other):        prompt = "总共运行了"        result =[]        for index in range(6):            result.append(self.1asted[index] + other.lasted[index])            if result[index]:                prompt += (str(result[index]) + self.unit[index])        return prompt

  整个代码如下:

import time as tclass MyTimer:    def __init__(self):        self.unit = ['年', '月', '日', '小时', '分钟', '秒']        self.prompt = "未开始计时!"        self.lasted = []        self.begin = 0        self.end = 0       def __str__(self):        return self.prompt    __repr__ = __str__    # 开始计时    def start(self):        self.begin = t.localtime()        self.prompt = "提示:请先调用stop()结束计时!"        print("计时开始…")    #停止计时    def stop(self):        if not self.begin:            print("提示:请先调用start()开始计时!")        else:            self.end = t.localtime()            self._calc()            print("计时结束!")    # 计算运行时间    def _calc(self):        self.lasted=[]        self.prompt = "总共运行了"        for index in range(6):            self.lasted.append(self.end[index] - self.begin[index])            if self.lasted[index]:                self.prompt += (str(self.lasted[index]) + self.unit[index])          # 为下一轮计算初始化变量        self.begin = 0        self.end = 0       def __add__(self, other):        prompt = "总共运行了"        result =[]        for index in range(6):            result.append(self.lasted[index] + other.lasted[index])            if result[index]:                prompt += (str(result[index]) + self.unit[index])        return prompt

  运行程序:
  >>> t1=MyTimer()
  >>> t1
  未开始计时!
  >>> t1.stop()
  提示:请先调用start()开始计时!
  >>> t1.start()
  计时开始…
  >>> t1
  提示:请先调用stop()结束计时!
  >>> t1.stop()
  计时结束!
  >>> t1
  总共运行了10秒
  >>> t2 = MyTimer()
  >>> t2.start()
  计时开始…
  >>> t2.stop()
  计时结束!
  >>> t2
  总共运行了8秒
  >>> t1 + t2
  ‘总共运行了18秒’

  后半部分内容见零基础入门学Python(十二)—— 魔法方法(下)。

4. 属性访问 5. 描述符 6. 定制序列 7. 迭代器 8. 生成器 总结

以上是内存溢出为你收集整理的零基础入门学Python(十二)—— 魔法方法(上)全部内容,希望文章能够帮你解决零基础入门学Python(十二)—— 魔法方法(上)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1186136.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存