Python常见的魔术方法和魔术属性

文章目录

魔术方法

Python的魔术方法(也称为特殊方法)是对象的一些特殊方法,它们都是以双下划线开头并以双下划线结尾,例如__init__。

魔术方法所体现的设计思想是构成python风格的重要组成部分

它们的目的是让对象能够响应内置的运算符或函数,比如len(),+运算符等。例如,当我们使用len(obj)函数调用对象obj的长度时,实际上会调用对象的__len__方法。

1、 __new__ 魔术方法

触发时机:实例化类生成对象的时候触发(触发时机在__init__之前)
功能:控制对象的创建过程
参数:至少一个cls接受当前的类,其他根据情况决定。cls是系统自动传递的。类名字随意
返回值:通常返回对象 或 None

 
 

创建对象时可以选择给或者不给

(1) 基本使用
class MyClass2():
    a = 100
obj2 = MyClass2()
#print(obj2)

一般情况下,通过类创建对象,默认返回类对象。是借助其默认继承的父类object创建的。如果我们想自己控制对象的创建,要通过__new__

在这里插入图片描述

自己控制创建对象,使用__new__魔术方法。但要写返回值,不然创建的对象为None
在这里插入图片描述

class MyClass1():
    def __new__(cls):
        # print(cls)        
        # 1.返回本类对象
        """类.成员方法(类)"""
        # return object.__new__(cls)
        # 2.返回其他类的对象
        # return obj2
        # 3.不返回对象,None
        return None        

obj = MyClass1()
#print(obj.a)
print(obj)

__new__传参,传类本身

在这里插入图片描述

#通过父类调用方法返回,这是默认情况下,借助父类创建的对象
在这里插入图片描述

返回其它类对象,可以直接调用其它类成员
在这里插入图片描述

#不返回对象,None
在这里插入图片描述

可以严格控制创建对象的过程

(2) __new__ 触发时机要快于 __init__

__new__ 创建对象
__init__ 初始化对象
先创建,再初始化

class MyClass():

def new(cls):
print(1)
return object.new(cls)

def init(self):
print(2)

obj = MyClass()

#new__触发时机快于__init,如下,虽然__init__定义早于__new__。但创建对象的时候,执行顺序还是先执行__new__
定义谁在前谁在后,没影响,关键在调用处,谁先谁后。系统默认调用的。由此可见__new__优先调用,触发时机快于__init__

在这里插入图片描述

(3) __new__的参数要和__init__参数一一对应。参数个数一致就行
class Boat():
    def __new__(cls,name):
        return object.__new__(cls)
<span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span>name<span class="token punctuation">)</span><span class="token punctuation">:</span>
    self<span class="token punctuation">.</span>name  <span class="token operator">=</span> name

obj = Boat(“万里阳光号”)
print(obj.name)

运行
运行

在这里插入图片描述

参数个数不一致会报错
在这里插入图片描述

其实传给__new__的参数也没用上,要是船的参数比较多,这样一一对应比较麻烦。我们可以使用收集参数进行改造
这样,不管初始化多少参数,__new__都可以接收

#使用收集参数进行改造

class Boat():
    # *args,**kwargs 可以收集多余的所有参数
    def __new__(cls,*args,**kwargs):
        return object.__new__(cls)
<span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span>name<span class="token punctuation">,</span><span class="token builtin">type</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    self<span class="token punctuation">.</span>name  <span class="token operator">=</span> name
    self<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">=</span> <span class="token builtin">type</span>

obj = Boat(“万里阳光号”,“破木头做的”)
print(obj.name , obj.type)

运行
运行

在这里插入图片描述

(4) __new____init__之间的注意点
如果__new__ 没有返回对象,或者返回的是其他类的对象,不会调用构造方法.
只有在返回自己本类对象的时候,才会调用构造方法.

class Children():
def new(cls,*args,**kwargs):
return obj2
# pass

def init(self,name,skin):
print("构造方法被触发 … ")
# self.name = name
# self.skin = skin

obj = Children(“灭霸”,“紫薯”)

# print(obj.name) error
# print(obj.skin) error

如果__new__什么也不返回,那返回的是None。此时该类的构造方法也不会被调用。构造不会被触发。对象都没被创建,无法初始化

在这里插入图片描述

如果__new__方法返回其他对象,则构造函数不会被调用。其实此时对象找的是被返回对象类里面的该成员
在这里插入图片描述

如果被返回的对象,类里面有name成员,那么将被正常打印。虽然是通过此类创建的对象,但由于__new__返回的是其他类的对象,实际上创建的是其他类的对象
在这里插入图片描述

2、 单态模式 : 同一个类,无论实例化多少次,都有且只有一个对象

每创建一个对象,就会在内存中多占用一份空间
为了节省空间,提升执行效率,使用单态模式
场景:只是单纯调用类中的成员,而不会额外为当前对象添加成员;
一般__new__方法大多数出现在单态模式

单态适用于对数据库操作,用一个对象解决不同的问题。节省空间

class Singleton():
    __obj = None
    def __new__(cls):
        if cls.__obj is None:
            cls.__obj = object.__new__(cls)
        return cls.__obj

 
 
运行
运行

给类设置私有属性,公有属性都可以。但一般设置为私有,因为设置公有的话,可以通过类来获取到,但获取到的为None。所以设置为私有,只能通过实例化之后调用
通过__new__方法创建对象之前,先判断一下,如果对象存在就将原对象返回,不存在再创建
调用属性,要么通过 类.属性 要么通过 对象.属性 不能直接调属性,否则报错 如下 cls.obj是通过类调 cls是__new__方法传参的传的类
在这里插入图片描述

如下所示,使用单态模式,两次创建的对象是同一个内存地址。而正常的类创建两个对象,内存地址不同,开辟了两个内存空间
由此可见,单态模式节省内存空间
在这里插入图片描述

“”"
第一次,在实例化对象时触发__new__魔术方法
if cls.__obj is None 条件成立 cls.__obj = object.__new__(cls) 创建一个对象给私有成员属性__obj
return cls.__obj 用obj1接收到了对象

第二次,在实例化对象时触发__new__魔术方法 if cls.__obj is None不满足,因为已经在__obj属性中存放了一个对象
return cls.__obj

第三次,在实例化对象时触发__new__魔术方法 if cls.__obj is None不满足,因为已经在__obj属性中存放了一个对象
return cls.__obj
“”"

obj1 = Singleton()
obj2 = Singleton()
obj3 = Singleton()
print(obj1,obj2,obj3)

 
 

在这里插入图片描述

这样无论实例化多少次对象,有且只有第一次创建的一个对象

class Singleton():
    __obj = None
    def __new__(cls,*args,**kwargs):
        if cls.__obj is None:
            cls.__obj = object.__new__(cls)
        return cls.__obj
<span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span>name<span class="token punctuation">)</span><span class="token punctuation">:</span>
    self<span class="token punctuation">.</span>name <span class="token operator">=</span> name

obj1 = Singleton(“康玉康”)
obj2 = Singleton(“张保张”)
print(obj1,obj2)
print(obj1.name)
print(obj2.name)

运行
运行

“”"
康玉康 康玉康
康玉康 张保张
张保张 张保张

第一次实例化对象时,
触发__new__ if cls.__obj is None: 创建一个新的对象进行返回
然后触发__init__ self.name = 康玉康

第二次实例化对象时
触发__new__ if cls.__obj is None: 条件不满足,返回的是第一次实例化的对象,是同一个
然后触发__init__ self.name = 张保张
“”"

name = "康裕康"
name = "张保障"
print(name)

 
 
运行
运行

名义上创建不同对象,实际上返回的是第一个对象,但是每次创建时都可以调用构造方法,根据传参不同,得到不同的执行结果
在这里插入图片描述
在这里插入图片描述

但要是多次实例化对象后再打印,对象是第一次实例化的,虽然后面返回的是第一次实例化的对象,但构造函数每次都执行,再打印成员属性时,是最后一次执行构造函数的结果。
在这里插入图片描述

3、 __del__ 魔术方法(析构方法)

触发时机:当对象被内存回收的时候自动触发[1.页面执行完毕回收所有变量 2.所有对象被del的时候]
    功能:对象使用完毕后资源回收
参数:一个self接收对象
返回值:无

 
 
(1) 基本语法
class Lion():
    def __init__(self,name):
        self.name = name
<span class="token keyword">def</span> <span class="token function">__del__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"析构方法被触发 ... "</span><span class="token punctuation">)</span>

# 触发方式一: 页面执行完毕回收所有变量
obj1 = Lion(“辛巴”)

运行
运行

在这里插入图片描述

#触发方式二: 所有对象被del的时候
obj2 = obj1
obj3 = obj1
print(obj2 , obj1 ,obj3)
print("<====start===>")
del obj1
del obj2
del obj3
print("<====end===>")

 
 

在这里插入图片描述

所有对象被删除时触发,而不是页面走完才触发
在这里插入图片描述

如果不是所有对象被删除,则到页面执行结束时触发
在这里插入图片描述

本质上都是对象全部释放的时候触发

析构方法综合运用

(2) 模拟文件操作
import os
class ReadFile():
    # 根据文件是否存在,创建对象
    def __new__(cls,filename):
        if os.path.exists(filename):
            return object.__new__(cls)
        else:
            print("抱歉,没有这个文件")

# 打开文件
def init(self,filename):
self.fp = open(filename,mode=“r”,encoding=“utf-8”)

# 关闭文件
def del(self):
self.fp.close()

# 读取文件
def readcontent(self):
return self.fp.read()

obj = ReadFile(“0.py”)
print(obj.readcontent())

在这里插入图片描述

4、 __str__ 魔术方法

触发时机: 使用print(对象)或者str(对象)的时候触发
功能:     查看对象
参数:     一个self接受当前对象
返回值:   必须返回字符串类型

 
 

如果定义了__str__方法,但没有返回字符串,或者无返回值,则会报错。必须返回字符串
在这里插入图片描述

class Cat():
    gift = "抓老鼠"
    def __init__(self,name):
        self.name = name

def cat_gift(self):
return “小猫叫{},小猫会{}”.format(self.name,self.gift)

def str(self):
return self.cat_gift()

__repr__ <span class="token operator">=</span> __str__

tom = Cat(“汤姆”)
# 触发时机1 : print(对象)
# print(tom)

定义了__str__方法。当打印对象时,会将__str__方法返回的内容打印出来

在这里插入图片描述

#触发时机2 : str(对象)

res = str(tom)
print(res)

 
 

在这里插入图片描述

print("<==================>")
res = repr(tom)
print(res , type(res))
print("<==================>")

 
 

5、__repr__ 魔术方法

触发时机: 使用repr(对象)的时候触发
功能:     查看对象,与魔术方法__str__相似
参数:     一个self接受当前对象
返回值:   必须返回字符串类型

 
 
class Mouse():
    gift = "偷油吃"
    def __init__(self,name):
        self.name = name

def mouse_gift(self):
return “老鼠叫{},老鼠会{}”.format(self.name,self.gift)

def repr(self):
return self.mouse_gift()

# 系统底层默认把__repr__方法赋值给__str__方法,所以通过print或者str强转可以触发;
# str = repr

jerry = Mouse(“杰瑞”)
# res = repr(jerry)
# print(res)

# 可以触发
# print(jerry)
res = str(jerry)
print(res)

运行
运行

在这里插入图片描述

如果写了__str__ 。想实现__repr__方法,必须将__str__赋值给__repr__。因为这个方向,系统默认没写
在这里插入图片描述

手动将__str__赋值给__repr__ 就可以了
在这里插入图片描述

6、__call__ 魔术方法

触发时机:把对象当作函数调用的时候自动触发
功能: 模拟函数化操作
参数: 参数不固定,至少一个self参数
返回值: 看需求

 
 
(1) 基本语法
class MyClass():
    def __call__(self):
        print("__call__魔术方法被触发 ... ")

obj = MyClass()
obj()

运行
运行

定义了__call__方法,对象就可以向函数一样调用,直接执行__call__方法里面的代码
在这里插入图片描述

如果没定义__call__方法,调用对象会报错

在这里插入图片描述

(2) 利用__call__魔术方法做统一调用,把其他方法都放到__call__ 方法里面一起调用

如果多个方法需要同时调用,每个都要调一次很麻烦,这时就需要__call__魔术方法

class Wash():
    def __call__(self,something):
        print("我要洗{}".format(something))
        self.step1(something)
        self.step2()
        self.step3()
        return "洗完了"

def step1(self,something):
print(“放水,把{}扔进去”.format(something))

def step2(self):
print("倒洗衣粉,洗衣液,蓝月亮,金纺,立白 … ")

def step3(self):
print(“洗一洗,晾干,穿上”)

obj = Wash()
# obj.step1()
# obj.step2()
# obj.step3()
res = obj(“袜子”)
print(res)

运行
运行

传参,对象作为函数被调用参数的个数要和__call__方法除了self的参数个数相同,如果不同,会报错
在这里插入图片描述

此时的something。是方法的形参,不是类的成员属性。不是类的成员属性不能通过对象调用,除非先创建类属性,或者通过构造方法创建类属性
在这里插入图片描述

直接属性名调用即可
在这里插入图片描述

打印返回值

在这里插入图片描述

(3) 模拟整型强转操作
import math
class MyInt():
    def __call__(self,num):
        if isinstance(num,bool):
            if num == False:
                return 0 
            else:
                return 1

elif isinstance(num,int):
return num

elif isinstance(num,float):
# 方法一
# a,b = str(num).split(“.”)
# return eval(a)
# 方法二
“”"
if num >= 0:
return math.floor(num)
else :
return math.ceil(num)
“”"

# 简写
return math.floor(num) if num >= 0 else math.ceil(num)

elif isinstance(num,str):
if (num[0] “+” or num[0] “-”) and num[1:].isdecimal():
# 获取当前字符串的正负值
if num[0] “+”:
sign = 1
elif num[0] “-”:
sign = -1
# 截取符号后面的字符串传递
return self.calc(num[1:],sign)

elif num.isdecimal():
return self.calc(num)
else:
return “这个算不了兄弟~”

# 计算最后的数值
def calc(self,num,sign=1):
# 去掉前面的"0"字符串
num = num.lstrip(“0”)
# print(num , type(num) , “<============>")
if num ”":
return 0

return eval(num) * sign

myint = MyInt()
res = myint(-5.67)
print(res , type(res))
res = myint(“-000000000000055555”)
print(res , type(res))
res = myint(“asdfasdfasdfasdf”)
print(res , type(res))
# print(myint(“+0000000000000”))
# bool int float 纯数字字符串

# int(3.78) => 3
# print(int(-3.78))
# import math
# print(math.floor(0) ) # => 3
# print(math.ceil(0))

“”"
print( int(“00000000000001223”) ) # 1223
print( int(“-00000000000001223”) ) # -1223
print( int(“+00000000000001223”) ) # 1223
print( int(“+0000000000000”) ) # 1223
“”“

# print( int(“asdfasdfasdfasdf”) ) # 1223
# print( eval(“00000000000001223”) )
# print( eval(”+00000000000001223") )
# print( eval(“-00000000000001223”) )
# print( eval(“-00000000000001223abc”) )

支持字符串是小数的强制转换
#通过__call__模拟整形强转操作
# int 强制把数据变成整型,只支持布尔型,整形,浮点型,纯数字类型的字符串,二进制(忽略) 转换成整形
import math
import re
class MyInt(object):
def call(self, num):
#布尔型转换
if isinstance(num,bool):
if num:
return 1
else:
return 0
#整形,直接返回
elif isinstance(num,int):
return num
#浮点型强转
elif isinstance(num,float):
return math.floor(num) if num >= 0 else math.ceil(num)
#纯数字字符串
elif isinstance(num,str):
if num != “” and (num[0] “+” or num[0] “-”) and re.search(r"^\d+.\d+KaTeX parse error: Undefined control sequence: \d at position 3: |^\̲d̲+,num[1:]):
#获取字符串正负号
if num[0] “+”:
sign = 1
elif num[0] ”-“:
sign = -1
#截取符号后面的字符串传递
return self.cal(num[1:],sign)
elif num.isdecimal():
return eval(num)
elif re.search(r”^\d+KaTeX parse error: Undefined control sequence: \d at position 4: (\.\̲d̲+)?",num):
return self.cal(num)
else:
return “这个计算不了,兄弟”
else:
return “这个计算不了,兄弟”

def cal(self,num,sign=1):
#去掉字符串前面的0
num = num.lstrip(“0”)
if num == ‘’:
return 0
else:
if isinstance(eval(num),float):
# return eval(num) * sign
return math.floor(eval(num)) sign if eval(num) >= 0 else math.ceil(eval(num)) sign
elif isinstance(eval(num),int):
return eval(num) * sign

myint = MyInt()
print(myint(2+3j))

7、__bool__ 魔术方法

触发时机:使用bool(对象)的时候自动触发
功能:强转对象
参数:一个self接受当前对象
返回值:必须是布尔类型

 
 

类似的还有如下等等(了解):

__complex__(self)complex强转对象时调用
__int__(self)int强转对象时调用
__float__(self)float强转对象时调用

 
 
...
...

 
 
class MyClass():
    def __bool__(self):
        return True
obj = MyClass()
print(bool(obj))

 
 
运行
运行

类对象默认,不做任何操作。使用bool强转,默认返回True
在这里插入图片描述

我们可以控制返回结果,只能是True或False,否则报错
在这里插入图片描述

类似的__int__。必须返回int类型数据等
在这里插入图片描述

8、__add__ 魔术方法 (与之相关的__radd__ 反向加法)

触发时机:使用对象进行运算相加的时候自动触发,对象在+的左边触发add
功能:对象运算
参数:二个对象参数
返回值:运算后的值

 
 

类似的还有如下等等(了解):

__sub__(self, other)           定义减法的行为:-
__mul__(self, other)           定义乘法的行为:
__truediv__(self, other)       定义真除法的行为:/

 
 
...
...

 
 
class MyClass():
    def __init__(self,num):
        self.num = num

# 当对象在 + 号的左侧时,自动触发,触发的时候,把+左边的值当成参数传递给self,+右边的值当成参数传递给other
def add(self,other):
# print(self)
# print(other)
return self.num * 3 + other

# 当对象在 + 号的右侧时,自动触发,触发的时候,把+右边的值当成参数传递给self,+左边的值当成参数传递给other
def radd(self,other):
# print(self) # 对象
# print(other) # 7
return self.num * 5 + other

# add的触发方式
a = MyClass(3)
res = a + 1
print(res)

运行
运行

对象在+左边,__add__方法触发,返回的是 self3 + other = 33 + 1 = 10

在这里插入图片描述

#radd的触发方式

b = MyClass(5)
res = 7 + b
print(res)

 
 

对象在+的右边,__radd__被触发,+右边的值传给self,+左边的值传给other。返回的是self 5 +1 = 55+1 = 26

在这里插入图片描述

#对象 + 对象
res = a + b
print(res)

a+b 触发的是add魔术方法 self 接受的是a other 接受的是b
return a.num + b => return 9 + b
res = 9 + b 触发的是radd魔术方法 self 接受的是b other 接受的是9
return b.num * 5 + 9 => 5 * 5 + 9 => 34

在这里插入图片描述

9、__len__ 魔术方法

触发时机:使用len(对象)的时候自动触发 
功能:用于检测对象中或者类中某个内容的个数
参数:一个self接受当前对象
返回值:必须返回整型

 
 

#len(对象) => 类中的所有自定义成员

class MyClass():
    pty1 = 1
    pty2 = 2 
    __pty3 = 3

def func1():
pass
def func2():
pass
def __func3():
pass

def len(self):
# 以__开头并且以__结尾的成员过滤掉;
return len( [ i for i in MyClass.dict if not ( i.startswith() and i.endswith(””) ) ] )

obj = MyClass()
print(len(obj))

“”"
代码原型;
print(MyClass.dict)
lst = []
for i in MyClass.dict:
print(i , type(i))
if not ( i.startswith(““) and i.endswith(””) ):
lst.append(i)
print(len(lst))
“”"

“”"

{
module’: ‘main’,
‘pty1’: 1,
‘pty2’: 2,
‘_MyClass__pty3’: 3,
‘func1’: <function MyClass.func1 at 0x7f6898431378>,
‘func2’: <function MyClass.func2 at 0x7f6898431400>,
‘_MyClass__func3’: <function MyClass.__func3 at 0x7f6898431488>,
len’: <function MyClass.len at 0x7f6898431510>,
dict’: <attribute ‘dict’ of ‘MyClass’ objects>,
weakref’: <attribute ‘weakref’ of ‘MyClass’ objects>,
doc’: None
}
“”"

运行
运行

在这里插入图片描述

魔术属性

与类相关的魔术属性
#__dict__ 获取对象或类的内部成员结构
#__doc__  获取对象或类的内部文档
#__name__ 获取类名函数名
#__class__ 获取当前对象所属的类
#__bases__ 获取一个类直接继承的所有父类,返回元组

 
 

1、 __dict__ 获取对象或类的内部成员结构

class Man():
    pass

class Woman():
pass

class Sasuke(Man,Woman):
“”"
描述: 佐助这个的天生属性,技能
成员属性: __eye skin
成员方法: skylight __moonread
“”"

__eye = “血轮眼->万花筒->轮回眼”

skin <span class="token operator">=</span> <span class="token string">"白色"</span>

def skylight(self , myfunc):
print(“使用天照,一团黑色的火焰 … 恐怖如斯”)
res = myfunc.name
print(res , type(res) )

def __moonread(self):
print(“使用月读,让敌人拉入到幻术空间,被施法者掌握”)

obj = Sasuke()

dic = Sasuke.dict
dic = obj.dict
print(dic)

运行
运行

在这里插入图片描述

2、 __doc__ 获取对象或类的内部文档

print(Sasuke.__doc__)
print(obj.__doc__)

 
 

类和对象获取的一样,都是三个引号的描述
在这里插入图片描述

help查看的更全面

在这里插入图片描述

3、 __name__ 获取类名函数名

def func343434():
    print("佩恩出场时,使用一手地爆天星,技惊四座,点燃所有观众")

obj.skylight(func343434)

获取传递的函数的函数名
在这里插入图片描述

在这里插入图片描述

4、 __class__ 获取当前对象所属的类

print(obj.__class__)

 
 

在这里插入图片描述

5、 __bases__ 获取一个类直接继承的所有父类,返回元组

print(Sasuke.__bases__)

 
 

在这里插入图片描述

可以查看该类继承的父类,放到元祖中

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值