Python魔法方法
简介:什么是魔法方法
- Python里面的魔法函数,是以带双下划线开头和结尾,可以帮助类增强一些功能。这样方法可以在特定的情况下被Python调用,而几乎不用直接调用。
- 魔法函数和类本身没有直接关系,和类的父类,object也没有关系。魔法函数可以写到任意一个类中,跟继不继承没有必然的关系。
- 魔法函数定义了,我们不需要显式的调用它,Python解释器自己会知道什么情况下会调用,我们只需要在使用相应的语法的时候就会调用。
PS:
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
基础魔法方法
__init__
最常用的魔法方法,在创建完对象后调用,对当前对象的一些实例初始化,无返回值,我们称之为构造方法
例1
class Demo(object):
def __init__(self):
print('调用__init__方法')
def func(self):
print('这是一个普通的方法')
d = Demo()
输出
调用__init__方法
这个例子印证了上述:__init__
方法在创建对象后被自动调用。
例2
对实例初始化。同样的,类中定义了__init__和func两个方法,但这次我们看到在__init__方法中添加了两个参数n和a,并将这两个值赋给成员属性self.name和self.age,在func方法中则使用了这两个成员属性
PS:
-
self是在为类编写实例方法的时候,放在变量名第一个位子的占位词。
-
在具体应用实例方法里,可以不使用self这个变量。
-
self,就是指由这个类创造出来的实例
class Demo:
def __init__(self, n, a):
self.name = n
self.age = a
def func(self):
print('我的名字是 %s, 我的年龄是 %d'%(self.name, self.age))
d1 = Demo('小明',23)
d1.func()
d2 = Demo('小红',21)
d2.func()
输出
我的名字是 小明, 我的年龄是 23
我的名字是 小红, 我的年龄是 21
__new__
new() 是一种负责创建类实例的静态方法,它无需使用 staticmethod 装饰器修饰,且该方法会优先 init() 初始化方法被调用
new()通常用于控制生成一个新实例的过程。它是类级别的方法。
new()至少有一个参数cls,代表要实例化的类。
new()必须有返回值,返回实例化出来的实例。
例3
class Demo:
def __new__(cls, *args, **kwargs):
print('调用__new__方法')
def __init__(self):
print('调用__init__方法')
d = Demo()
输出
调用__new__方法
确实首先调用了__new__方法,但奇怪的是并没有调用__init__方法,这是为什么呢?实际上,__new__是负责对当前类进行实例化,并将实例返回,并传给__init__方法,__init__方法中的self就是指代__new__传过来的对象,所以再次强调,__init__是实例化后调用的第一个方法。
PS:args和**kwargs是python函数方法的位置参数和关键字参数,当你不确定你的函数里将要传递多少参数时你可以用args,**kwargs
允许你使用没有事先定义的参数名。
例4
def print_func1(*args):
print(args)
def print_func2(**kwargs):
print(kwargs)
print_func1(1, 2, '嘿嘿', [])
print_func2(a=1, b=2, c='哈哈', d=[])
输出
(1, 2, '嘿嘿', [])
{'a': 1, 'b': 2, 'c': '哈哈', 'd': []}
例5
class Demo:
def __new__(cls, *args, **kwargs):
print('调用__new__方法')
return object.__new__(cls)
def __init__(self):
print('调用__init__方法')
d = Demo()
输出
调用__new__方法
调用__init__方法
在__new__方法中添加了一条return语句,返回object的__new__方法,由于我们自定义的Demo类的父类都是object类,实际上这里是为了返回了父类object生成的对象,然后将此对象传递给__init__的self。因此只有__new__方法正确return了当前类cls的实例,__init__方法才会被调用。特别需要说明的是,__new__方法中一定是返回父类的__new__方法来创建对象,如果返回当前对象会造成死循环。
__del__
同__init__方法相反,__del__在对象销毁时被调用,往往用于清除数据或还原环境等操作,比如在类中的其他普通方法中实现了插入数据库的语句,当对象被销毁时我们需要将数据还原,那么这时可以在__del__方法中实现还原数据库数据的功能。__del__被成为析构方法。
例6
class Demo:
def __init__(self):
print('调用__init__方法')
def func(self):
print('这是一个普通的方法')
def __del__(self):
print('调用__del__方法')
d = Demo()
d.func()
输出
调用__init__方法
这是一个普通的方法
调用__del__方法
当d.func()执行后,对象d没有在任何一个地方被继续引用,这时Python的垃圾回收机制会主动回收这个对象,即销毁d,此时自动调用__del__方法。
__call__
允许类的一个实例像函数那样被调用。本质上这代表了 x() 和 x.call() 是相同的。注意 call 可以有多个参数,这代表你可以像定义其他任何函数一样,定义 call ,喜欢用多少参数就用多少。
例7
class test:
def __init__(self):
self.x = 1
def __call__(self, x):
return "value is %s" % x
b = test()
print b(100)
输出
value is 100
属性相关的魔法方法
__getattr__
当我们访问一个不存在的属性的时候,会抛出异常,提示我们不存在这个属性。而这个异常就是__getattr__方法抛出的,其原因在于他是访问一个不存在的属性时最后执行的行为。
例8
class A:
def __init__(self, value):
self.value = value
def __getattr__(self, item):
print("into __getattr__")
return "can not find"
a = A(10)
print(a.value)
print(a.name)
输出
10
into __getattr__
can not find
__setattr__
由于每次类实例进行属性赋值时都会调用__setattr__(),所以可以重载__setattr__()方法,来动态的观察每次实例属性赋值时__dict__()的变化。下面的Fun类重载了__setattr__()方法,并且将实例的属性和属性值作为__dict__的键-值对:
例9
class Fun:
def __init__(self):
self.name = "Liu"
self.age = 12
self.male = True
def __setattr__(self, key, value):
print("*"*50)
print("setting:{}, with:{}".format(key, value))
print("current __dict__ : {}".format(self.__dict__))
# 属性注册
self.__dict__[key] = value
fun = Fun()
输出
**************************************************
setting:name, with:Liu
current __dict__ : {}
**************************************************
setting:age, with:12
current __dict__ : {'name': 'Liu'}
**************************************************
setting:male, with:True
current __dict__ : {'name': 'Liu', 'age': 12}
通过在__setattr__()中将属性名作为key,并将属性值作为value,添加到了__dict__中,可以看出,init()中三个属性赋值时,每次都会调用一次__setattr__()函数。
__delattr__
本函数的作用是删除属性,实现了该函数的类可以用del 命令来删除属性。
例10
class Coordinate:
def __init__(self):
self.x = 10
self.y = -5
self.z = 0
point1 = Coordinate()
print('x = ',point1.x)
print('y = ',point1.y)
print('z = ',point1.z)
delattr(Coordinate, 'z')
print('--删除 z 属性后--')
print('x = ',point1.x)
print('y = ',point1.y)
# 触发错误
print('z = ',point1.z)
输出
('x = ', 10)
('y = ', -5)
('z = ', 0)
--删除 z 属性后--
('x = ', 10)
('y = ', -5)
Traceback (most recent call last):
File "test.py", line 22, in <module>
print('z = ',point1.z)
AttributeError: Coordinate instance has no attribute 'z'
__len__
len函数被调用后会调用对象的__ len__函数,实现实例对象的内部逻辑
例11
class Foo:
def __init__(self, ):
self.a = [1, 3, 5]
def __len__(self):
return len(self.a) + 100
f1 = Foo()
print(len(f1))
输出
103
Process finished with exit code 0
参考链接:
Python魔法方法指南: https://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html