【Python】魔术方法(Magic Methods)

Posted by 西维蜀黍 on 2019-11-24, Last Modified on 2024-01-09

Python的魔术方法一般以__methodname__的形式命名,如:__init__(构造方法)、 __getitem____setitem____delitem__(调用del obj[key]时对应触发的函数)、 __len__(对类实例调用len(…)时对应触发的函数)等。

魔术方法具体有:

  • __init__(self):构造方法
  • __del__ :析构函数,释放对象时使用
  • __getitem__(self,key):返回键对应的值(按照索引获取值)
  • __setitem__(self,key,value):设置给定键的值(按照索引赋值)
  • __delitem__(self,key):删除给定键对应的元素
  • __len__():对类实例调用len(…)时对应触发的函数
  • __cmp_: 比较运算
  • __call_:调用
  • __add__:加运算
  • __sub__:减运算
  • __mul__:乘运算
  • __div__:除运算
  • __mod__:求余运算
  • __pow__:幂

需要注意的是,这些成员里面有些是方法,调用时要加括号,有些是属性,调用时不需要加括号。

# coding:utf-8
'''
    desc:尝试定义一种新的数据类型
          等差数列
'''


class ArithmeticSequence(object):
	MAX = 10

	def __init__(self, start=0, step=1):
		print 'Call function __init__'
		self.start = start
		self.step = step
		self.myData = {}
		i = self.start
		index = 0
		while i <= ArithmeticSequence.MAX:
			self.myData[index] = i
			i = i + self.step
			index += 1

	# 定义获取值的方法
	def __getitem__(self, key):
		print 'Call function __getitem__'
		try:
			return self.myData[key]
		except KeyError:
			return self.start + key * self.step

	# 定义赋值方法
	def __setitem__(self, key, value):
		print 'Call function __setitem__'
		self.myData[key] = value

	# 定义获取长度的方法
	def __len__(self):
		print 'Call function __len__'
		return len(self.myData)

	# 定义删除元素的方法
	def __delitem__(self, key):
		print 'Call function __delitem__'
		del self.myData[key]


s = ArithmeticSequence(1, 2)
print s[3]  # 9
print len(s)
del s[3]  # 删除3这个key

输出结果:

Call function __init__
Call function __getitem__
7
Call function __len__
5

__init__(self) - 构造方法

如果我们希望在创建某个特定类的任何一个实例时,都执行某些逻辑,我们可以将这个逻辑代码放在这个类的__init__方法中,这个逻辑通常是一些初始化工作:

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print('%s: %s' % (self.name, self.score))

注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的这个类实例本身。


与普通的实例方法类似,如果子类不重写 __init__,实例化子类时,Python interpreter 会自动先调用父类的__init__

如果子类重写了 __init__,实例化子类时,则只会调用子类的 __init__,此时如果想使用父类的 __init__,可以使用super函数,如下:

class P(object):
    def __init__(self, name, score):
        self.name = name
        self.score = name


class C(P):
    def __init__(self, name, score, age):
        # 显式地调用父类的构造函数
        super(C, self).__init__(name, score)
        self.age = age 

c = C('cq', 100, 31) 

__new__()

注意:__init__ 是实例创建之后调用的第一个方法,而 __new__ 更像构造函数,它在__init__之前被调用。

另外,__new__ 方法是一个静态方法,第一参数是cls,__new__方法必须返回创建出来的实例。

例如,用__new__实现单例模式:

class Singleton(object):
    def __new__(cls):
        # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance
 
obj1 = Singleton()
obj2 = Singleton()
 
obj1.attr1 = 'value1'
print obj1.attr1, obj2.attr1
print obj1 is obj2

__del__(self) - 析构函数

class NewClass(object):
    num_count = 0 

    def __init__(self,name):
        self.name = name
        self.__class__.num_count += 1
        print name,NewClass.num_count

    def __del__(self):
        self.__class__.num_count -= 1
        print "Del",self.name,self.__class__.num_count

a = NewClass("a")  
b = NewClass("b")  
c = NewClass("c")

del a
del b
del c

注意:用del删除一个对象的时候,不一定会调用__del__,只有在对象的引用计数为零时,del()才会被执行。

__iter__(self)next(self)

如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def next(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration();
        return self.a # 返回下一个值 

__call__() - 实例可以像函数一样调用

class Student(object):

	def __init__(self):
		print '__init__ is called'

	def __call__(self):
		print '__call__ is called'


s = Student() # __init__ is called
s()  # __call__ is called

__str__(self) - 返回用户看到的字符串

__repr__(self) - 返回开发者看到的字符串(用于调试)

# test.py
class P(object):
    def __str__(self):
        return "__str__ called"

    def __repr__(self):
        return "__repr__ called"

p = P() 

可以看下__str__和__repr__的区别:

>>> from test import p
>>> p
__repr__ called
>>> print p
__str__ called

Reference