ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-04-24 14:56:17  阅读:191  来源: 互联网

标签:index 入门 Python self 魔法 t1 int other def


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


魔法方法(上)


  需要学习的基础知识有:构造和析构、算术运算、简单定制、属性访问、描述符、定制序列、迭代器、生成器等。因本部分内容较多,故分为上下两个篇章。
    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 “

  所以一般在需要进行初始化的时候才重写_ _ 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)
  

  普通的BIF应该是,而工厂函数则是。如果定义一个类:

  example1: >>> class C:
           pass

        >>>type( C )
       

  类对象的类型也是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 “

  为什么会陷入无限递归呢?问题就出在这里:
  def _ _ add _ _(self, other):
    return self + other
  当对象涉及加法操作时,自动调用魔法方法_ _add_ _(),但上边的魔法方法写的是returm 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
       

  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 “

  当直接执行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 “

  运行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. 生成器

标签:index,入门,Python,self,魔法,t1,int,other,def
来源: https://blog.51cto.com/u_15178976/2729120

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有