笔记3:python魔法方法
定义:在Python的类中,以两个下划线开头、两个下划线结尾的方法如:__init__、__str__、__del__
魔术方法在类或对象的某些事件触发后会自动执行,如果希望根据自己的程序定制特殊功能的类,那么就需要对这些方法进行
python中常见的魔法方法大致可分为以下几类:
-
构造与初始化
-
类的表示
-
访问控制
-
比较操作
-
容器类操作
-
可调用对象
-
序列化
我们都知道一个最基本的魔术方法, __init__
。通过此方法我们可以定义一个对象的初始操作。但当实例化我们定义的类,如x = SomeClass() 的时候, __init__
并不是第一个被调用的方法。实际上,还有一个叫做 __new__
的方法,来实例化这个对象。然后给在开始创建时候的初始化函数 来传递参数。在对象生命周期的另一端,也有一个 __del__
方法。接下来看一看这三个方法:
3.1 __new__()
(1)__new__
(cls, [...]) 是在一个对象实例化的时候所调用的第一个方法,所以它才是真正意义上的构造方法。 (2)它的第一个参数是这个类,其他的参数是用来直接传递给 __init__
方法。 (3)__new__
决定是否要使用该 __init__
方法,因为 __new__
可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__
没有返回实例对象,则 __init__
不会被调用。 (4)__new__
主要是用于继承一个不可变的类型比如一个 tuple 或者 string。
class Person(object):
def __new__(cls, *args, **kwargs):
print("__new__()方法被调用了")
print('这个是*agrs', *args)
print('这个是kwagrs', **kwargs)
# cls表示这个类,剩余所有的参数传给__init__()方法,
# 若不返回,则__init__()不会被调用
return object.__new__(cls)
def __init__(self, name, age):
print("__init__()方法被调用了")
self.name = name
self.age = age
print(self.name, self.age)
p = Person("张三", 20)
# Output:
# __new__()方法被调用了
# 这个是*agrs 张三 20
# 这个是kwagrs
# __init__()方法被调用了
# 张三 20
那__new__()
在什么场景使用呢?
答:当我们需要继承内置类时,例如,想要继承 int、str、tuple不可变类型,就无法使用 __init__
来初始化了,只能通过 __new__
来初始化数据:下面这个例子实现了一个类,这个类继承了 float,之后就可以对这个类的实例进行计算了。
class g(float):
"""千克转克"""
def __new__(cls, kg):
return float.__new__(cls, kg * 2)
a = g(50) # 50千克转为克
print(a) # 100
print(a + 100) # 200 由于继承了float,所以可以直接运算,非常方便!
一般情况下,覆写 __new__()
的实现将会使用合适的参数调用其超类的 super().__new__()
,并在返回之前修改实例。例如:
class demoClass:
instances_created = 0
def __new__(cls,*args,**kwargs):
print("__new__():",cls,args,kwargs)
instance = super().__new__(cls)
instance.number = cls.instances_created
cls.instances_created += 1
return instance
def __init__(self,attribute):
print("__init__():",self,attribute)
self.attribute = attribute
test1 = demoClass("abc")
test2 = demoClass("xyz")
print(test1.number,test1.instances_created)
print(test2.number,test2.instances_created)
__new__(): <class '__main__.demoClass'> ('abc',) {}
__init__(): <__main__.demoClass object at 0x0000026FC0DF8080> abc
__new__(): <class '__main__.demoClass'> ('xyz',) {}
__init__(): <__main__.demoClass object at 0x0000026FC0DED358> xyz
0 2
1 2
__new__()
通常会返回该类的一个实例,但有时也可能会返回其他类的实例,如果发生了这种情况,则会跳过对 __init__()
方法的调用。而在某些情况下(比如需要修改不可变类实例(
class nonZero(int):
def __new__(cls,value):
return super().__new__(cls,value) if value != 0 else None#内建函数
def __init__(self,skipped_value):
#此例中会跳过此方法
print("__init__()")
super().__init__()
print(type(nonZero(-12)))
print(type(nonZero(0)))
返回
__init__()
<class '__main__.nonZero'>
<class 'NoneType'>