Python中的魔法方法:__new__、__init__和__call__的功能和应用
前言
python中有很多魔法方法,好多刚接触python编程的同学对此一头雾水,更不清楚实践中该如何使用。这里先主要介绍3个魔法方法以及一些相关实践。
神奇的魔法
__new__
__new__
是构造函数,负责对象的创建,他需要返回一个实例
案例一
class ClassA:
def __new__(cls, *args, **kwargs):
print("ClassA.__new__")
return super().__new__(cls)
ClassA() # ClassA.__new__
可以看到输出ClassA.__new__
,表明在创建对象时就会调用__new__
方法
案例二
再看下面这个例子,我们让__new__
不返回对象,会出现啥情况呢?
class ClassA:
def __new__(cls, *args, **kwargs):
print("ClassA.__new__")
a = ClassA()
print(a)
'''
ClassA.__new__
None
'''
a的值为None,为什么呢?就是因为没有在__new__
中返回任何对象。
注意:__new__
是构造函数,负责对象的创建,他需要返回一个实例
案例三
下面这个例子,我们在__new__
中返回一个其他对象,会出现啥情况呢?
class ClassA:
def __new__(cls, *args, **kwargs):
print("ClassA.__new__")
return 1
a = ClassA()
print(a)
'''
ClassA.__new__
1
'''
可以看到a的值为1,而不是ClassA object
。真是一个神奇的方法,这喝案例表明,我们可以通过重写__new__
方法来控制类对象的实例化过程。
当然也可以在ClassA的__new__
方法中创建其他对象,比如下面这样
class ClassA:
def __new__(cls, *args, **kwargs):
print("ClassA.__new__")
return super().__new__(ClassB)
# return ClassB() # 也可以这样写
class ClassB:
pass
a = ClassA()
print(a)
'''
ClassA.__new__
<__main__.ClassB object at 0x103b17a00>
'''
这样就在ClassA
中创建了ClassB
对象。
注意: 这里只是举个例子来讲述__new__
的魔法,实践项目中避免这种写法。
__init__
__init__
是一个初始化函数,负责对__new__
实例化的对象进行初始化。这样看来,应该是先调用__new__
再调用__init__
案例一
class ClassA:
def __new__(cls, *args, **kwargs):
print("ClassA.__new__ ")
return super().__new__(cls)
def __init__(self):
print("ClassA.__init__")
ClassA()
'''
ClassA.__new__
ClassA.__init__
'''
证实了我们上面的观点,创建一个对象时,会先调用__new__
方法,再调用__init__
方法
__new__
方法必须有返回值,那__init__
方法呢?我们看下面的案例
案例二
class ClassA:
def __init__(self, value):
self.value = value
return "Initialization complete"
obj = ClassA(10)
运行发现报如下错误TypeError: __init__() should return None, not 'str'
很显然,不允许有返回值。
__call__
内建函数callable 如果callable的对象参数显示为可调用,则返回True,否则返回False.如果返回True,则调用仍然可能失败;但如果为False,则调用对象永远不会成功。 我们平时自定义的函数、内置函数和类都属于可调用对象,但凡可以把一对括号"()"应用到某个对象身上时,都可称之为可调用对象
def funTest(name):
print("this is test function, name:", name)
print(callable(filter)) # True
print(callable(max)) # True
print(callable(object)) # True
print(callable(funTest)) # True
var = "test"
print(callable(var)) # False
funTest("python")
__call__
的作用就是声明这个类的对象是可调用的(callable)。即实现__call__
方法之后,用callable调用这个类的对象时,结果都为True.
class ClassA:
pass
a = ClassA()
print(callable(a)) # False
实现__call__
的类
class ClassA:
def __call__(self, *args, **kwargs):
print("this is __call__ function, args", args)
a = ClassA()
print(callable(a)) # True
a("arg1", "arg2") # this is __call__ function, args ('arg1', 'arg2')
a是ClassA的实例对象,同时还是可调用对象,因此就可以像调用函数一样调用它
实践一:
还记得类装饰器吗,类装饰器主要依赖于函数__call__()
,每当你调用一个类的实例时,函数__call__()
就会被执行一次
日志功能,比如下面这段代码
class Logger:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('Calling function:', self.func.__name__)
return self.func(*args, **kwargs)
@Logger
def add(a, b):
return a + b
result = add(3, 5)
# 输出:
# Calling function: add
# 8
在上面的示例中,Logger
类实现了 __call__
方法,使其实例可以像函数一样被调用。它接收原始函数作为参数,并在调用时打印函数名。通过将 Logger
实例应用为装饰器,我们可以将其作用于 add
函数上,从而在函数调用时添加额外的行为。
实践二:
创建一个可以像函数一样被调用的对象,而不是一个普通的函数
class Calculator:
def __init__(self):
self.result = 0
def __call__(self, a, b):
self.result = a + b
return self.result
calc = Calculator()
result = calc(3, 5) # 调用可调用对象
print(result) # 输出: 8
在上面的示例中,Calculator
类实现了 __call__
方法,使其实例可以像函数一样被调用。通过调用 calc
实例并传递参数,会触发 __call__
方法的执行,并返回计算的结果。
总之,__call__
方法可以在实际项目中提供更灵活和自定义的对象行为。通过实现 __call__
方法,类的实例可以表现得像函数一样,从而增加了代码的可读性和可扩展性。
最后
详细到这里已经基本了解这三个魔法方法以及各自的用途了。但这里还是做一个总结:
__new__
方法是一个类方法,负责创建并返回一个新的实例对象。它是在对象实际被创建之前调用的,因此其参数传递的是类本身(通常被称为cls
)以及__new__
方法所需的其他参数。__new__
方法的返回值通常是通过调用父类的__new__
方法来获得新的实例对象。这个新的实例对象随后将被传递给__init__
方法进行初始化。__init__
方法是一个实例方法,在对象创建后被调用,负责对象的初始化。它接收的第一个参数是 self,表示正在初始化的实例对象。__init__
方法用于设置对象的初始状态,设置属性值,执行其他必要的初始化操作。它一般不返回任何值,只是对对象进行初始化。__call__
方法是一个实例方法,用于使对象变为可调用(类似于函数)。当调用一个对象时,Python 会查找并调用该对象的__call__
方法。__call__
方法可以使对象像函数一样被调用,接收参数并执行相应的操作。通过定义__call__
方法,可以使类的实例表现得像函数一样可调用。
再简短点概括:
__new__
方法负责对象的创建和内存分配,返回一个新实例对象。__init__
方法负责对象的初始化和属性设置。__call__
方法使对象变为可调用(类似于函数)
转载自:https://juejin.cn/post/7268281090611216425