Python笔记:魔术方法----常用魔术方法(__init__、__new__、__call__、__str__、__repr__、__del__等)

本文深入解析Python中的魔术方法,如__init__、__new__、__call__、__str__、__repr__、__del__、__bool__、__len__等,探讨它们的触发时机、作用及使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法(Magic Method)。这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能。

常用的魔术方法

__init__:构造函数

触发时机:实例化对象之后自动触发,在__new__之后执行。实例化对象其实包含两步,1:制作一个对象 2:为对象初始化操作
作用:为对象添加对象的所属成员
参数:self必填,接收当前对象,其他参数可根据实例化的传参决定
返回值:无 注意事项:无

class Human:
    def __init__(self, name):
        print('__init__:%s' % name)
    # 方法
    def eat(self):
        print('eat method')
        
    def run(self):
        print('run method')


human = Human('Tom')  # 执行这一步实例化对象human之后, 会触发 __init__, 打印出__init__:Tom

__new__:构造函数

触发时机:实例化对象的时候自动触发,在__init__之前执行。实例化对象其实包含两步,1:制作一个对象 2:为对象初始化操作
作用:管理控制对象的生成过程;实现单例设计模式
参数:cls必填,其他参数可根据实例化的传参决定
返回值:必须返回类的实例化对象
注意事项:无

在object类中存在一个静态的__new__(cls, *args, **kwargs)方法,该方法需要传递一个参数cls,cls表示需要实例化的类,此参数在实例化时由Python解释器自动提供,__new__方法必须有返回值,且返回的是被实例化的实例,只有在该实例返回后才会调用__init__来进行初始化,初始化所用的实例就是__new__返回的结果。

调用以创建一个 cls 类的新实例__new__()是一个静态方法 (因为是特例所以你不需要显式地声明),它会将所请求实例所属的类作为第一个参数。其余的参数会被传递给对象构造器表达式 (对类的调用)。 __new__()的返回值应为新对象实例 (通常是 cls 的实例)。

典型的实现会附带适宜的参数使用super().__new__(cls[, ...]),通过超类的__new__()方法来创建一个类的新实例,然后根据需要修改新创建的实例再将其返回。

如果__new__()在构造对象期间被发起调用并且它返回了一个实例或 cls 的子类,则新实例的__init__() 方法将以__init__(self[, ...])的形式被发起调用,其中 self 为新实例而其余的参数与被传给对象构造器的参数相同。

__new__()的目的主要是允许不可变类型的子类 (例如 int, str 或 tuple) 定制实例创建过程。它也常会在自定义元类中被重载以便定制类创建过程。

class Human:
    def __init__(self, name):
        print('__init__:%s' % name)

    def __new__(cls, name):
        print('__new__:%s' % name)
        return object.__new__(cls)  # 也可以用 return super().__new__(cls)
    # 方法
    def eat(self):
        print('eat method')

    def run(self):
        print('run method')

human = Human('Tom')  # 执行这一步实例化对象human之后, 会先触发__new__打印出__new__:Tom,
# 返回实例化对象,再触发 __init__,打印出__init__:Tom

# 输出结果:
>>>>>__new__:Tom
>>>>>__init__:Tom

__new__在元类与普通类继承中的比较:

# ******************元类**********************
class ListMetaclass(type):
     def __new__(cls, name, bases, attrs):
       print(cls, name, bases, attrs)
       result = type.__new__(cls, name, bases, attrs)
       print('Result--',result)
       return result

class MyList(object, metaclass=ListMetaclass):
     name = 'mylist'
     def fun():
         pass
     
# *********普通类继承*************************                                              
class Animal(object):
     def __init__(self):
        pass
     def __new__(cls, *args, **kw):
        print("\n"*2,cls, args, kw)
        result = super().__new__(cls)
        print("Result--",result)
        return result

class Cat(Animal):
    name = 'my name is cat.'
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight
        
Cat('huahua', 30)


# 输出结果:
<class '__main__.ListMetaclass'> MyList (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'MyList', 'name': 'mylist', 'fun': <function MyList.fun at 0x000001AEB63578B8>}
Result-- <class '__main__.MyList'>   # cls为ListMetaclass(元类),返回MyList类


<class '__main__.Cat'> ('huahua', 30) {}  
Result--<__main__.Cat object at 0x000001AEB6276408>  # cls为cat类(子类),返回cat对象

__call__

触发时机:将实例化对象当做函数调用的时候自动触发(函数其实也是对象,python中一切皆对象)
作用:常用于归结类或对象的操作步骤,方便后期操作
参数:self必填,接收当前对象
返回值:可有可无
注意事项:无

对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Human:
    def __init__(self, name):
        self.name = name
    
    def __call__(self):
        print('__call__ is called ')
        return self.name
        
    def run(self):
        print('run method')


human = Human('Tom')  
human()        # 实例human可以被调用,即human()
print(human()) # 把实例human当函数调用

# 输出结果:
>>>>>__call__ is called 
>>>>>__call__ is called 
>>>>>Tom

函数或可调用对象,都是由__call__方法实现的,如:
在这里插入图片描述
用__call__()弥补 hasattr() 函数的短板
hasattr() 函数的功能是查找类的实例对象中是否包含指定名称的属性或者方法,但该函数有一个缺陷,即它无法判断该指定的名称,到底是类属性还是类方法。
要解决这个问题,我们可以借助可调用对象的概念。要知道,类实例对象包含的方法,其实也属于可调用对象,但类属性却不是。举个栗子:

class Python:
    def __init__ (self):
        self.name = "Python"
       
    def say(self):
        print("我正在学Python")
        
python = Python()
if hasattr(python,"name"):
    print(hasattr(python.name,"__call__"))
print("**********")
if hasattr(python,"say"):
    print(hasattr(python.say,"__call__"))

# 输出结果:
>>>>>False
>>>>>**********
>>>>>True 

__str__

触发时机:使用print打印对象的时候触发
作用:可以自定义打印对象时输出的内容
参数:self必填,接收当前对象
返回值:必须有,且一定是字符串类型
注意事项:除print之外,只用str()时也会触发该魔术方法

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

    def __str__(self):
        print('print will call __str__ first.')
        return 'Hello ' + self.name + '!'

    def eat(self):
        print('eat method')
        
mc = MyClass("Jerry") # 实例化调用
print(mc)  # 没有__str__方法,打印输出<__main__.MyClass object at 0x036F7610>
print(MyClass('Tom'))# 类直接调用 没有__str__方法,打印输出<__main__.MyClass object at 0x0398D0E8>

# 输出结果:

>>>>>print will call __str__ first.
>>>>>Hello Tom!

如果不是要用str()函数转换,我们可以直接print的对象都是实现了__str__这个方法的,比如str,list,dict。
在这里插入图片描述
__repr__

触发时机:使用repr转换对象的时候触发
作用:可以设置repr函数操作对象的输入结果
参数:self必填,接收当前对象
返回值:必须有,且一定是字符串类型
注意事项:其实__str__和__repr__是完全一样的,在实际中,我们可以只用实现__repr__,因为如果一个对象没有实现__str__,解释器会用__repr__代替

class Human:

    def __init__(self, name):
        self.name = name

    # def __str__(self):
    #     return 'Name: %s' % self.name

    def __repr__(self):
        return 'Name:%s' % self.name

    # 方法
    def eat(self):
        print('eat method')

    def run(self):
        print('run method')


human = Human('Python')
print(human.__repr__(),human) # 该类没有实现__str__,但是打印对象的时候,会执行__repr__
# human.__repr__(),human等价

# 输出结果:
Name:Python Name:Python

__del__

触发时机:对象被系统回收的时候自动触发(del不一定触发)
作用:回收程序使用过程中的信息和变量等
参数:self必填,接收当前对象
返回值:无
注意事项:如果主动使用del(非__del__),则会在调用del的时候触发,而非程序最后,但也不是调用了del一定会触发__del__

注:del x并不直接调用x.__del__(),前者会将 x 的引用计数减一,而后者仅会在 x 的引用计数变为零时被调用,并且定义了__del__()的实例无法被Python的循环垃圾收集器收集,所以尽量不要自定义__del__()。一般情况下,__del __()不会破坏垃圾处理器。

class Point:
   def __init__( self, x=0, y=0):
      self.x = x
      self.y = y
   def __del__(self):
      class_name = self.__class__.__name__
      print(class_name, "销毁")
 
pt1 = Point()
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3)) # 打印对象的id
del pt1
del pt2
del pt3  # 引用计数变为0,最后调用__del__()

# 输出结果为:
>>>>>52131344 52131344 52131344
>>>>>Point 销毁

__bool__

触发时机:使用bool()转换对象的时候触发
作用:用于检测对象成员的信息
参数:self必填,接收当前对象
返回值:必须有,且一定是bool值,即True或False
注意事项:无

class Human:
    def __init__(self, name):
        self.name = name

    def __bool__(self):
        if self.name == 'Tom':
            return True
        else:
            return False
    # 方法
    def eat(self):
        print('eat method')

    def run(self):
        print('run method')


human = Human('Tom')
human2 = Human('Python')
print(bool(human))
print(bool(human2))

# 输出结果:
>>>>>True
>>>>>False

__len__

触发时机:使用len函数检测对象长度的时候自动触发
作用:使len函数可以直接检测对象中某个数据的长度
参数:self必填,接收当前对象,无其他参数
返回值:必须有,且必须是整形
注意事项:无

class Human:
    def __init__(self, name):
        self.name = name

    def __len__(self):
        # 必须有返回值,而且必须是整形
        return len(self.name)
    # 方法
    def eat(self):
        print('eat method')

    def run(self):
        print('run method')


human = Human('Tom')
print(len(human)) # 等价于human.__len__()
#如果没有实现__len__, 则无法直接使用len(human), 
#否则报TypeError: object of type 'Human' has no len(), 实现了__len__后,可以根据需要
# 等价于利用__len__方法实现len()函数的功能


# 输出结果:
>>>>>3

可以使用len()的对象都实现了__len__这个方法,如str,dict。
在这里插入图片描述

参考来源:https://www.jianshu.com/p/892792cb774d
https://www.cnblogs.com/wangchengpei/p/11116546.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值