31.Python面向对象str和repr原理魔法方法__call__和__new__方法单例模式

Posted 孤寒者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了31.Python面向对象str和repr原理魔法方法__call__和__new__方法单例模式相关的知识,希望对你有一定的参考价值。

目录:

每篇前言:


Python面向对象(四)

1.1 str和repr原理

不知道大家学到现在有没有注意到一个问题:

  • 在交互模式下输出的交互信息与直接print的信息有些不同
    这背后的原理是什么嘞?

其实这就涉及到了str和repr原理(有跟着我的Django专栏学习的童鞋可能发现在django中创建模型时用过这玩意,但是大家只是知道要那样用,会有那样的效果,而没有深究,所以这里就来好好探讨探讨!)。

  1. return返回的必须是字符串。
  2. print打印实例对象的时候(没有重写__repr__方法,直接是pass)显示的是 地址;
  3. 如果重写了__repr__方法,那么打印的就是__repr__方法里面的东西;
  4. 如果同时重写了__repr__和__str__方法,打印实例对象的时候,只会显示__str__里面的内容。
    例一:
# -*- coding: utf-8 -*-
"""
__author__ = 孤寒者
"""
class Person:

    def __repr__(self):
        return '这是一个repr方法'

    def __str__(self):
        return '这是一个str方法'


p = Person()
print(p)

  • %s 使用的是__str__方法; %r 使用的是__repr__方法。

例二:

# -*- coding: utf-8 -*-
"""
__author__ = 孤寒者
"""
class Person:

    def __repr__(self):
        return '这是一个repr方法'

    def __str__(self):
        return '这是一个str方法'


p = Person()
print('  %s' % p)
# 输出为:这是一个str方法
print('  %r' % p)
# 输出为:这是一个repr方法

在python中,str和repr方法在处理对象的时候,分别调用的是对象的__str__和__repr__方法;
print打印对象,调用str函数,如果对象没有定义__str__方法,则调用__repr__方法处理;
在交互模式下,直接输出对象,显示 __repr__ 的返回值。

1.2 魔术方法__call__方法

正常情况下,实例是不能像函数一样被调用的,要想实例能够被调用,就需要定义 __call__ 方法

# -*- coding: utf-8 -*-
"""
__author__ = 孤寒者
"""
class Person:

    def a(self):
        print('this is a ')

    # 实例对象加上括号就会自动调用call方法
    def __call__(self, *args, **kwargs):
        print('this is call')


p = Person()
p()  # 输出为: this is call

  • 拓展:
    类中的一些查询相关信息的方法(了解既可,而且我其实大都在前面讲过了,再讲一遍加深印象~)
  1. __class__ 查看类名

    格式: 实例.__class__

  2. __dict__ 查看全部属性,返回属性和属性值键值对形式

    格式:实例.__dict__

  3. __doc__ 查看对象文档,即类中(用三个引号引起来的部分)

    格式:类名.__dict__

  4. __bases__ 查看父类

    格式:类名.__base__

  5. __mro__ 查看多继承的情况下,子类调用父类方法时,搜索顺序

    格式:子类名.__mro__
    实例.__class__.__mro__

下来再来深入看一个魔法方法:

1.3 魔法方法__new__方法

类每次实例化的时候都会创建一个新的对象,如果要求类只能被实例化一次该怎么做呢?

  • __new__方法

举个例子:

# -*- coding: utf-8 -*-
"""
__author__ = 孤寒者
"""
class Person:

    #初始化
    def __init__(self):
        print('this is init')

    def __new__(cls, *args , **kwargs):
        print('这new方法在init之前就进行了调用')
        #重写new方法
        #new方法是最先被调用的
        #new 必须返回父类的new方法,程序才能继续往下运行,如果不返回,即没有下面这行程序就不会往下运行。
        return super().__new__(cls)

guhanzhe= Person()

四个点理解__new__方法:

  1. __new__方法是在类创建实例的时候自动调用的。
  2. 实例是通过类里面的__new__方法是在 类 创建出来的。
  3. 先调用__new__方法创建实例,再调用 __init__方法初始化实例。
  4. __new__方法,后面括号里的cls代表的是类本身

在上面的例子中,我们可以看到创建实例的时候,自动调用了__new__方法和__init__方法,并且
是先调用的__new__(__new__方法会在内存当中开辟一个空间)再调用的__init__方法,打印 cls 的时候显示的这个Person类

下一个知识点引入:
new方法会开辟空间
每次实例化,这个new方法都会开辟一个新的空间
那就有个问题——可不可以让这new方法只开辟一个空间?

  • 这就涉及到了伟大的单例模式!

1.4 单例模式

提前引入一些小知识点(前面也讲过了,怕大家忘了,再来温习一遍):
hasattr() #has attribute
hasattr() 函数用于判断对象是否包含对应的属性
hasattr(object(对象),name(“字符串,属性名”))
返回True(有);False(没有)

直接结合范式代码讲解:

# -*- coding: utf-8 -*-
"""
__author__ = 孤寒者
"""
class Person:
    #范式:固定的公式;固定的写法
    def __new__(cls,*args,**kwargs):
        #hasattr 判断类里面有没有这个方法
        #如果类没有instance这个属性
        if not hasattr(cls,'instance'):    #cls就是Person这个类本身
            #没有instance这个属性就会执行下面这句
            #创建instance这个属性,等于父类的new方法
            #new返回父类的new方法,和返回instance都可以
            cls.instance = super().__new__(cls)           #上面说如果类没有instance这个属性就执行这句,把父类的new方法添加为类本身的属性
                                                          #在下一句再return这个类本身的这个instance属性,这样在第二次实例化的时候,return
            											  #的仍然是第一次实例化的cls.instance,就没有返回父类的new方法,也就没有创建新的内存
            											  #空间,而是仍然指向第一次创建的内存空间
        return cls.instance        #如果把返回的改为 super().__new__(cls),直接返回父类的new方法,这就不是单例模式,因为每次实例化都会返回父类
                                   #的new方法,即创建一个新的内存空间。
    def __init__(self,name):
        self.name = name

a = Person('孤寒者')
b = Person('新的孤寒者')
print(id(a))
print(id(b))

会发现两次的id都一样了,即两次指向同一片内存空间,相当于a被b覆盖了。
意味着这两个其实引用的是同一个实例,是一个实例的不同名字

以上是关于31.Python面向对象str和repr原理魔法方法__call__和__new__方法单例模式的主要内容,如果未能解决你的问题,请参考以下文章

31.Python面向对象str和repr原理魔法方法__call__和__new__方法单例模式

对象名称重载__str__ 和__repr__

python_魔法方法:__str__()和__repr__()

python Class:面向对象高级编程 __str__ / __repr__

常见的魔法方法及使用

Python面向对象编程第11篇 特殊方法之__str__和__repr__