Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

时间:2022-12-09 23:55:59

1.魔术方法 __new__

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

注意:python3.x 新式类 python2.x 旧式类, 新式类不需要每次都写一次object,默认继承控制创建的对象。

class MyClass2(): abc = 1 obj2 = MyClass2() class MyClass(object): def __new__(cls): print(cls)  # <class '__main__.MyClass'>
        # 借助object父类方法里面__new__ 来为本类创建一个对象
        # return object.__new__(cls) # 返回一个本类对象
        # return None # 返回一个空对象
        return obj2 # 返回的是一个其他类的对象
    pass

# 实例化对象obj
obj = MyClass() print(obj) print(obj.abc)
执行结果如下:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 

1.1.对比__new__和 __init__的触发时机

__new__ 魔术方法是用来创建对象的
__init__魔术方法是用来初始化对象的
得现有对象,才能够初始化,没有对象初始化谁?
__new__ 的触发时机要快于__init__
__new__ __init__ 这两个方法的参数要一一匹配.

 


1.2.__new__ __init__这两个方法中的参数一一匹配

#(1)一个参数的类型
class Boat(): def __new__(cls, name): print(1) return object.__new__(cls) def __init__(self, name): self.name = name obj = Boat("大锤") #(2)无限个参数
class Boat(): def __new__(cls, *args, **kwargs): print(1) return object.__new__(cls) def __init__(self, *args, **kwargs): strvar = ""
        for i in args: strvar += i + " "
        print("小船的贡献者有:", strvar) print("小船的名字是{}".format(kwargs['name'])) obj = Boat("王俊文", "舒畅", "境泽年", "五金玲", name="Anglelababy")
代码运行结果:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 

 注意:如果通过__new__返回的是其他类的对象,不会触发到自己的__init__ ,因为__init__初始化的是本类的对象。

    例:

class Boat(): def __new__(cls, *args, **kwargs): return obj2 #返回的是MyClass()类中产生的对象 def __init__(self): print("init调用了") obj = Boat() print(obj.abc)
代码运行结果为:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

即是1.中我们定义的对象obj2 = MyClass2()中的属性,即是输出123,实际 def __init__()

这初始化函数没有运行,因为我们定义__new__返回的是MyClass2()类中产生的对象obj2,

obj2调用类中的公有属性最后输出结果为123

 2.单态模式

无论实例化多少次,都有且只有一个对象.最终目的:为了节省内存空间.应用的场景是只调用相关的成员属性或方法,而不用动态
添加成员属性方法的环境中。

 

2.1 基本用法

class Singleton(): __obj = None def __new__(cls): if cls.__obj is None: # 借助父类创建对象
            obj = object.__new__(cls) # 把这个对象赋值给类中成员属性__obj
            cls.__obj = obj return cls.__obj

# 第一次创建时,因为cls.__obj 是None 所以创建一个对象
obj = Singleton() print(obj) # 第二次实例化时,因为cls.__obj 不是None ,直接返回上一次创建的那个对象
obj = Singleton() print(obj) # 第三次实例化时,同第二次
obj = Singleton() print(obj) obj = Singleton() print(obj) obj = Singleton() print(obj).
代码运行结果:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

总结:可以借助创建一个私有属性,默认为None,那么第一次实例化会利用__new__创建一个Singleton类的

对象,通过cls.__obj = obj赋值把已创建的对象赋值给cls.__obj。假使是第二次实例化的时,我们发现

Singleton有这属性(或者这个属性不是空),那么我们就不用用__new__创建对象这样就能达到单态,即是

只有一个内存空间的办法,从而节省内存空间。

对上面的代码就行稍作改造:

lass Singleton():
    __obj = None
    def __new__(cls,*args,**kwargs):
        if cls.__obj is None:
            cls.__obj = object.__new__(cls)
        return cls.__obj 
        
    def __init__(self,name):
        self.name = name
        
obj1 = Singleton("王波")

obj2 = Singleton("方真强")
print(obj1.name)
print(obj2.name)
运行结果:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

总结:创建的obj1、obj2对象同时指向了Singleton类,所以最后访问的是obj2的name属性。
详解:

     第一次是创建对象,并且通过init初始化对象,为该对象赋值成员属性name
     self.name = 王波

     第二次是直接返回上一个对象,然后对他进行初始化,为该对象赋值成员属性name
     self.name = 方真强

     两个不同的变量名指向的是同一个对象
     而此时该对象的成员属性name 这个值是方真强
     print(obj1.name)
     print(obj2.name)
     打印的都是方真强

 3.__del__析构方法 __init__构造方法

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

 3.1页面执行完毕回收所有变量:

class LangDog(): def __init__(self,name): self.name = name def eat(self,something): print("可爱的小狼{},喜欢吃{}".format(self.name,something)) def __del__(self): print("__del__方法被触发") # (1) 1.页面执行完毕回收所有变量
obj = LangDog("詹姆斯·狗蛋") obj.eat("") print("<===>")

代码执行结果:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 3.2 触发__del__机制中的第二点所有对象被del删除时候。

      所有对象被del的时候只有当所有指向该对象的变量都删除的时候,才算真正的删除该对象。

class LangDog(): def __init__(self,name): self.name = name def eat(self,something): print("可爱的小狼{},喜欢吃{}".format(self.name,something)) def __del__(self): print("__del__方法被触发") # (1) 1.页面执行完毕回收所有变量
obj = LangDog("詹姆斯·狗蛋") obj.eat("") print("<===>") # (2) 2.所有对象被del的时候
''' 只有当所有指向该对象的变量都删除的时候,才算真正的删除该对象 ''' obj2 = obj print("<==start===>") del obj del obj2 print("<==end===>")
代码执行结果:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 3.3 用类来模仿文件的写操作

一个普通文件的写入和读写操作如下:

# fp = open("ceshi111.txt",mode="w",encoding="utf-8") # fp.write("sfdsdf") # fp.close()

# fp = open("ceshi111.txt",mode="r",encoding="utf-8") # res = fp.read() # fp.close() # print(res)

 

 总结:打开 -写入文件内容/读出文件内容-关闭文件

用面对对象的写法如下:

import os class MyFile(): # 判断是否创建MyFile该对象
    def __new__(cls,filename): if os.path.exists(filename): return object.__new__(cls) return print("该文件不存在") # 产生文件对象
    def __init__(self,filename): self.fp = open(filename,mode="r",encoding="utf-8") # 读取文件
    def readfile(self): res = self.fp.read() return res def __del__(self): print(0) # 自动触发__del__ ,帮助我们关闭文件
 self.fp.close() obj = MyFile("ceshi222.txt") res = obj.readfile() print(res) 代码输出: 即是文件的内容结果,需要传入MyFile类中一个参数即是文件("ceshi222.txt")

 

4.魔术方法

4.1__call__的魔术方法

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

 

基本语法:

class MyClass(): def __call__(self): print("__call__方法被调用") return "done" obj = MyClass() res = obj()#也就是函数在调用的时候触发__call__ print(res)
代码执行结果如下:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 4.2利用__call__这个特性写一个洗衣服的流程

如下:

 class Wash():
  
# 使用call方法,进行统一的调用 def __call__(self, something): self.something = something print("以下是洗{}的过程:".format(something)) self.step1() self.step2() self.step3() def step1(self): print("第一步放水,把{}扔盆里".format(self.something)) def step2(self): print("第二部导入洗衣液,金纺,蓝月亮,吊牌洗衣皂扔盆里搓") def step3(self): print("第三部晒一下,穿上")
obj
= Wash() #用户使用Wash类的时候,只需要调用Wash就可以了,不用利用创建的obj对象依次调用洗衣的步骤,简单方便
obj("裤衩")

代码运行结果:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 # 如果用户不采用内部写一个__call__方法的话,会一直用对象去调用,导致非常的不方便。

 # 一边写一边骂街
 # obj.step1()
 # obj.step2()
 # obj.step3()

 4.2.1 利用__call__的触发机制(把对象当函数调用的时候会自动触发),写一个模拟内置int方法实现myint

 代码如下:

import math


class MyInt():

    def stoi(self, n, sign=1):
        res = n.lstrip("0")
        if res == "":
            return 0
        num = eval(res) * sign
        return num

    def __call__(self, n):
        # 判断的是布尔类型
        if isinstance(n, bool):
            if n == True:
                return 1
            else:
                return 0
        # 判断的是整型
        elif isinstance(n, int):
            return n
        # 判断的是浮点型
        elif isinstance(n, float):
            if n < 0:
                return math.ceil(n)
            else:
                return math.floor(n)

        # 判断的是字符串
        elif isinstance(n, str):
            if (n[0] == "-" or n[0] == "+") and n[1:].isdecimal():
                if n[0] == "+":
                    sign = 1
                elif n[0] == "-":
                    sign = -1
                return self.stoi(n[1:], sign)
            elif n.isdecimal():
                # 如果能够走到这个条件,一定没有带任何正负号
                return self.stoi(n)
            else:
                return "对不起,老弟,这个算不了"

        else:
            print("对不起,老哥,这个算不了")


myint = MyInt()
res = myint("12233")
print(res)
print(myint("+000000000333333"))
print(myint("00000000000"))
print(myint("-0000000000078"))
print(myint(-7.6))
print(myint(8.8))
print(myint(False))
print(myint(True))
print(myint(4))
print(myint([1, 3, 4]))

代码执行结果:

Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

可以说我们这个功能比内置的int还牛逼!

 4.3 __str__方法

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

 

 

class Cat(): gift = "抓老鼠"
    def __init__(self,name): self.name = name def cat_info(self): strvar = "这个对象的名字{},这个对象的天赋:{}".format(self.name,self.gift) return strvar def __str__(self): return self.cat_info() tom = Cat("汤姆") # (1) 打印对象触发__str__方法 # print(tom) # (2) str强转对象时候触发__str__方法
res = str(tom) print(res)

代码执行结果如下:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

4.4 __repr__ 方法

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

 

 例:

class Mouse(): gift = "打洞" def __init__(self,name): self.name = name def mouse_info(self): strvar = "该对象的名字{},它的天赋是{},龙胜龙,凤生凤,老鼠的儿子会打洞".format(self.name,self.gift) return strvar def __repr__(self): return self.mouse_info() # 在系统的底层加了如下一句话:如果存在__repr__ 这个方法,就把它赋值给__str__ # __str__ = __repr__ jerry = Mouse("杰瑞") # res = repr(jerry) # print(res) print(jerry)
代码运行结果:

Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 4.5 __bool__ 方法

触发时机:使用bool(对象)的时候自动触发 功能:强转对象 参数:一个self接受当前对象 返回值:必须是布尔类型 类似的还有如下等等(了解): __complex__(self) 被complex强转对象时调用 __int__(self) 被int强转对象时调用 __float__(self)        被float强转对象时调用

 代码:

class MyBool():
    def __bool__(self):
        print(122)
        # return True
        return False
运行结果如下:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 4.6__add__ 方法

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

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

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

 

 例:

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

    # 对象+数值,并且对象在+加号的左边,自动触发__add__方法
    def __add__(self, other):
        # self.num  => 3 + 56 => 59
        return self.num + other

    def __radd__(self, other):
        # self 接受b, other 接受33
        return self.num + other * 10


# 1.当对象在加号的左侧  自动触发add 方法
a = MyAdd(3)
res = a + 56
print(res)


# 2.当对象在加号的右侧  自动触发radd 方法
b = MyAdd(5)
res = 33 + b
print(res)

# 3 a+b =?
res = a + b
print(res)
'''
a在加号的左侧,触发add魔术方法
self.num + other => 3 + b

b在加号的右侧,触发radd魔术方法
res = 3+b
self.num + other * 10 => 5 + 3 *10 => 35
代码运行结果如下:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__

 

*4.7 __len__方法

触发时机:使用len(对象)的时候自动触发 
功能:用于检测对象中或者类中某个内容的个数
参数:一个self接受当前对象
返回值:必须返回整型
用len(对象)方式,算出该对象所归属的类有多少自定义成员

 

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

    def func1():
        pass

    def func2():
        pass

    def __func3():
        pass

    def __func4():
        pass

    def __func5():
        pass

    def __len__(self):
        # print(MyLen.__dict__) 打印结果如下分析
        dic = MyLen.__dict__
        lst = [i for i in dic if not(i.startswith("__") and i.endswith("__"))]
        num = len(lst)
        return num


obj = MyLen()
res = len(obj)
print(res)
"""分析
{'__module__': '__main__', 
'pty1': 1, 'pty2': 2,
 '_MyLen__pty3': 3, 
 'func1': <function MyLen.func1 at 0x7f10880d9620>, 'func2': <function MyLen.func2 at 0x7f10880d96a8>,
 '_MyLen__func3': <function MyLen.__func3 at 0x7f10880d9730>, '__len__': <function MyLen.__len__ at 0x7f10880d97b8>,
 '__dict__': <attribute '__dict__' of 'MyLen' objects>, 
 '__weakref__': <attribute '__weakref__' of 'MyLen' objects>, '__doc__': None}
"""
运行代码结果如下:
Day20 python__new__、单态模式、析构方法、常用魔术方法、__str__、__repr__、__bool__ 、__len__