首页 > 编程语言 >python魔术方法模拟篇

python魔术方法模拟篇

时间:2023-07-16 18:55:09浏览次数:45  
标签:__ MyList python self 魔术 def ._ data 模拟

6,模拟篇

  • __call__
  • __len__
  • __length_hint__
  • __getitem__
  • __setitem__
  • __delitem__
  • __reversed__
  • __contains__
  • __iter__
  • __missing__
  • __enter__和__exit__

__call__方法

所谓的callable就是可以以函数调用的形式来使用的对象,那想让一个类的对象成为callable,我们需要给它定义这个__call__ method,下面我们建立一个Multiplier的object o 并且让它的乘法倍数是3,然后我们可以像使用函数一样使用这个对象o,那么它实际上调用的就是这个__call__ method ,我们给进的参数4,就被传到了这个arg里,所以我们理论上该返回一个3乘以4就是12

class Multiplier:
    def __init__(self, mul):
        self.mul = mul

    def __call__(self, arg):
        return self.mul * arg


o = Multiplier(3)
print(o(4))
# 12

 

__len__方法

接下来我们看emulate container type,所谓container就是容器,在python中我们最常见的容器是list跟Dictionary,它们呢也分别对应了两个类别,一个叫sequence一个叫mapping。下面我们写了一个套皮list,我们要看的魔术方法是__len__,它要返回你这个container的长度,这里我直接借用list实现,返回我这个self.data的长度,我们建立了一个MyList object然后打印它的长度就是2,这个__len__最主要的作用呢就是被这个方法builtin方法len调用。

class MyList:
    def __init__(self, data):
        self._data = data

    def __len__(self):
        return len(self._data)


x = MyList([1, 2])
print(len(x))
# 2

如果你这个object没有被定义__boo__这个魔术方法,当你把它当作一个条件去判断的时候,它就会尝试去找这个__len__魔术方法,如果他的长度是0认为是False,否则认为是True,这个特性和我们所有builtin的container都是一样的,我们平常也会写if lst   if dct,我们运行下面的代码是有输出的,那如果我们把这个MyList初始化为空呢它就没有了

class MyList:
    def __init__(self, data):
        self._data = data

    def __len__(self):
        return len(self._data)


x = MyList([1, 2])
if x:
    print("Yeah")
# Yeah

 

__length_hint__方法

说完__len__我们稍微提一下这个__length_hint__,__length_hint__是返回一个估计的长度,它是被这个operator.length_hint调用的,我们运行一下,结果是2,但是这个magic method实际上是很少用到的

import operator


class MyList:
    def __init__(self, data):
        self._data = data

    def __length_hint__(self):
        return len(self._data)


x = MyList([1, 2])
print(operator.length_hint(x))
# 2

 

__getitem__方法

__getitem__就是你用方括号的时候会调用的这么一个魔术方法,比如让你打印x[0]的时候,这个0就会被放到key里传进来,这里的结果就是返回它的index为0的那个值,运行结果是1

class MyList:
    def __init__(self, data):
        self._data = data

    def __getitem__(self, item):
        return self._data[item]


x = MyList([1, 2])
print(x[0])
# 1

我们做一个小小的改变,有的时候我们拿到的数据这个0是一个string,如果我们不小心把这个string作为index传进去了,那list是没有办法认出来这种index的,所以它就会报错

class MyList:
    def __init__(self, data):
        self._data = data

    def __getitem__(self, item):
        return self._data[item]


x = MyList([1, 2])
print(x["0"])
TypeError: list indices must be integers or slices, not str

这个时候我们可以在我们这个类的__getitem__里面,把这个key先转化成int,那这样一来就算传进来的key是一个string,它也能正确的返回这个值了

class MyList:
    def __init__(self, data):
        self._data = data

    def __getitem__(self, item):
        return self._data[int(item)]


x = MyList([1, 2])
print(x["0"])
# 1

 

__setitem__方法

当我们用方括号去读取一个值的时候使用的是__getitem__,但是当我们用方括号去赋值的时候调用的就是__setitem__,例如给x[0]赋值为3,然后打印x[0],那么运行结果就是3。

class MyList:
    def __init__(self, data):
        self._data = data

    def __getitem__(self, item):
        return self._data[int(item)]

    def __setitem__(self, key, value):
        self._data[key] = value


x = MyList([1, 2])
x[0] = 3
print(x["0"])
# 3

x[3] = 4
print(x["0"])
IndexError: list assignment index out of range

那比如说有时候我们给这个list赋值它超出了这个index,比如x[3] = 4,它就会报错,如果我希望这个list,可以在这个Index错误的时候直接无视它不要管它,我们就可以把这个__setitem__稍微改一下,如果出现IndexError直接pass算了,这个时候当我给x[3]赋值它就当无事情发生了

class MyList:
    def __init__(self, data):
        self._data = data

    def __getitem__(self, item):
        return self._data[int(item)]

    def __setitem__(self, key, value):
        try:
            self._data[key] = value
        except IndexError:
            pass


x = MyList([1, 2])
x[3] = 4
print(x["0"])
# 1

 

__delitem__方法

__delitem__就是删除一个key,就是被del这个关键字触发的,x本来是[1, 2, 3]然后把x[1]删除掉,再打印x[1],由于原来的x[1]这个2被删除掉了,现在的x[1]就是3了

class MyList:
    def __init__(self, data):
        self._data = data

    def __getitem__(self, item):
        return self._data[int(item)]

    def __setitem__(self, key, value):
        try:
            self._data[key] = value
        except IndexError:
            pass

    def __delitem__(self, key):
        self._data = self._data[0: key] + self._data[key+1:]


x = MyList([1, 2, 3])
del x[1]
print(x[1])
# 3

 

__reversed__方法

__reversed__会被python的builtin function reversed调用,reverse平时就是把这个东西反过来

class MyList:
    def __init__(self, data):
        self._data = data

    def __getitem__(self, item):
        return self._data[int(item)]

    def __setitem__(self, key, value):
        try:
            self._data[key] = value
        except IndexError:
            pass

    def __delitem__(self, key):
        self._data = self._data[0: key] + self._data[key+1:]

    def __reversed__(self):
        return MyList(self._data[::-1])


x = MyList([1, 2, 3])
print(reversed(x)._data)
# [3, 2, 1]

 

__contains__方法

__contains__方法它是当你使用in这个操作的时候被调用的

...
    def __contains__(self, item):
        return item in self._data


x = MyList([1, 2, 3])
print(2 in x)
print(5 in x)
# True
# False

 

__iter__方法

__iter__是返回这个container的一个iterator,这里我们就直接把保存的这个_data的iterator给返回回去,这样我们在做for i in x的时候,我们实际上迭代的就是里面的那个list,可以看到打印出来的就是1,2,3

...
    def __iter__(self):
        return iter(self._data)


x = MyList([1, 2, 3])
for i in x:
    print(i)
# 1
# 2
# 3

 

__missing__方法

container里面有一个稍微特殊点的的magic method叫做__missing__,这个__missing__只有在dict的subclass里面才有用,也就是你新建的这个class,必须要继承它builtin的这个dict,它的作用是当你想在这字典里找一个key,但找不着的时候它应该干嘛,比如下面我们新建了一个字典之后我们想找d[0],但是我们知道这个字典里面现在一个key都没有,那当这个字典找不到这个key的时候,它就会把这个key仍到__missing__里面来问应该怎么办,这里我们统一返回0,可以看到那这种情况下d[0]就是0了

class MyDict(dict):
    def __missing__(self, key):
        return 0


d = MyDict()
print(d[0])

 

__enter__和__exit__方法

说完了container,我们介绍一下这个context type,我们平常在写python的时候经常会用到with什么东西,这个with后面的东西就叫做context,想自定义一个context,我们需要定义它的__enter__和__exit__ magic method,下面我们做了一个最简单的Timer,也就是在当进入这个context的时候,记录一下时间,再出去这个context的时候打印一下这个context里面花了多少时间

import time


class Timer:
    def __enter__(self):
        self.start = time.time()

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"T: {time.time()- self.start}")


with Timer():
    _ = 1000 * 1000
# T: 0.0

 有的时候我们会遇见这种with Timer() as t的用法,这个as t 就是把__enter__这个的函数的返回值给保存到 t 里面,在这个部分最常见的操作就是直接把self给返回回去,比如我们就这样做然后在里面打印一下t.start,可以看到把这个context的起始时间就给打印出来了

import time


class Timer:
    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"T: {time.time()- self.start}")


with Timer() as t:
    print(t.start)
    _ = 1000 * 1000
1689503740.810218
T: 0.0

那你可能会发现这个__exit__函数里面还有一些其他的argument,它们分别是exc_type、exc_value还有traceback,这几个东西呢主要是在出现exception的时候使用到,比如我们把刚才那个操作变成1000 / 0 ,然后我们把这几个argument都打印出来,可以看到exception type是zero division error,exception value是division by zero,然后这个traceback是一个object,注意这里我们依然把这个程序跑的时间给记录下来了,然后python才报了错,也就是说即便在这个context里面它raise了一个exception,我timer里__exit__函数的全部内容依然被执行了,所以这个context非常适合拿来释放资源,比如我们常常鼓励大家在打开文件的时候使用with,它就可以防止你忘记了把这个文件关上,同时就算程序报错了你依然可以把这个文件成功的关上

import time


class Timer:
    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type, exc_val, exc_tb)
        print(f"T: {time.time()- self.start}")


with Timer() as t:
    _ = 1000 / 0

 

标签:__,MyList,python,self,魔术,def,._,data,模拟
From: https://www.cnblogs.com/jiushao-ing/p/17557340.html

相关文章

  • python:processing
     """进程"""importmultiprocessingimportthreadingimporttimeimportthreadSingdefdance():""":return:"""whileTrue:print('dance')time.sl......
  • 【Python】从同步到异步多核:测试桩性能优化,加速应用的开发和验证
    测试工作中常用到的测试桩mock能力在我们的测试工作过程中,可能会遇到多个项目并行开发的时候,后端服务还没有开发完成,或者我们需要压测某个服务,这个服务测在试环境的依赖组件(如MQ)无法支撑我们svr的并发访问的场景,这个时候我们可能就需要手写一个服务,来替代测试环境的这些依赖组......
  • Noip优质模拟赛口胡题解
    HDU5719题意概括:第一行输入t表示输入数据,每组数据第一行n,表示对1—n进行排序。接下来输入n个数b[n]表示排列中第i个数之前的最小值为b[i]。第三行n个数c[n],表示排列中第i个数之前的最大值为c[i]。解题思路:递推,排除掉6种不可能的情况,1、b[i]>b[i-1]2、c[i]<c[i-1]3、b[i]......
  • 相场模拟——枝晶生长(COMSOL模拟雪花形成)
    一、介绍1.1物理含义雪花是人们最常见的枝晶。枝晶生长是一种生长的不稳定现象,常起因于过冷的液体,或晶体的生长速度受限于溶质原子向固体表面的扩散速度。造成以上条件的原因,可以是液相中负的温度梯度,也可以是负的浓度梯度。在这种模式下,晶体倾向于在其棱角处优先生长,从而形成......
  • 2023.07.16 高质量 NOIP 模拟赛题解
    HDU5719Arrange【模拟】给定数列\(B_n,C_n\),求出满足\[B_i=\min_{j=1}^i\{A_j\},\quadC_i=\max_{j=1}^i\{A_j\}\]的排列\(A\)的数量。维护每个位置可能的数字数量,然后乘法原理即可。代码:http://acm.hdu.edu.cn/viewcode.php?rid=38654445。HDU5807KeepInTouch......
  • 空套件python
    空套件Python:了解Python中的空值和None在Python中,空值是表示没有值的特殊对象。在很多编程语言中,空值通常用null或nil表示,但在Python中,我们用None来表示空值。本文将为您介绍Python中的空值和None的概念及其在代码中的应用。什么是None?None是Python的一个特殊常量,用于表示空值或......
  • 金仓数据库python操作
    金仓数据库Python操作金仓数据库(Kingbase)是一种高性能的关系型数据库管理系统,其功能强大并且支持SQL语言,是企业级应用中常用的数据库之一。本文将介绍如何在Python中使用金仓数据库进行操作,并提供代码示例。安装金仓数据库驱动在使用Python操作金仓数据库之前,需要先安装相关的驱......
  • HHHOJ #1247. 「NOIP 2023 模拟赛 20230715 A」1 题解--zhengjun
    法老找来的题,说是找了三道其他模拟赛的T4拼成T1~T3,另外搞了道T4。思维好题,但是放在T1有点搞心态,但是还好大样例够强,400没挂。然而T3大样例输出错了,浪费了我0.5h,差评。首先发现向左走之后向右走是一定不优的,所以最短路的情况只能先向右再向左。考虑枚举起点\(s......
  • 用python怎么敲出下划线
    用Python怎么敲出下划线在Python中,要敲出下划线可以使用不同的方法,这取决于你想在什么环境下使用下划线。下面将介绍几种常见的情况和对应的解决方案。1.在字符串中敲出下划线如果你想在一个字符串中敲出下划线,可以使用转义字符\来实现。下面是一个示例代码:text="Hello\_Wor......
  • 用python爬去电影评分保存进mysql
    用Python爬取电影评分保存进MySQL在今天的数字化时代,电影评分是人们选择观看电影的重要参考因素之一。那么,如何通过Python爬取电影评分数据,并保存到MySQL数据库中呢?本文将为你提供一种简单的方法来实现这个目标。准备工作在开始之前,我们需要确保已经安装了Python和MySQL,并且已经......