Python的魔术方法(Magic Methods)也称为双下划线方法(double underscore method),以双下划线开头和结尾,用于重载类的特殊行为。可以使类的实例对象表现出像内置类型的行为,如加、减、乘、切片、比较等,增加代码的可读性和可维护性。以下是Python中一些重要的魔术方法:
1.__init__
方法
__init__(self[, ...])
方法在创建类的实例对象时自动调用,用于进行初始化操作,为实例对象的属性设置初始值。其中,self代表实例对象本身。此外,__new__
方法在创建类的实例对象之前被调用,可以用来进行一些定制化的操作。
示例:
class MyClass:
def __init__(self, name):
self.name = name
obj = MyClass("Tom")
print(obj.name) # 输出 Tom
2.__call__
方法
__call__(self[, ...])
方法使得对象可以像函数一样被调用,用于对实例对象添加可调用特性。一般情况下,Python中的函数即对象,而__call__
方法可以让实例对象也表现得像函数。
示例:
class MyClass:
def __call__(self):
print("Hello, I can be called like a function.")
obj = MyClass()
obj() # 输出:Hello, I can be called like a function.
3.__str__
和__repr__
方法
__str__(self)
方法返回一个字符串,用于自定义实例对象的字符串输出格式。__repr__(self)
方法也返回一个字符串,表示对实例对象进行计算或操作的表达式或字符串,通常被用于调试和日志输出。
示例:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}, {self.age} years old."
def __repr__(self):
return f"Person(name={self.name}, age={self.age})"
p = Person("Tom", 18)
print(str(p)) # 输出:Tom, 18 years old.
print(repr(p)) # 输出:Person(name=Tom, age=18)
4.__add__
、__sub__
、__mul__
、__div__
(加减乘除)
__add__(self, other)
方法表示“加”,用于定义对象的加法行为,self
代表第一个加数,other
代表第二个加数。类似的,有__sub__
、__mul__
、__div__
等方法表示减、乘、除等。
示例:
class Vector:
def __init__(self, x=None, y=None):
self.x = x
self.y = y
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __str__(self):
return f"({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # 输出:(4, 6)
5.__len__
方法
__len__(self)
方法用于定义实例对象的长度,返回一个整数,即对象中元素的数量。常用于定制容器类型的大小。
示例:
class MyClass:
def __init__(self, lst=None):
self.lst = lst
def __len__(self):
return len(self.lst)
obj = MyClass([1, 2, 3])
print(len(obj)) # 输出:3
6.__getitem__
、__setitem__
和__delitem__
方法
__getitem__(self, key)
方法和__setitem__(self, key, value)
方法定义了实例对象的索引和切片功能,__delitem__(self, key)
方法用于实现删除操作。其中,key
表示索引或切片范围,value
表示要赋值的值。
示例:
class MyList:
def __init__(self, lst=None):
self.lst = lst if lst else []
def __getitem__(self, index):
return self.lst[index]
def __setitem__(self, index, value):
self.lst[index] = value
def __delitem__(self, index):
del self.lst[index]
def __len__(self):
return len(self.lst)
lst = MyList([1, 2, 3])
print(lst[1]) # 输出:2
lst[1] = 5
print(lst[1]) # 输出:5
del lst[0]
print(len(lst)) # 输出:2
7.__iter__
和__next__
方法
__iter__(self)
方法返回一个可迭代对象,用于定义实例对象的迭代行为。__next__(self)
方法用于定义实例对象的下一个元素是什么,具体包括迭代开始、迭代状态以及迭代结束时所需的返回值。
示例:
class Fibonacci:
def __init__(self, n):
self.n = n
self.current = 0
self.next = 1
def __iter__(self):
return self
def __next__(self):
if self.current + self.next > self.n:
raise StopIteration
self.current, self.next = self.next, self.current + self.next
return self.current
for i in Fibonacci(100):
print(i, end=' ') # 输出:1 1 2 3 5 8 13 21 34 55 89
8.__getattr__
和__setattr__
方法
__getattr__(self, attr)
方法用于获取实例对象的属性,当代码中使用了实例对象没有的属性时,该方法会被调用,如果没有定义该方法,则会抛出AttributeError
异常。__setattr__(self, attr, value)
方法用于设置实例对象的属性,当代码中设置实例对象没有的属性时,该方法会被调用。
示例:
class MyClass:
def __init__(self, name):
self.name = name
def __getattr__(self, attr):
return f"{attr} 属性不存在。"
def __setattr__(self, attr, value):
if attr == "name":
self.__dict__[attr] = value.upper()
else:
self.__dict__[attr] = value
obj = MyClass("Tom")
print(obj.name) # 输出:TOM
print(obj.age) # 输出:age 属性不存在。
9.__enter__
和__exit__
方法
__enter__(self)
方法会在进入with
语句时被调用,用于进行一些初始化操作,同时该方法需要返回一个上下文管理器对象。__exit__(self, exc_type, exc_value, traceback)
方法会在离开with
语句时被调用,用于进行一些清理操作。
示例:
class File:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.f = open(self.filename, "r")
return self.f
def __exit__(self, exc_type, exc_value, traceback):
self.f.close()
with File("test.txt") as f:
content = f.read()
print(content)
10.__eq__
、__lt__
、__le__
等比较方法
__eq__(self, other)
方法用于定义等于运算符==
,__lt__(self, other)
、__le__(self, other)
、__gt__(self, other)
、__ge__(self, other)
方法分别用于定义小于、小于等于、大于、大于等于运算符。
示例:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
def __lt__(self, other):
return self.age < other.age
p1 = Person("Tom", 18)
p2 = Person("Jerry", 20)
print(p1 == p2) # 输出:False
print(p1 < p2) # 输出:True
11.__hash__
和__call__
方法
__hash__(self)
方法定义了实例对象的hash
值,通常用于优化字典、集合等操作。__call__(self)
方法定义了实例对象的调用过程,用于将实例对象作为函数调用时的行为。
示例:
class MyClass:
def __init__(self, attr):
self.attr = attr
def __hash__(self):
return hash(self.attr)
def __call__(self):
print(f"The attribute is {self.attr}.")
obj1 = MyClass(1)
obj2 = MyClass(2)
print(hash(obj1)) # 输出:1
print(hash(obj2)) # 输出:2
obj1() # 输出:The attribute is 1.
12.__format__
方法
__format__(self, format_spec)
方法用于自定义实例对象的格式化,其中format_spec
为格式化字符串。
示例:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __format__(self, format_spec):
if format_spec == "r":
return f"({self.y}, {self.x})"
else:
return f"({self.x}, {self.y})"
p = Point(1, 2)
print(format(p)) # 输出:(1, 2)
print(format(p, "r")) # 输出:(2, 1)
13.__dir__
方法
__dir__(self)
方法用于自定义类的dir()
函数返回的属性列表,默认情况下会返回类的所有属性和方法,包括从父类继承的。
示例:
class MyClass:
def __init__(self, name):
self.name = name
def foo(self):
pass
def __dir__(self):
return [attr for attr in dir(self.__class__) if not attr.startswith("__")]
obj = MyClass("Tom")
print(dir(obj)) # 输出:['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'foo', 'name']
在上述示例中,__dir__
方法返回了类的所有属性和方法,但排除了以__
开头的魔术方法。
14.__init_subclass__
方法
__init_subclass__(cls, **kwargs)
方法在每个子类被创建时被调用,通常用于对子类进行初始化或配置。
示例:
class BaseClass:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
print(f"Creating subclass {cls.__name__}")
class SubClass1(BaseClass):
pass
class SubClass2(BaseClass):
pass
# 输出:Creating subclass SubClass1
# Creating subclass SubClass2
在上述示例中,当子类SubClass1
和SubClass2
被创建时,会自动调用__init_subclass__
方法,并输出相应的信息。
15.__set_name__
方法
__set_name__(self, owner, name)
方法在类的属性被设置时被调用,通常用于对属性进行验证或初始化。注意,该方法只在 Python 3.6 及以上版本中可用。
示例:
class Validator:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, owner):
return obj.__dict__[self.name]
def __set__(self, obj, value):
if value < 0:
raise ValueError(f"{self.name} should be greater than 0.")
obj.__dict__[self.name] = value
class MyClass:
x = Validator()
obj = MyClass()
obj.x = 1
print(obj.x) # 输出:1
obj.x = -1 # 抛出 ValueError 异常
在上述示例中,Validator
类的__set_name__
方法会将属性名保存在name
属性中,然后通过__get__
和__set__
方法对属性进行访问和设置。这里通过设置x
属性来演示__set_name__
方法的应用,如果设置的值小于 0,则抛出ValueError
异常。
16.__new__
方法
__new__(cls, *args, **kwargs)
方法是一个特殊的静态方法,用于创建类的实例对象,通常用于自定义类的实例化过程。该方法会返回创建的新实例对象,然后调用__init__
方法进行初始化。
示例:
class Singleton:
instance = None
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super().__new__(cls, *args, **kwargs)
return cls.instance
else:
return cls.instance
def __init__(self, name):
self.name = name
obj1 = Singleton("Tom")
obj2 = Singleton("Jerry")
print(obj1 == obj2) # 输出:True
print(obj1.name) # 输出:Tom
print(obj2.name) # 输出:Tom
在上述示例中,Singleton
类通过重写__new__
方法,实现了单例模式,保证每次实例化时返回的都是同一个实例对象。