Python中的魔法方法

前面的话:在Pyhton中,我们经常可以看到以双下滑线__包裹起来的方法,最常见的就是__init__,这些方法被称为魔法方法或者特殊方法。简单的说,这些方法可以给Python的类提供特殊功能。方便我们定制一个类。定义一个类时,我们用的最多就是__init__方法,而__new__方法和__call__方法使用的比较少。下面我们介绍最常用的几个魔法方法。

一、 __init__方法

__init__方法用于控制实例对象的初始化工作,也就是给实例对象绑定属性,以初始化对象的特征。要不生成的对象在人类的世界是一样的,因为并没有特殊的标识,但每个对象在内存中的地址是相互独立的,也就是说并不是同一个对象。

另外要特别注意点是:这个方法并不是类实例化过程中调用的第一个方法。因为系统执行该方法前,其实该对象已经存在了,要不初始化什么东西呢?

class Magic(object):
          def __init__(self):
                    print("__init__  is  called")
                    super(Magic, self).__init__()

          def __new__(cls):
                    print("__new__  is  called")
                    return super(Magic, cls).__new__(cls)

          def __call__(self):
                    print("__call__  is  called")

instance = Magic()

输出:

这里写图片描述
从输出结果来看,__new__方法最先被调用,返回一个实例对象,接着__init__方法被调用。

稍微修改下代码:

class Magic(object):
          def __init__(self):
                    print("__init__  is  called")
                    print(self)
                    super(Magic, self).__init__()

          def __new__(cls):
                    print("__new__  is  called")
                    self = super(Magic, cls).__new__(cls)
                    print(self)
                    return self

          def __call__(self):
                    print("__call__  is  called")

instance = Magic()

输出:
这里写图片描述
从输出来看,__new__方法的返回值就是类的实例对象,这个实例对象会传递给__init__方法中定义的self参数,以便实例对象可以被正确的初始化。

如果__new__方法不返回值(或者说返回None),那么__init__方法将不会得到调用。因为实例对象还没有创建出来,调用__init__是没有任何意义的。

另外,__init__方法中除了self之外定义的参数,都将与__new__方法中除cls参数以外的所有参数都必须保持一致或者等效。
这里写图片描述

二、__new__方法

__new__方法用于控制类实例化过程中对象的生成工作,它是一个类级别的方法。
最基本的作用:就是当你继承一些不可变class的时候(比如int、tuple),提供给你一个自定义这些类的实例化过程的途径,还有就是实现自定义的metaclass。

首先我们来看第一个功能,假设我们需要一个永远是正数的整数类型。我们可能会写出这样的代码。

class PositiveInteger(int):
    def __init__(self, value):
        super(PositiveInteger, self).__init__(self, abs(value))

i = PositiveInteger(-3)
print(i)

但运行后发现,对于int这种不可变对象,我们只有重载它的__new__方法才能起到自定义的作用。

class PositiveInteger(int):
          def __new__(cls, value):
                    return super(PositiveInteger, cls).__new__(cls, abs(value))

i = PositiveInteger(-2)
print(i)

通过重载__new__方法,我们实现了所要的功能。
另一个作用,就是自定义metaclass。
通过__new__实现设计模式中的单例模式。因为类每一次实例化后产生的过程都是通过__new__来控制的,所以我们可以通过重载__new__方法。就可以很简单的实现单列模式。

class SingLeton(object):

          def __new__(cls):
                    if not hasattr(cls, "instance"):
                              cls.instance = super(SingLeton, cls).__new__(cls)
                    return cls.instance

obj1 = SingLeton()
obj2 = SingLeton()

obj1.attr1 = "value1"

print(id(obj1))
print(id(obj2))

print(obj1.attr1, obj2.attr1)
print(obj1 is obj2)

这里写图片描述
可以看到obj1和obj2是同一个实例。
这就是通过__new__方法实现单例模式的一种方式,如果实例对象存在,则直接返回该实例对象,如果不存在,那么就先创建一个实例对象,再返回。

三、__call__方法

用于使一个实例对象成为一个可调用对象,就像函数那样可以调用(函数名())。
下面先来解释什么是可调用对象:可调用对象(callable),我们平时自定的函数,内置函数和类都是可调用对象,但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象。判断一个对象是不是可调用对象可以用函数callable

所以说,如果一个类中,实现了__call__方法,那么由这个类生成的实例对象就是一个可掉用对象。
这里写图片描述

所以此时,这个instance对象既是一个实例对象,又是一个可调用对象,我们可以像函数一样调用它。
这里写图片描述

那么这个特点有哪些应用场合了:实例对象可以像函数一样作为可调用对象来用,基于这个特点,我们可以实现基于类的装饰器,在类里面记录状态,比如,我们可以记录函数被调用的次数。

class Counter(object):

          def __init__(self, fun):
                    self.fun = fun
                    self.count = 0

          def __call__(self, *args, **kwargs):
                    self.count += 1
                    return self.fun(*args, **kwargs)

@Counter
def foo():
          pass

for i in range(10):
          foo()

print(foo.count)

这里写图片描述

上面这么解释可能不是很好理解,这是因为我们定义了一个无参的类装饰器,所以我们调用函数时,会把foo函数作为参数传给Counter这个类,然后实例化了一个实例对象(foo),因此,我们现在调用foo时,实际上是调用的这个实例对象,进而去调用这个类中的__call__方法。达到计数的功能。

四、__del__方法

__del__方法:用于删除一个对象,这个方法有一个专业术语——析构函数。Python类似Java语言是有垃圾回收机制的,所以不需要像C++那样通过析构函数手动释放内存。但python同样提供了`__del__释放方法。当一个对象的引用计数为0时,会被自动调用,以清除这个对象。

都知道python是一门脚本语言,解释型语言,但Python其实是有编译过程的。所以说的细致一点python是一门先编译后解释的语言,编译指的是编译成字节码,然后逐行解释这些字节码。
所以整个过程是这样的:过程.py->编译->PyCodeObject->解释(虚拟机执行)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值