Python学习(六)

魔法方法

在python中,有一些内置好的特定的方法,这些方法在进行特定的操作时会自动被调用,称之为魔法方法。

构造和析构

魔法方法总是被双下横线包围,例如__init__;
是面向对象的Python的一切;
他们总是能在适当的时候被调用。

  • __init__(self[, …])
    相当于其他编程语言的构造方法,类在实例化对象的时候首先会调用的一个方法。
    在这里插入图片描述

  • __new__(cls[, …])
    init并不是实例化对象调用的第一个方法,new方法才是,它的第一个参数是cls,通常情况下是返回cls类的对象,也可以返回其他类的对象。
    new方法是极少去重写它的,Python会默认执行,但是当需要继承一个不可变类型又需要修改的时候,那么就需要重写了。
    在这里插入图片描述

  • __del__(self)
    当对象将要被销毁的时候这个方法会自动被调用,但是

del x 
并不等于调用了
x.__del__() 

del方法是当垃圾回收机制,即当没有任何变量去引用这个对象的时候,垃圾回收机制会自动销毁,这时才会调用对象的self方法。
在这里插入图片描述
注意:内置的__del__()方法并不是发生del操作的时候就会调用,当对象生成后,所有对它的引用都被del后才会启动垃圾回收机制,才会调用__del__()方法。

算数运算

在Python2.2之前类和类型是分开的,类是属性和方法的封装,类型是如整型、浮点型、字符串这些类型,但是在Python2.2后,试图对两者进行统一,做法就是将int、float、string、list、tuple这些内置函数通常转化为工厂函数。

  • 什么是工厂函数

参考

在这里插入图片描述
在这里插入图片描述
原因: 当a+b,识别到加法会先调用前者a的add,返回self+other,即返回了a+b,就又运行了加法add,进入了无限递归。
所以在实现的时候,一定要注意避免出现无限递归的情况。
稍作修改
在这里插入图片描述

  • 一些算数运算
方法相应二元算数运算符
__add__(self, other)定义加法的行为:+
__sub__(self, other)定义减法的行为:-
__mul__(self, other)定义乘法的行为:*
__truediv__(self, other)定义真除法的行为:/
__floordiv__(self, other)定义整数除法的行为://
__mod__(self, other)定义取模算法的行为:%
__divmod__(self, other)定义当被divmod()调用时的行为【divmod(a, b)返回值是一个元组(a//b, a%b)】
__pow__(self, other[, modulo])定义当被pow()调用或**运算时的行为
__lshift__(self, other)定义按位左移位的行为:<<
__rshift__(self, other)定义按位右移位的行为:>>
__and__(self, other)定义按位与操作的行为:&
__xor__(self,other)定义按位异或操作的行为:^
__or__(self, other)定义按位或操作的行为:|

通过对指定魔法方法的重写,可以让Python根据自己的意图来实现程序
在这里插入图片描述

魔法方法参考

  • 反运算
    在这里插入图片描述
    这里是3-1,并不是1-3,如果想让1-3,那么就要互换int.sub()中self, other的位置,举例说明
    在这里插入图片描述

定制一个简单的类

在这里插入图片描述

  • 需要的资源
    (1)使用time模块的localtime方法获取时间

time模块详解

在这里插入图片描述
(2)time.localtime返回struct_time的时间格式
(3)表现你的类:重写__str__和__repr__
在这里插入图片描述

  • 程序MyTimer
import time as t

class MyTimer():
    def __str__(self):
        return self.prompt

    __repr__ = __str__
    # 开始计时
    def start(self):
        self.start = t.localtime()
        print("计时开始...")

    # 停止计时
    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])

在这里插入图片描述
但是这里有一个问题就是,如果定以后直接调用,就会报错
在这里插入图片描述
因为这时,prompt还没有被定义,这时就需要所有属于实例对象的变量先在init中定义

import time as t

class MyTimer():
	# 添加init定义
    def __init__(self):
        self.prompt = "未开始计时!"
        self.lasted = []
        self.start = 0
        self.stop = 0
        
    def __str__(self):
        return self.prompt

    __repr__ = __str__
    # 开始计时
    def start(self):
        self.start = t.localtime()
        print("计时开始...")

    # 停止计时
    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])

在这里插入图片描述
这时在执行虽然不会直接调用t1出错了,但是运行起来却又出现“整型不能被调用”问题,这里是由于在init中将self.start定义为0导致,因为类的方法名和属性名一样时,属性会覆盖方法,这里就认为start是属性。
这时只需要将start和stop改一下名字即可
并且在这里改变一下显示的方式和累加计时

import time as t

class 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 __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
    # 开始计时
    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]: # 为0不显示  
                self.prompt += str(self.lasted[index]) + self.unit[index]
        # 为下一轮计时初始化变量
        self.begin = 0
        self.end = 0

在这里插入图片描述

  • 代码存在的问题
    (1)生成的时间会存在负数的情况
    在这里插入图片描述
    (2)精度不够,只能到秒

属性访问

(1)直接访问属性
(2)通过getattr()访问
在这里插入图片描述
(3)利用property(),以属性的方式访问属性
在这里插入图片描述

  • __getattr__(self, name)
    定义当用户试图获取一个不存在的属性时的行为
  • __getattribute__(self, name)
    定义当该类的属性被访问时的行为
  • __setattr__(self, name, value)
    定义当一个属性被设置时的行为
  • __delattr__(self, name)
    定义当一个属性被删除时的行为
>>> class C:
	def __getattribute__(self, name):
		print("getattribute")
		return super().__getattribute__(name)
	def __getattr__(self, name):
		print("getattr")
	def __setattr__(self, name, value):
		print("setattr")
		super().__setattr__(name, value)
	def __delattr__(self, name):
		print("delattr")
		super().__delattr__(name)

在这里插入图片描述

  • 练习
    在这里插入图片描述
class Rectangle:
    def __init__(self, width=0, height=0):
        self.width = width
        self.height = height

    def __setattr__(self, name, value):
        if name == 'square':
            self.width = value
            self.height = value
        else:
            self.name = value

    def getArea(self):  # 获得面积
        return self.width * self.height
# 输入 r1 = Rectangle(4, 5)
# 这样写会出现一个无限递归,因为执行__init__中的self.width和self.height赋值语句,会触发__setattr__中的else后的语句self.name  = value,再重复调用__setattr__,这样就会无限递归下去
# =====================================================================================================
# 下面进行改进
class Rectangle:
    def __init__(self, width=0, height=0):
        self.width = width
        self.height = height

    def __setattr__(self, name, value):
        if name == 'square':
            self.width = value
            self.height = value
        else:
            super().__setattr__(name, value)

    def getArea(self):  # 获得面积
        return self.width * self.height

在这里插入图片描述
另一种改进方法就是给一个特殊属性dict,dict是以字典的形式显示出当前对象的所有属性以及对应的值
在这里插入图片描述

class Rectangle:
    def __init__(self, width=0, height=0):
        self.width = width
        self.height = height

    def __setattr__(self, name, value):
        if name == 'square':
            self.width = value
            self.height = value
        else:
            self.__dict__[name] = vallue

    def getArea(self):  # 获得面积
        return self.width * self.height

描述符

描述符就是将某种特殊类型的类的实例指派给另一个类的属性。

__get__(self, instance, owner)
# 用于访问属性,返回属性的值
__set__(self, instane, value)
# 将在属性分配操作中调用,不返回任何内容
__delete__(self, instance)
# 控制删除操作,不返回任何内容
  • 实例
class MyDescriptor
	def __get__(self, instance, owner):
		print("getting...", self, instance, owner)
	def __set__(self, instance, value):
		print("setting...", self, instance, value)
	def __delete__(self, instnce):
		print("deleting...", self, instance)

class Test:
	x = MyDescriptor()

【ps:这里改用spyder编辑了,输入处显示方式改变,实际操作同python idle相同】
这里就是将某种特殊类型的类(MyDescriptor)的实例(MyDescriptor())指派给另一个类(Test)的属性(x),就说明MyDescriptor就是x的描述符。
在这里插入图片描述
实例化对象后,用text.x强制打印
可以看到打印出三个参数,第一个是self的参数描述符类MyDescriptor本身的实例,第二个是instance的参数类的拥有者Test的实例test,第三个就是拥有者类Test本身
验证一下
在这里插入图片描述
在这里插入图片描述
对实例化对象进行赋值,出现赋值调用set的特殊方法,打印self、instance和value
在这里插入图片描述
del同理,打印self和instance

  • 定义一个MyProperty
    之前提到的property其实就是一个描述符
class MyProperty:
    def __init__(self, fget=None, fset=None, fdel=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        
    def __get__(self, instance, value):
        return self.fget(instance)
    
    def __set__(self, instance, value):
        self.fset(instance, value)
        
    def __delete__(self, instance):
        self.fdel(instance)
        
class C:
    def __init__(self):
        self._x = None
   
    def getX(self):
        return self._x
    
    def setX(self, value):
        self._x = value

    def delX(self):
        del self._x
        
    x = MyProperty(getX, setX, delX)

在这里插入图片描述
同样的这里将MyProperty的实例MyProperty()指派给类C的属性x,对类C的实例对象c的x属性赋值,调用setX返回c._x再进行操作。

  • 练习
    在这里插入图片描述
class Celsius:
    def __init__(self, value = 26.0):
        self.value = float(value)
    
    def __get__(self, instance, owner):
        return self.value
    
    def __set__(self, instance, value):
        self.value = float(value)
        
class Fahrenheit:  
    def __get__(self, instance, owner):
        return instance.cel * 1.8 +32
    
    def __set__(self, instance, value):
        instance.cel = (float(value) - 32) / 1.8
        
class Temperature:
    cel = Celsius()
    fah = Fahrenheit()

在这里插入图片描述

定制容器

  • 协议
    协议Protocols相似于接口,规定了必须要定义的方法。而在Python中协议更像是一种指南。
  • 容器类型的协议
    (1)定制不可变容器
    只需定义__len__()和__getitem__()方法
    (2)定制可变容器
    除__len__()和__getitem__()方法外,还需定义__setitem__()和__delitem__()两个方法

Python魔法方法详解

  • 练习
    编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。
class CountList:
    def __init__(self, *args): # 星号代表参数是可变数量的
        self.values = [x for x in args] # 依次取列表中元素
        self.count = {}.fromkeys(range(len(self.values)),0)
        # fromkeys 用于创建一个新字典
        
    def __len__(self):
        return len(self.values)
    
    def __getitem__(self, key):
        self.count[key] += 1
        return self.values[key]        

在这里插入图片描述

迭代器

提供迭代方法的容器称之为迭代器。
通常的迭代器有序列、列表、元组、字符串、字典、文件。

  • for语句迭代
    在这里插入图片描述
  • 字典迭代
    在这里插入图片描述
  • 关于迭代操作,Python提供了两个BIF内置函数inter()和next()
    inter() 即iteration
    对于容器对象调用iter就得到它的迭代器,调用next()就会返回下一个值,当迭代器没有值可以返回了,Python就会抛出一个“StopIteration”的异常,此时迭代结束。
    在这里插入图片描述
    这样就可以知道for语句是如何执行的
    利用while语句来模拟for语句的执行
    在这里插入图片描述
  • 迭代器的魔法方法
# 两个魔法方法分别对应两个BIF容器的实现
iter()
--> __iter__()	# 返回迭代器本身
next()
--> __next__()	# 决定迭代器的迭代规则
  • 实现斐波那契数列的打印
class Fibs:
    def __init__(self, n=10):
        self.a = 0
        self.b = 1
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > self.n:
            raise StopIteration
        return self.a 

在这里插入图片描述


生成器

生成器并不涉及魔法方法、类和对对象,只通过普通的函数实现。生成器实际上是迭代器的一种实现。
生成器延续了Python简洁的特点,并且使协同程序的概念得以实现,协同程序就是可以运行的独立函数调用,函数可以暂停或挂起,并在需要的时候从程序离开的地方继续或者重新开始。

Generator 实例

def myGen():
    print("生成器被执行!")
    yield 1
    yield 2
    # 一旦一个函数中出现yield语句
    # 那么就说明这个函数被定义为生成器
    # yield就相当于普通函数中的return
    # 和return的区别:
    # 出现yield,就将yield后的参数返回,并暂停在yield处

在这里插入图片描述

  • 实现斐波那契数列
def libs():
    a = 0
    b = 1
    while True:
        a, b = b, a + b
        yield a 
        # 由于有yield,所以while True不会变成死循环

在这里插入图片描述

  • 推导式
    (1)列表推导式
    在这里插入图片描述
    (2)字典推导式
    在这里插入图片描述
    有“:”的是字典,没有的是集合
    (3)集合推导式
    在这里插入图片描述
    (4)没有字符串推导式
    在这里插入图片描述
    (5)元组(tuple)推导式
    在这里插入图片描述
    打印元组e发现e是一个生成器推导式
    在这里插入图片描述
    生成器推导式如果作为函数的参数,是可以直接写推导式,不需要加括号
    【100以内不能被2整除的整数和】
    在这里插入图片描述

[扩展阅读] 提高你的 Python:解释 yield 和 Generators(生成器)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值