python魔法方法和普通方法_python魔法方法,详解__new__()和__init__()

魔法方法__new__()和__init__()真是困扰了我好久。其实就是对__new__()一直不是很理解。今天做一个小小的总结吧,在这个知识点上真是花了太久的时间了。

首先回顾一下类与对象

python中一切皆为对象,python类本身也是一种对象,我们可以称其为类对象。对象=属性+方法,对象是类的实例,准确地来说,应该是:实例对象是类对象的实例

《python编程:从入门到实践》中是这么说的:面向对象编程是最有效的软件编写方法之一。在面向对象编程中,你编写表示现实世界的实物和情景的类,定义一大类对象都有的通用行为。基于类创建对象时,每个对象都自动兼备这种通用行为,然后可根据需要赋予每个对象独特的个性。根据类创建对象被称为实例化,这让你能够使用类的实例。end

类主要是定义对象的结构,然后我们以类为模板创建对象。类不但包含方法定义,还包含所有实例共享的数据。

接下来,进入正题。

大家应该对__init__()方法都很熟悉,它的第一个参数一定是self,__init__()方法负责对象的初始化,系统执行该方法前,其实该实例对象已经存在,要不然初始化什么呢,

先看一小段代码:

class Dog():
    def __new__(cls, *args, **kwargs):
        print("run the new of dog")
        #return super(Dog,cls).__new__(cls)
        return object.__new__(cls)  #两条return语句作用相同

    def __init__(self):
        print("run the init of dog")
        print(self)
        print(self.__class__)

a = Dog()
# run the new of dog
# run the init of dog
# <__main__.Dog object at 0x00000197AAA3A8D0>
# <class '__main__.Dog'>

可以看出,

  1. 当我实例化Dog类对象时,python中首先调用的是类对象的__new__()方法,如果该对象没有定义__init__()方法,则去父类中依次查找,直到object类(object类是所有类的基类哦)。

2. __new__()的返回语句中,object.__new__(cls)意思是调用父类(object)的__new__(),super()是一个特殊函数,帮助python将父类和子类关联起来,父类也成超类,名称super因此得名。

3. __new__()需要传递一个参数cls,__init__()需要传递一个参数self,self代表的就是实例对象本身,cls代表的是类对象本身。python中的self相当于C++的this指针。

__new__()必须要有返回值,返回实例化出来的实例对象。

4. 看一下阿里云天池python训练营对这两个魔法方法的教学:

__new__(cls[, ...]) 是在一个对象实例化的时候所调用的第一个方法,在调用 __init__初始化前,先调用 __new____new__至少要有一个参数 cls,代表要实例化的类,此参数在实例化时由 Python 解释器自动提供,后面的参数直接传递给 __init____new__对当前类进行了实例化,并将实例返回,传给 __init__self。但是,执行了 __new__,并不一定会进入 __init__,只有 __new__返回了,当前类 cls的实例,当前类的 __init__才会进入。
__new__没有正确返回当前类 cls的实例,那 __init__是不会被调用的,即使是父类的实例也不行,将没有 __init__被调用。 __new__方法主要是当你继承一些不可变的 class 时(比如 int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。

一般我们不会去重写__new__()方法,除非你确切知道怎么做,什么时候你会去关心它呢,它作为构造函数用于创建对象,是一个工厂函数,专用于生产实例对象。著名的设计模式之一,单例模式,就可以通过此方法来实现。

在自己写框架级的代码时,可能你会用到它,我们也可以从开源代码中找到它的应用场景,例如微型 Web 框架 Bootle 就用到了。

再来看几个例子。

通常来说,类开始实例化时,__new__()方法会返回cls(cls指代当前类)的实例然后该类的__init__()方法会接收这个示例(即self)作为自己的第一个参数,然后依次转入__new__()方法中接收的位置参数和命名参数。

notice:如果__new__()没有返回cls(即当前类的实例),那么当前类的__init__()方法是不会被调用的,如果__new__()返回了其他类的实例,那么只会调用被返回的那个类的构造方法。

上代码:

class A(object):
    def __init__(self, *args, **kwargs):
        print("run the init of A")
    def __new__(cls, *args, **kwargs):
        print("run thr new of A")
        return object.__new__(B, *args, **kwargs)

class B(object):
    def __init__(self):
        print("run the init of B")
    def __new__(cls, *args, **kwargs):
        print("run the new of B")
        return object.__new__(cls)

a = A()
print(type(a))
# run thr new of A
# <class '__main__.B'>
b = B()
print(type(b))
# run the new of B
# run the init of B
# <class '__main__.B'>

这个例子也是参考网上,自己修改的。感觉这个例子明白后,对__new__()的理解就差不多了。

接下来,看一下单例模式。

  1. 单例模式是什么

举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。

2. 创建单例-保证只有一个对象

# 实例化一个单例
class Singleton(object):
    __instance = None

    def __new__(cls, age, name):
        #如果类数字__instance没有或者没有赋值
        #那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时
        #能够知道之前已经创建过对象了,这样就保证了只有1个对象
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance
    
    def __init__(self,age,name):
        self.age = age
        self.name = name
a = Singleton(18, "wk")
b = Singleton(8, "mm")

print(id(a)==id(b))
print(a.age,a.name)
print(b.age,b.name)
a.size = 19 #给a指向的对象添加一个属性
print(b.size)#获取b指向的对象的age属性

# True
# 8 mm
# 8 mm
# 19

3. 创建单例,只执行一次__init__()方法。

# 实例化一个单例
class Singleton(object):
    __instance = None
    __first_init = False

    def __new__(cls, age, name):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def __init__(self,age,name):
        if not self.__first_init:
            self.age = age
            self.name = name
            Singleton.__first_init = True

a = Singleton(18, "wk")
b = Singleton(8, "mm")

print(id(a)==id(b))
print(a.age,a.name)
print(b.age,b.name)
a.size = 19 #给a指向的对象添加一个属性
print(b.size)#获取b指向的对象的age属性

# True
# 18 wk
# 18 wk
# 19

奇妙!基本ok了。

总结下__init__()和__new__()的区别:

  1. __init__()通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性,做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
  2. __new__()通常用于控制生成一个新实例的过程。它是类级别的方法。
  3. __new__()至少有一个参数cls,代表要实例化的类,此参数在实例化时会有python编辑器自动提供。
  4. __new__()必须有返回值,返回实例化出来的实例。
  5. 如果将类比作制造商,__new__()方法发就是前期的原材料环节,__init__()方法就是在有了原材料的基础上,加工,初始化商品的环节。

其他的魔法方法得再写一篇文章了,真是参考了大量网上的例子,真诚感谢前人们的博客、文章。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值