python常见魔法函数

1、__init__():

所有类的超类object,有一个默认包含pass的__init__()实现,这个函数会在对象初始化的时候调用,我们可以选择实现,也可以选择不实现,一般建议是实现的,不实现对象属性就不会被初始化,虽然我们仍然可以对其进行赋值,但是它已经成了隐式的了,编程时显示远比隐式的更好,看下面的小栗子:

class test1:
	def method(self):
		self.a = ""
		self.b = ""
		return self.a, self.b

class test2:
	def __init__(self):
		self.a = ""
		self.b = ""

	def method(self):
		return self.a, self.b

print(vars(test1()))
print("-"*20)
print(vars(test2()))

"""
{}
--------------------
{'a': '', 'b': ''}
"""

我们可以通过vars函数获知显示声明的属性,但是隐式的就无法获知了,这并不值得提倡,但是在知道参数的情况下我们还是可以对其进行赋值的,如下:

class test1:
	def method(self):
		return self.a, self.b

class test2:
	def __init__(self):
		self.a = ""
		self.b = ""

	def method(self):
		return self.a, self.b

t1 = test1()
t1.a = 1
t1.b = 2
print(t1.method())

print("-"*20)

t2 = test2()
t2.a = 3
t2.b = 4
print(t2.method())

"""
(1, 2)
--------------------
(3, 4)
"""

不论怎么样,显示的初始化属性是一个好习惯。

2、__str__():

直接打印对象的实现方法,__str__是被print函数调用的,一般都是return一个什么东西,这个东西应该是以字符串的形式表现的。如果不是要用str()函数转换,我们可以直接print的对象都是实现了__str__这个方法的,比如dict。看下面的例子。

print(dir([]))

['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

__str__是dict其中的一个方法,这个方法的实现赋予了它直接被print的能力,知道这些,我们就可以给自己的类定义这个方法使其可以print了,例子如下:

class test:
	def __init__(self):
		self.a = 1

	def __str__(self):
		return "this is a str magic method, self.a=" + str(self.a)

print(test())

"""
this is a str magic method, self.a=1
"""

但是这个函数返回值必须为string,否则会抛异常:

class test:
	def __init__(self):
		self.a = 1

	def __str__(self):
		return self.a

print(test())

"""
Traceback (most recent call last):
  File "D:/myproject/leetcode/magic_method.py", line 11, in <module>
    print(test())
TypeError: __str__ returned non-string (type int)
"""

3、__new__():

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

class test(object):
	def __init__(self):
		print("start init ...")
		self.a = 1

	def __new__(cls, *args, **kwargs):
		print("create a instance [%s]" % cls)
		return object.__new__(cls, *args, **kwargs)

test()

"""
create a instance [<class '__main__.test'>]
start init ...
"""

可以看到,在实例化时候,先执行__new__再执行__init__,而且python会自动传入我们希望实例化的类,这里我们显示的调用了object的__new__,也可以调用其他的父类的__new__,那么如果我们定义了__new__,但是并没有返回一个本身实例,会发生什么事呢?例子如下:

class test(object):
	def __init__(self):
		print("start init ...")
		self.a = 1

	def __new__(cls, *args, **kwargs):
		print("create a instance [%s]" % cls)
		return "abc"

test()

"""
create a instance [<class '__main__.test'>]
"""

可以看到本身的__init__函数并未被调用,而是调用了str的__init__,可能这样并不直观,那么换一个实例返回,如下:

class obj(object):
	def __init__(self):
		print("another object called")

class test(object):
	def __init__(self):
		print("start init ...")
		self.a = 1

	def __new__(cls, *args, **kwargs):
		print("create a instance [%s]" % cls)
		test_new = obj()
		return test_new

test()
"""
create a instance [<class '__main__.test'>]
another object called
"""

这个就比较明显了,另一个实例的__init__被调用了。

4、__call__():

对象通过提供__call__()方法可以模拟函数的行为,如果一个对象提供了该方法,就可以像函数一样使用它,还是用例子进行说明。

class test(object):
	def __init__(self):
		self.a = 1

	def __call__(self, x):
		return "the value is {%s}" % x

c = test()
print(c(100))
"""
the value is {100}
"""

可以看到,我们在像使用函数一样使用类,实在是很有意思的事。

6、__len__():

len调用后会调用对象的__len__函数,我们可以为其定制输出,如下例子:

class test(object):
	def __init__(self):
        print("start init ...")
		self.a = [1,2,3,4,5]

	def __len__(self):
		return len(self.a) + 100

c = test()
print(len(c))
"""
start init ...
105
"""

但是该函数要求我们返回的值必须为int,否则会报错,如下:

class test(object):
	def __init__(self):
		print("start init ...")
		self.a = [1,2,3,4,5]

	def __len__(self):
		return str(len(self.a) + 100)

c = test()
print(len(c))

"""
Traceback (most recent call last):
  File "D:/myproject/leetcode/test.py", line 13, in <module>
    print(len(c))
start init ...
TypeError: 'str' object cannot be interpreted as an integer
"""

7、__repr__():

函数str() 用于将值转化为适于人阅读的形式,而repr() 转化为供解释器读取的形式,某对象没有适于人阅读的解释形式的话,str() 会返回与repr()一样,所以print展示的都是str的格式。例子:

class test(object):
	def __init__(self):
		print("start init ...")
		self.a = [1,2,3,4,5]

	def __str__(self):
		return "this is a string" + str(len(self.a) + 100)

	def __repr__(self):
		return str(len(self.a) + 100)

c = test()
print('-'*20)
print("%r" % test())
print('-'*20)
print(repr(test()))
print('='*20)
"""
start init ...
--------------------
start init ...
105
--------------------
start init ...
105
====================
"""

8、__setattr__():

该函数可以设置函数的属性,文字不知怎么描述,直接上例子:

class test(object):
	def __setattr__(self, key, value):
		if key == 'a':
			object.__setattr__(self,key,value+50)
		if key == 'b':
			object.__setattr__(self,key,value-30)

c = test()
c.a = 100
c.b = 200
print(vars(c))
"""
{'a': 150, 'b': 170}
"""

从上例可以看出,__setattr__函数可以支持对象增加属性,我们可以有计划的修改增加属性的内容。

9、__getattr__()

获取对象属性,只有在属性没有找到的时候调用,还是看例子:

class obj(object):
	def __init__(self):
		self.c = 5

class test(object):
	def __init__(self):
		self.a = 5

	def __setattr__(self, key, value):
		if key == 'a':
			object.__setattr__(self,key,value+50)
		if key == 'b':
			object.__setattr__(self,key,value-30)

	def __getattr__(self, item):
		a = obj()
		print("can not find attr %s" % item)
		return a.__getattribute__(item)

c = test()
c.a = 100
c.b = 200
print(c.a)
print('-'*20)
print(c.c)

"""
150
--------------------
can not find attr c
5

"""

第一个属性可以找到,所以不会调__getattr__,第二个属性找不到,所以会调用到。

10、__getattribute__():

该函数和上面介绍的__getattr__很像,都是获取属性,但是__getattr__是在属性不存在时被调用,而__getattribute__是无条件被调用,这样会方便我们做一些控制,需要注意,一旦定义了__getattribute__,则__getattr__不再会被调用,除非显式调用,例子如下:

class obj(object):
	def __init__(self):
		self.c = 5

class test(object):
	def __setattr__(self, key, value):
		if key == 'a':
			object.__setattr__(self,key,value+50)
		if key == 'b':
			object.__setattr__(self,key,value-30)

	def __getattribute__(self, item):
		try:
			ret = object.__getattribute__(self,item)
			print("attr %s existed" % item)
			return ret
		except:
			a = obj()
			print("can not find attr %s" % item)
			return a.__getattribute__(item)

c = test()
c.a = 100
c.b = 200
print(c.a)
print('-'*20)
print(c.c)
"""
attr a existed
150
--------------------
can not find attr c
5
"""

11、__delattr__():

本函数的作用是删除属性,实现了该函数的类可以用del 命令来删除属性,下面还是看个例子。

class obj(object):
	def __init__(self):
		self.c = 5

class test(object):
	def __setattr__(self, key, value):
		object.__setattr__(self,key,value+50)

	def __getattribute__(self, item):
		return object.__getattribute__(self,item)

	def __delattr__(self, item):
		object.__delattr__(self, item)


c = test()
c.a = 100
c.b = 200
print(vars(c))
del c.a
print(vars(c))

"""
{'a': 150, 'b': 250}
{'b': 250}
"""

12、__setitem__():

该函数可以给对象赋值,我们可以以下标的方式对其进行操作,下面看个例子。

class test(dict):
	def __setitem__(self, key, value):
		self.update({key: 'value is %s' % str(value)})

c = test()
c['a'] = 100
c.b = 200
print(c)
"""
{'a': 'value is 100'}
"""

13、__getitem__():

与__setitem__()函数相反,__getitem__可以使对象支持已下标的方式获取值,例子如下:

class test(object):
	def __init__(self):
		self.dict_t = {}

	def __setitem__(self, key, value):
		self.dict_t[key] = 'value is %s' % str(value)

	def __getitem__(self, item):
		ret = self.dict_t[item].split(' ')
		return bin(int(ret[-1]))

c = test()
c['a'] = 100
print(c.dict_t)
print(c['a'])
"""
{'a': 'value is 100'}
0b1100100
"""

这一类魔法函数对我们来说最大的好处是可以增加代码的优雅程度,并且可以方便的进行流程的控制,上面的例子,我们向操作字典一样在操作一个对象,并且在赋值与取值时都进行了变更,实在是比较好玩。

14、__delitem__():

该函数支持以下标方式删除对象数据,实现了这三个函数,这个类就像字典一样,具备了基本的增删查功能,有时候这样写会很方便。

class test(object):
	def __init__(self):
		self.dict_t = {}

	def __setitem__(self, key, value):
		self.dict_t[key] = 'value is %s' % str(value)

	def __getitem__(self, item):
		ret = self.dict_t[item].split(' ')
		return bin(int(ret[-1]))

	def __delitem__(self, key):
		del self.dict_t[key]

c = test()
c['a'] = 100
c['b'] = 200
print(c.dict_t)
print(c['a'])
del c['a']
print(c.dict_t)
"""
{'a': 'value is 100', 'b': 'value is 200'}
0b1100100
{'b': 'value is 200'}
"""

15、__iter__():

只要定义了__iter__()方法对象,就可以使用迭代器访问,这意味着,我们可以迭代我们自己定义的对象,例子如下。

class iteratorTest:
	def __init__(self):
		self._obj = []

	def __iter__(self):
		return iter(self._obj)

	def add(self, obj):
		self._obj.append(obj)

if __name__ == '__main__':
	def method_a():
		pass
	def method_b():
		pass
	c = iteratorTest()
	c.add(method_a)
	c.add(method_b)
	for ch in c:
		print(ch)
"""
<function method_a at 0x0000024E052EC1E0>
<function method_b at 0x0000024E056237B8>
"""

16、__del__():

这可以说是一个析构器,或者回收器,在对象引用数降到0时执行,有时可能还需要等一会再执行,所以一般不推荐使用,但是在代码中我们偶尔可以用它来实现一些必须要做的,但是并不紧急的事,下面是个例子。

class test(object):
	def __init__(self):
		self.id = 1

	def __del__(self):
		print("clean the garbage")

def test_del():
	a = test()
	return a.id

print(test_del())
"""
clean the garbage
1
"""

可以看到,在对象不再被引用后,会运行__del__函数。

posted @ 2022-04-05 19:07  我是一言  阅读(103)  评论(0编辑  收藏  举报