首页 > 其他分享 >类的魔法方法

类的魔法方法

时间:2024-05-07 12:34:22浏览次数:21  
标签:__ name self 魔法 pass print 方法 def

【引入】
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类
这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
init :初始化类时触发
del :删除类时触发
new :构造类时触发
str :str函数或者print函数触发
repr :repr或者交互式解释器触发
doc :打印类内的注释内容
enter :打开文档触发
exit :关闭文档触发
getattr : 访问不存在的属性时调用
setattr :设置实例对象的一个新的属性时调用
delattr :删除一个实例对象的属性时调用
setitem :列表添加值
getitem :将对象当作list使用
delitem :列表删除值
call :对象后面加括号,触发执行
iter :迭代器
【一】init(),del()__,new()
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类

这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发

这个方法是一个类的构造函数,与之对应的__del__()是析构函数,通过此方法我们可以定义一个对象的初始操作。

但实际上,新式类的__new__()才是真正的初始化函数。

class A(object):
def init(self):
print('init')

def __new__(cls, *args, **kwargs):
    print('__new__')
    # cls表示一个类,一个当前要被实例化的类,参数由py解释器自动提供
    return super().__new__(cls, *args, **kwargs)

def __del__(self):
    print('__del__')

a = A()
print('do something')

new

init

do something

del

实际上,

new()负责创建一个对象,

__init__负责定制化该对象,即是在对象创建好之后初始化变量

既然知道了__new__()方法,我们是不是可以考虑一下,如何应用它呢?最常见的就是单例模式了,下面给出实现实例。
class Singleton(object):
_instance = None

def __new__(cls, *args, **kwargs):
    """
    注意这实际上是一个类方法, cls 表示当前类
    :param args:
    :param kwargs:
    :return:
    """
    if cls._instance is None:
        cls._instance = super().__new__(cls, *args, **kwargs)

    return cls._instance

s1 = Singleton()
s2 = Singleton()
if s1 is s2:
print('yeah')
【二】str,repr
【1】str
__str__方法会在对象被打印时自动触发
print功能打印的就是它的返回值
我们通常基于方法来定制对象的打印信息
该方法必须返回字符串类型
class People:
def init(self, name, age):
self.name = name
self.age = age

def __str__(self):
    # 返回类型必须是字符串
    return f'<Name:{self.name} Age:{self.age}>'

person = People('dream', 18)
print(person) # 触发p.str(),拿到返回值后进行打印

<Name:dream Age:18>

【2】repr
repr或者交互式解释器触发
class School:
def init(self, name, addr, type):
self.name = name
self.addr = addr
self.type = type

def __repr__(self):
    return 'School(%s,%s)' % (self.name, self.addr)

def __str__(self):
    return '(%s,%s)' % (self.name, self.addr)

s1 = School('dreamCity', '北京', '私立')
print('from repr: ', repr(s1))
print('from str: ', str(s1))
print(s1)
【3】小结
str函数或者print函数---> obj.str()
repr或者交互式解释器---> obj.repr()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
【三】__del__方法
__del__会在对象被删除时自动触发。

由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法

但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下

关于系统资源的回收,Python的垃圾回收机制便派不上用场了
需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
class MySQL:
def init(self, ip, port):
# 伪代码,发起网络连接,需要占用系统资源
self.conn = connect(ip, port)

def __del__(self):
    # 关闭网络连接,回收系统资源
    self.conn.close()

在对象obj被删除时,自动触发obj.del()

obj = MySQL('127.0.0.1', 3306)
class Person():
def init(self, name, age):
print(f'这是 :>>>> {name} , age :>>>> {age}')

def __del__(self):
    print(f"我离开你了")

person = Person("dream", 18)
print("这是断层")
【四】isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)检查是否obj是否是类cls 的对象
class Bar():
pass

class Foo(object):
pass

foo = Foo()
bar = Bar()

isinstance(obj,cls)检查是否obj是否是类cls的对象

res_Foo = isinstance(foo, Foo)
res_Bar = isinstance(foo, Bar)
print(res_Foo)
print(res_Bar)
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object):
pass

class Bar(Foo):
pass

res = issubclass(Bar, Foo)
print(res)
【五】doc
class Foo:
'我是描述信息'
pass

print(Foo.doc)
class Foo:
'我是描述信息'
pass

class Bar(Foo):
pass

该属性无法继承给子类

print(Bar.doc)
【六】enter__和__exit

我们知道在操作文件对象的时候可以这么写

with open('a.txt') as f:
  '代码块'
上述叫做上下文管理协议,即with语句
为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
class Open:
def init(self, name):
self.name = name

def __enter__(self):
    print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    # return self

def __exit__(self, exc_type, exc_val, exc_tb):
    print('with中代码块执行完毕时执行我啊')

with Open('a.txt') as f:
print('=====>执行代码块')
# print(f,f.name)
exit()中的三个参数分别代表

异常类型

异常值

追溯信息

with语句中代码块出现异常,则with后的代码都无法执行

class Open:
def init(self, name):
self.name = name

def __enter__(self):
    print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

def __exit__(self, exc_type, exc_val, exc_tb):
    print('with中代码块执行完毕时执行我啊')
    print(exc_type)
    print(exc_val)
    print(exc_tb)

with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('着火啦,救火啊')
print('0' * 100) # ------------------------------->不会执行
如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样
with后的语句正常执行
class Open:
def init(self, name):
self.name = name

def __enter__(self):
    print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

def __exit__(self, exc_type, exc_val, exc_tb):
    print('with中代码块执行完毕时执行我啊')
    print(exc_type)
    print(exc_val)
    print(exc_tb)
    return True

with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('着火啦,救火啊')
print('0' * 100) # ------------------------------->会执行

出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量

=====>执行代码块

with中代码块执行完毕时执行我啊

<class 'AttributeError'>

着火啦,救火啊

<traceback object at 0x000002591C0AC140>

0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

【七】setattr,delattr,getattr
【1】方法介绍
getattr(self, name): 访问不存在的属性时调用

setattr(self, name, value):设置实例对象的一个新的属性时调用

delattr(self, name):删除一个实例对象的属性时调用

class Foo:
x = 1

def __init__(self, y):
    self.y = y

def __getattr__(self, item):
    print(f'----> from getattr:你找的属性不存在 :>>>> {item}')

def __setattr__(self, key, value):
    print(f'----> from setattr  key : {key}  value : {value}')
    # self.key=value #这就无限递归了,你好好想想
    # self.__dict__[key]=value #应该使用它

def __delattr__(self, item):
    print(f'----> from delattr :>>>> {item}')
    # del self.item #无限递归了
    self.__dict__.pop(item)

__setattr__添加/修改属性会触发它的执行

f1 = Foo(10)

因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值

print(f1.dict)
f1.z = 3
print(f1.dict)

__delattr__删除属性的时候会触发

f1.dict['a'] = 3 # 我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.dict)

__getattr__只有在使用点调用属性且属性不存在的时候才会触发

f1.xxxxxx
对象属性查找顺序
首先访问__getattribute__()魔法方法(隐含默认调用,无论何种情况,均会调用此方法
去实例对象t中查找是否具备该属性: t.dict 中查找,每个类和实例对象都有一个__dict__的属性
若在t.__dict__中找不到对应的属性, 则去该实例的类中寻找,即 t.class.dict
若在实例的类中也招不到该属性,则去父类中寻找,即 t.class.bases.__dict__中寻找
若以上均无法找到,则会调用__getattr__方法,执行内部的命令(若未重载 getattr 方法,则直接报错:AttributeError)
【2】二次加工标准类型(包装)
(1)包装
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)

继承list所有的属性,也可以派生出自己新的,比如append和mid

class List(list):
def append(self, p_object):
' 派生自己的append:加上类型检查'
if not isinstance(p_object, int):
raise TypeError('must be int')
super().append(p_object)

@property
def mid(self):
    '新增自己的属性'
    index = len(self) // 2
    return self[index]

l = List([1, 2, 3, 4])
print(l)
l.append(5)
print(l)

l.append('1111111') #报错,必须为int类型

print(l.mid)

其余的方法都继承list的

l.insert(0, -123)
print(l)

l.clear()
print(l)
(2)授权
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。

授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

import time

class FileHandle:
def init(self, filename, mode='r', encoding='utf-8'):
# 定义一个文件句柄 ---- 文件打开 并 将文件句柄给 self.file
self.file = open(filename, mode, encoding=encoding)

def write(self, line):
    t = time.strftime('%Y-%m-%d %T')
    self.file.write('%s %s' % (t, line))

def __getattr__(self, item):
    # 返回 映射方法 ---- 映射自己self.file 是否存在 read 方法
    # print(item) # read
    return getattr(self.file, item)

实例化类得到对象

f1 = FileHandle('b.txt', 'w+')

写入文本内容

f1.write('你好啊')

移动光标

f1.seek(0)

触发 getattr(self, item) 方法 , self 是 f1 对象 ,item 是 read 方法

print(f1.read())

关闭 对象

f1.close()

自定义文件处理器

class FileHandle:
def init(self, filename, mode='r', encoding='utf-8'):
if 'b' in mode:
self.file = open(filename, mode)
else:
self.file = open(filename, mode, encoding=encoding)
self.filename = filename
self.mode = mode
self.encoding = encoding

def write(self, line):
    if 'b' in self.mode:
        if not isinstance(line, bytes):
            raise TypeError('must be bytes')
    self.file.write(line)

def __getattr__(self, item):
    return getattr(self.file, item)

def __str__(self):
    if 'b' in self.mode:
        res = "<_io.BufferedReader name='%s'>" % self.filename
    else:
        res = "<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" % (self.filename, self.mode, self.encoding)
    return res

f1 = FileHandle('b.txt', 'wb')

f1.write('你好啊啊啊啊啊')

#自定制的write,不用在进行encode转成二进制去写了,简单,大气

f1.write('你好啊'.encode('utf-8'))
print(f1)
f1.close()
【八】setitem,getitem,delitem
getitem()将对象当作list使用
如obj = ACLASS(), obj_first=obj[0]
class Foo:
def init(self, name):
self.name = name

def __getitem__(self, item):
    print(self.__dict__[item])

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

def __delitem__(self, key):
    print('del obj[key]时,我执行')
    self.__dict__.pop(key)

def __delattr__(self, item):
    print('del obj.key时,我执行')
    self.__dict__.pop(item)

f1 = Foo('sb')
f1['age'] = 18
f1['age1'] = 19
del f1.age1
del f1['age']
f1['name'] = 'alex'
print(f1.dict)
【九】call
对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的

即:对象 = 类名() ;
而对于 call 方法的执行是由对象后加括号触发的

即:对象() 或者 类()()
class Foo:

def __init__(self):
    pass

def __call__(self, *args, **kwargs):
    print('__call__')

obj = Foo() # 执行 init
obj() # 执行 call
【十】gt(),lt(),eq(),ne(),ge()
定义对象比较方法,为关系运算符>,>=,<,==等提供接口
class SavingsAccount(object):
def init(self, name, pin, balance=0.0):
self._name = name
self._pin = pin
self._balance = balance

def __lt__(self, other):
    return self._balance < other._balance

s1 = SavingsAccount("dream", "1000", 0)
s2 = SavingsAccount("hope", "1001", 30)
print(s1 < s2)
其实,类似的数值运算的魔法方法非常非常之多,好吧,我真的用了两个,现在是三个非常来形容,以下列出部分
class Point(object):
def init(self):
self.x = -1
self.y = 5

# 正负,取反、绝对值
def __pos__(self):
    pass

def __neg__(self):
    pass

def __invert__(self):
    pass

def __abs__(self):
    pass

# 加减乘除、取余,指数
def __add__(self, other):
    pass

def __sub__(self, other):
    pass

def __divmod__(self, other):
    pass

def __mul__(self, other):
    pass

def __mod__(self, other):
    pass

def __pow__(self, power, modulo=None):
    pass

# 逻辑运算符
def __and__(self, other):
    pass

def __or__(self, other):
    pass

def __xor__(self, other):
    pass

# 位移
def __lshift__(self, other):
    pass

def __rshift__(self, other):
    pass

# 赋值语句
def __iadd__(self, other):
    pass

def __imul__(self, other):
    pass

def __isub__(self, other):
    pass

def __idiv__(self, other):
    pass

def __imod__(self, other):
    pass

def __ipow__(self, other):
    pass

def __ilshift__(self, other):
    pass

def __irshift__(self, other):
    pass

【十一】iter()
为for … in提供接口,返回一个迭代对象,并调用对象next()方法,直到StopIteration
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 > 10:  # 退出循环的条件
        raise StopIteration()
    return self.a  # 返回下一个值

for i in Fib():
print(i)
【十二】描述符(get(),set(),delete())
【1】什么是描述符
描述符本质就是一个新式类,在这个新式类中
至少实现了__get__(),set(),delete()中的一个
这也被称为描述符协议
get():调用一个属性时,触发
set():为一个属性赋值时,触发
delete():采用del删除属性时,触发
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
def get(self, instance, owner):
pass
def set(self, instance, value):
pass
def delete(self, instance):
pass
【2】描述符的作用
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
【3】描述符分两种
数据描述符:至少实现了__get__()和__set__()
class Foo:
def set(self, instance, value):
print('set')

def __get__(self, instance, owner):
    print('get')

UTF-8
【引入】
【一】init(),del()__,new()
【二】str,repr
【1】str
【2】repr
【3】小结
【三】__del__方法
【四】isinstance(obj,cls)和issubclass(sub,super)
【五】doc
【六】enter__和__exit
【七】setattr,delattr,getattr
【1】方法介绍
【2】二次加工标准类型(包装)
(1)包装
(2)授权
【八】setitem,getitem,delitem
【九】call
【十】gt(),lt(),eq(),ne(),ge()
【十一】iter()
【十二】描述符(get(),set(),delete())
【1】什么是描述符
【2】描述符的作用
【3】描述符分两种

标签:__,name,self,魔法,pass,print,方法,def
From: https://www.cnblogs.com/zenopan101861/p/18177017

相关文章

  • Windows 10 LTSC启用Microsoft Store的方法
    新建msreg.bat文件,并编辑内容如下:==========@echooff::BatchGotAdmin:-------------------------------------REM-->Checkforpermissions>nul2>&1"%SYSTEMROOT%\system32\cacls.exe""%SYSTEMROOT%\system32\config\system&q......
  • Firewalld 白名单配置方法
    白名单配置方法以仅信任来自10.60.22.0/24,10.60.23.0/24ip端的连接为例1.配置信任来源#添加IP地址范围到"trusted"的区域firewall-cmd--permanent--zone=trusted--add-source=10.60.22.0/24firewall-cmd--permanent--zone=trusted--add-source=10.60.23.0/2......
  • 两个事务方法导致异常抛出Transaction rolled back because it has been marked as ro
    异常现场(背景)在springboot事物操作开发中,如果A方法调用B方法,A和B方法都在不同的类中,且A和B都加了@Transactional注解,A调用B方法时,将B方法trycatch了。代码:@ServicepublicclassBService{@Transactional(rollbackFor=Exception.class)publicvoidb......
  • Markdown和Latex中文字上下标的方法
    技术背景在Markdown和Latex中,如果只是写公式,不论是行内公式还是行间公式,都可以直接使用^和_这两个符号实现上下标。但有个问题是,如果只是使用公式来做上下标,出来的字体是斜着的。例如这样的语法:$$P_{OK}$$输出结果是这样的:\[P_{OK}\]但是有些时候想要的字符不能使用斜体,这......
  • vscode/texstudio中实现latex字数统计的方法
    A:vscode 1.在这里免登录下载perl语言:[Download&InstallPerl-ActiveState](https://www.activestate.com/products/perl/),默认安装步骤并将其文件夹路径加入系统Path(使用ActiveState安装的perl其路径在一串杂码下,在安装完成后会显示这个路径,路径名称参见[安装ActiveState......
  • NumPy 数组创建方法与索引访问详解
    NumPy创建数组NumPy中的核心数据结构是ndarray,它代表多维数组。NumPy提供了多种方法来创建ndarray对象,包括:使用array()函数array()函数是最常用的方法之一,它可以将Python列表、元组甚至其他数组转换为ndarray对象。语法:ndarray=np.array(data,dtype=dtype,o......
  • uniapp renderjs "只能行内调用, 不能通过方法调用" 学习
    故障代码app报错(h5正常),说echarts为undefined。<viewclass="content"><button@click="get"@touchend="update">update</button><imgsrc=""alt="我是alt"id="img"/></vi......
  • 「实用」这几个写注释的方法,你一定要看一看
    前言大家好,我是Charzie,欢迎来到我的博客,今天这篇文章,我门来谈一谈如何写C++的注释以及应该避免那些写注释的方法C++注释详解在C++编程中,注释是一个非常重要的工具,它可以帮助我们理解代码的功能、逻辑和目的。通过添加注释,我们可以让其他开发者更容易地阅读和理解我们的代码,也可......
  • 用蒙特卡罗方法求积分
    实验任务采用Monte-Carlo法计算函数 y=x2 在0~10之间的积分值实验目的熟悉MPI_Reduce() 函数的用法实验方法该算法的思想是通过随机数把函数划分成小的矩形块,通过求矩形块的面积和来求积分值,我们生成n个0~10之间的随机数,求出该随机数所对应的函数值作为矩形的高,由......
  • 重写DRF的to_representation和to_internal_value方法有什么用途?
    DRF所有序列化器类都继承了BaseSerializer类,通过重写该类的to_representation()和to_internal_value()方法可以改变序列化和反序列化的行为,比如给序列化后的数据添加额外的数据,或者对客户端API请求携带的数据进行反序列化处理以及用来自定义序列化器字段。to_representation(......