目录
![]()
![]()
一、概述
1、定义
魔法方法(Magic Methods/Special Methods,也称特殊方法或双下划线方法)是Python中一类具有特殊命名规则的方法,它们的名称通常以双下划线(`__`)开头和结尾。
魔法方法用于在特定情况下自动被Python解释器调用,而不需要显式地调用它们,它们提供了一种机制,让你可以定义自定义类时具有与内置类型相似的行为。
2、作用
魔法方法允许开发者重载Python中的一些内置操作或函数的行为,从而为自定义的类添加特殊的功能。
二、应用场景
1、构造和析构
1-1、__init__(self, [args...]):在创建对象时初始化属性。
1-2、__new__(cls, [args...]):在创建对象时控制实例的创建过程(通常与元类一起使用)。
1-3、__del__(self):在对象被销毁前执行清理操作,如关闭文件或释放资源。
2、操作符重载
2-1、__add__(self, other)、__sub__(self, other)、__mul__(self, other)等:自定义对象之间的算术运算。
2-2、__eq__(self, other)、__ne__(self, other)、__lt__(self, other)等:定义对象之间的比较操作。
3、字符串和表示
3-1、__str__(self):定义对象的字符串表示,常用于print()函数。
3-2、__repr__(self):定义对象的官方字符串表示,用于repr()函数和交互式解释器。
4、容器管理
4-1、__getitem__(self, key)、__setitem__(self, key, value)、__delitem__(self, key):用于实现类似列表或字典的索引访问、设置和删除操作。
4-2、__len__(self):返回对象的长度或元素个数。
5、可调用对象
5-1、__call__(self, [args...]):允许对象像函数一样被调用。
6、上下文管理
6-1、__enter__(self)、__exit__(self, exc_type, exc_val, exc_tb):用于实现上下文管理器,如with语句中的对象。
7、属性访问和描述符
7-1、__getattr__, __setattr__, __delattr__:这些方法允许对象在访问或修改不存在的属性时执行自定义操作。
7-2、描述符(Descriptors)是实现了__get__, __set__, 和__delete__方法的对象,它们可以控制对另一个对象属性的访问。
8、迭代器和生成器
8-1、__iter__和__next__:这些方法允许对象支持迭代操作,如使用for循环遍历对象。
8-2、__aiter__, __anext__:这些是异步迭代器的魔法方法,用于支持异步迭代。
9、数值类型
9-1、__int__(self)、__float__(self)、__complex__(self):定义对象到数值类型的转换。
9-2、__index__(self):定义对象用于切片时的整数转换。
10、复制和序列化
10-1、__copy__和__deepcopy__:允许对象支持浅复制和深复制操作。
10-2、__getstate__和__setstate__:用于自定义对象的序列化和反序列化过程。
11、自定义元类行为
11-1、__metaclass__(Python 2)或元类本身(Python 3):允许自定义类的创建过程,如动态创建类、修改类的定义等。
12、自定义类行为
12-1、__init__和__new__:用于初始化对象或控制对象的创建过程。
12-2、__init_subclass__:在子类被创建时调用,允许在子类中执行一些额外的操作。
13、类型检查和转换
13-1、__instancecheck__和__subclasscheck__:用于自定义isinstance()和issubclass()函数的行为。
14、自定义异常
14-1、你可以通过继承内置的Exception类来创建自定义的异常类,并定义其特定的行为。
![]()
三、学习方法
要学好Python的魔法方法,你可以遵循以下方法及步骤:
1、理解基础
首先确保你对Python的基本语法、数据类型、类和对象等概念有深入的理解,这些是理解魔法方法的基础。
2、查阅文档
仔细阅读Python官方文档中关于魔法方法的部分,文档会详细解释每个魔法方法的作用、参数和返回值。你可以通过访问Python的官方网站或使用help()函数在Python解释器中查看文档。
3、编写示例
为每个魔法方法编写简单的示例代码,以便更好地理解其用法和效果,通过实际编写和运行代码,你可以更直观地感受到魔法方法如何改变对象的行为。
4、实践应用
在实际项目中尝试使用魔法方法。如,你可以创建一个自定义的集合类,使用__getitem__、__setitem__和__delitem__方法来实现索引操作。只有通过实践应用,你才能更深入地理解魔法方法的用途和重要性。
5、阅读他人代码
阅读开源项目或他人编写的代码,特别是那些使用了魔法方法的代码,这可以帮助你学习如何在实际项目中使用魔法方法。通过分析他人代码中的魔法方法使用方式,你可以学习到一些新的技巧和最佳实践。
6、参加社区讨论
参与Python社区的讨论,与其他开发者交流关于魔法方法的使用经验和技巧,在社区中提问或回答关于魔法方法的问题,这可以帮助你更深入地理解魔法方法并发现新的应用场景。
7、持续学习
Python语言和其生态系统不断发展,新的魔法方法和功能可能会不断被引入,保持对Python社区的关注,及时学习新的魔法方法和最佳实践。
8、练习与总结
多做练习,通过编写各种使用魔法方法的代码来巩固你的理解,定期总结你学到的知识和经验,形成自己的知识体系。
9、注意兼容性
在使用魔法方法时,要注意不同Python版本之间的兼容性差异,确保你的代码在不同版本的Python中都能正常工作。
10、避免过度使用
虽然魔法方法非常强大,但过度使用可能会导致代码难以理解和维护,在编写代码时,要权衡使用魔法方法的利弊,避免滥用。
总之,学好Python的魔法方法需要不断地学习、实践和总结,只有通过不断地练习和积累经验,你才能更好地掌握这些强大的工具,并在实际项目中灵活运用它们。
![]()
四、魔法方法
71、__str__方法
71-1、语法
- __str__(self, /)
- Return str(self)
71-2、参数
71-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
71-2-2、 /(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
71-3、功能
用于定义当对象被转换为字符串时应该返回什么样的值。
71-4、返回值
返回一个字符串,其代表了对象的“官方”字符串表示。
71-5、说明
无
71-6、用法
- # 071、__str__方法:
- # 1、基本字符串表示
- class Person:
- def __init__(self, name, age):
- self.name = name
- self.age = age
- def __str__(self):
- return f"Person(name={self.name}, age={self.age})"
- if __name__ == '__main__':
- p = Person('Myelsa', 18)
- print(p) # 输出:Person(name=Myelsa, age=18)
-
- # 2、格式化输出
- class Circle:
- def __init__(self, radius):
- self.radius = radius
- def __str__(self):
- return f"Circle with radius {self.radius} has area {self.area()}"
- def area(self):
- import math
- return math.pi * (self.radius ** 2)
- if __name__ == '__main__':
- c = Circle(5)
- print(c) # 输出:Circle with radius 5 has area 78.53981633974483
-
- # 3、列表元素的字符串表示
- class Item:
- def __init__(self, id, name):
- self.id = id
- self.name = name
- def __str__(self):
- return f"Item {self.id}: {self.name}"
- if __name__ == '__main__':
- items = [Item(1, "Apple"), Item(2, "Banana")]
- print(items)
-
- # 4、字典值的字符串表示
- class User:
- def __init__(self, username, email):
- self.username = username
- self.email = email
- def __str__(self):
- return f"User({self.username}, {self.email})"
- if __name__ == '__main__':
- print(users)
-
- # 5、日期和时间
- from datetime import datetime
- class Event:
- def __init__(self, name, time):
- self.name = name
- self.time = time
- def __str__(self):
- return f"{self.name} happens at {self.time.strftime('%Y-%m-%d %H:%M:%S')}"
- if __name__ == '__main__':
- event = Event("Meeting", datetime.now())
- print(event) # 输出:类似于Meeting happens at 2024-06-08 09:41:58
-
- # 6、文件路径
- import os
- class FilePath:
- def __init__(self, path):
- self.path = path
- def __str__(self):
- return self.path.replace(os.sep, '/') # 标准化路径分隔符
- if __name__ == '__main__':
- path = FilePath("/test.txt")
- print(path)
-
- # 7、分数
- from fractions import Fraction
- class Score:
- def __init__(self, numerator, denominator):
- self.fraction = Fraction(numerator, denominator)
- def __str__(self):
- return str(self.fraction)
- if __name__ == '__main__':
- score = Score(2, 3)
- print(score)
-
- # 8、复数
- class ComplexNumber:
- def __init__(self, real, imag):
- self.real = real
- self.imag = imag
- def __str__(self):
- return f"{self.real} + {self.imag}j"
- if __name__ == '__main__':
- c = ComplexNumber(3, 4)
- print(c)
-
- # 9、自定义列表
- class MyList:
- def __init__(self, *args):
- self.items = list(args)
- def __str__(self):
- return "[" + ", ".join(map(str, self.items)) + "]"
- if __name__ == '__main__':
- lst = MyList(3, 5, 6, 8, 10, 11, 24)
- print(lst) # 输出:[3, 5, 6, 8, 10, 11, 24]
-
- # 10、二维坐标点
- class Point2D:
- def __init__(self, x, y):
- self.x = x
- self.y = y
- def __str__(self):
- return f"({self.x}, {self.y})"
- if __name__ == '__main__':
- p = Point2D(1, 2)
- print(p) # 输出:(1, 2)
-
- # 11、矩形类
- class Rectangle:
- def __init__(self, width, height):
- self.width = width
- self.height = height
- def __str__(self):
- return f"Rectangle({self.width} wide x {self.height} tall)"
- if __name__ == '__main__':
- rect = Rectangle(10, 24)
- print(rect) # 输出:Rectangle(10 wide x 24 tall)
-
- # 12、学生信息
- class Student:
- def __init__(self, id, name, grade):
- self.id = id
- self.name = name
- self.grade = grade
- def __str__(self):
- return f"Student {self.id}: {self.name} - Grade {self.grade}"
- if __name__ == '__main__':
- student = Student(123, "John Doe", "A")
- print(student) # 输出:Student 123: John Doe - Grade A
-
- # 13、图书信息
- class Book:
- def __init__(self, title, author, isbn):
- self.title = title
- self.author = author
- self.isbn = isbn
- def __str__(self):
- return f"{self.title} by {self.author} (ISBN: {self.isbn})"
- if __name__ == '__main__':
- book = Book("The Great Gatsby", "F. Scott Fitzgerald", "1234567890")
- print(book) # 输出:The Great Gatsby by F. Scott Fitzgerald (ISBN: 1234567890)
-
- # 14、电子邮件消息
- class EmailMessage:
- def __init__(self, sender, recipients, subject, body):
- self.sender = sender
- self.recipients = recipients
- self.subject = subject
- self.body = body
- def __str__(self):
- return f"From: {self.sender}\nTo: {', '.join(self.recipients)}\nSubject: {self.subject}\n\n{self.body}"
- if __name__ == '__main__':
- print(email)
- # From: [email protected]
- # To: [email protected]
- # Subject: Hello
- #
- # How are you?
-
- # 15、订单详情
- class Order:
- def __init__(self, order_id, products, total_price):
- self.order_id = order_id
- self.products = products
- self.total_price = total_price
- def __str__(self):
- return f"Order {self.order_id}: {', '.join(self.products)} - Total: ${self.total_price}"
- if __name__ == '__main__':
- order = Order(1001, ["Product A", "Product B"], 100.0)
- print(order) # 输出:Order 1001: Product A, Product B - Total: $100.0
-
- # 16、车辆信息
- class Car:
- def __init__(self, make, model, year):
- self.make = make
- self.model = model
- self.year = year
- def __str__(self):
- return f"{self.year} {self.make} {self.model}"
- if __name__ == '__main__':
- car = Car("Toyota", "Corolla", 2023)
- print(car) # 输出:2023 Toyota Corolla
-
- # 17、动物信息
- class Animal:
- def __init__(self, name, species, age):
- self.name = name
- self.species = species
- self.age = age
- def __str__(self):
- return f"{self.name} is a {self.species} and is {self.age} years old."
- if __name__ == '__main__':
- animal = Animal("Buddy", "Dog", 5)
- print(animal) # 输出:Buddy is a Dog and is 5 years old.
-
- # 18、综合案例
- class Employee:
- def __init__(self, name, position, salary):
- self.name = name
- self.position = position
- self.salary = salary
- self.bonus = 0 # 初始时没有年终奖
- def adjust_salary(self, percentage):
- """调整薪资,增加或减少指定百分比"""
- self.salary *= (1 + percentage / 100)
- def calculate_bonus(self, percentage):
- """计算年终奖"""
- self.bonus = self.salary * (percentage / 100)
- def __str__(self):
- """返回员工的字符串表示"""
- return f"Employee: {self.name}\n" \
- f"Position: {self.position}\n" \
- f"Salary: ${self.salary:.2f}\n" \
- f"Bonus: ${self.bonus:.2f}"
- if __name__ == '__main__':
- employee = Employee("Myelsa", "Software Engineer", 50000)
- employee.calculate_bonus(10) # 假设年终奖是薪资的10%
- print(employee)
- # Employee: Myelsa
- # Position: Software Engineer
- # Salary: $50000.00
- # Bonus: $5000.00
72、__sub__方法:
72-1、语法
- __sub__(self, other, /)
- Return self - other
72-2、参数
72-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
72-2-2、other(必须):表示与self进行减法操作的另一个对象。
72-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
72-3、功能
用于实现自定义类型的减法操作。
72-4、返回值
返回值通常是减法操作的结果。
72-5、说明
返回值可以是任何类型,但最好是与操作数(即self和other)具有相似性质的类型,以保持类型的一致性。
72-5-1、对于数字类型,返回值通常是一个数字。
72-5-2、对于向量类型,返回值通常是一个新的向量实例,表示两个向量的差。
72-5-3、对于集合或列表类型,返回值通常是一个新的集合或列表实例,表示差集。
72-6、用法
- # 072、__sub__方法:
- # 1、简单的数值相减
- class Number:
- def __init__(self, value):
- self.value = value
- def __sub__(self, other):
- if isinstance(other, Number):
- return Number(self.value - other.value)
- else:
- raise TypeError("Unsupported operand type")
- def __str__(self):
- return str(self.value)
- if __name__ == '__main__':
- a = Number(10)
- b = Number(5)
- c = a - b
- print(c) # 输出:5
-
- # 2、二维向量相减
- class Vector2D:
- def __init__(self, x, y):
- self.x = x
- self.y = y
- def __sub__(self, other):
- return Vector2D(self.x - other.x, self.y - other.y)
- def __str__(self):
- return f"({self.x}, {self.y})"
- if __name__ == '__main__':
- v1 = Vector2D(3, 6)
- v2 = Vector2D(5, 11)
- v3 = v1 - v2
- print(v3) # 输出:(-2, -5)
-
- # 3、时间差计算
- from datetime import datetime, timedelta
- class MyDateTime:
- def __init__(self, year, month, day):
- self.datetime = datetime(year, month, day)
- def __sub__(self, other):
- if isinstance(other, MyDateTime):
- delta = self.datetime - other.datetime
- return delta.days
- else:
- raise TypeError("Unsupported operand type")
- if __name__ == '__main__':
- date1 = MyDateTime(2019, 3, 13)
- date2 = MyDateTime(2024, 6, 8)
- days_difference = date2 - date1
- print(days_difference) # 输出:1914
-
- # 4、复数相减
- class ComplexNumber:
- def __init__(self, real, imag):
- self.real = real
- self.imag = imag
- def __sub__(self, other):
- return ComplexNumber(self.real - other.real, self.imag - other.imag)
- def __str__(self):
- return f"{self.real} + {self.imag}i"
- if __name__ == '__main__':
- c1 = ComplexNumber(3, 6)
- c2 = ComplexNumber(10, 24)
- c3 = c1 - c2
- print(c3) # 输出:-7 + -18i
-
- # 5、矩阵相减
- class Matrix:
- def __init__(self, data):
- self.data = data
- def __sub__(self, other):
- result = []
- for i in range(len(self.data)):
- row = []
- for j in range(len(self.data[0])):
- row.append(self.data[i][j] - other.data[i][j])
- result.append(row)
- return Matrix(result)
- def __str__(self):
- return str(self.data)
- if __name__ == '__main__':
- m1 = Matrix([[1, 2], [3, 4]])
- m2 = Matrix([[5, 6], [7, 8]])
- m3 = m1 - m2
- print(m3) # 输出: [[-4, -4], [-4, -4]]
-
- # 6、温度差计算
- class Temperature:
- def __init__(self, celsius):
- self.celsius = celsius
- def __sub__(self, other):
- return self.celsius - other.celsius
- if __name__ == '__main__':
- t1 = Temperature(25)
- t2 = Temperature(10)
- diff = t1 - t2
- print(diff) # 输出: 15
-
- # 7、货币值相减
- class Money:
- def __init__(self, amount, currency):
- self.amount = amount
- self.currency = currency
- def __sub__(self, other):
- if self.currency != other.currency:
- raise ValueError("Currencies must be the same")
- return Money(self.amount - other.amount, self.currency)
- if __name__ == '__main__':
- m1 = Money(100, "USD")
- m2 = Money(50, "USD")
- m3 = m1 - m2
- print(m3.amount, m3.currency) # 输出: 50 USD
-
- # 8、物理向量力的合成与分解(简化版)
- import math
- class ForceVector:
- def __init__(self, magnitude, angle):
- self.magnitude = magnitude
- self.angle = angle # 假设是相对于x轴的角度
- def __sub__(self, other):
- # 这里简化处理,只考虑x轴分量
- x1 = self.magnitude * math.cos(math.radians(self.angle))
- x2 = other.magnitude * math.cos(math.radians(other.angle))
- return ForceVector(x1 - x2, 0) # 假设只考虑x轴分量
- if __name__ == '__main__':
- f1 = ForceVector(10, 45)
- f2 = ForceVector(5, 0)
- f3 = f1 - f2
- print(f3)
- # f3现在是沿着x轴的简化后的合力,需要进一步的数学计算来处理角度和完整的二维或三维向量
-
- # 9、图像坐标点的差值
- class Point:
- def __init__(self, x, y):
- self.x = x
- self.y = y
- def __sub__(self, other):
- return Point(self.x - other.x, self.y - other.y)
- if __name__ == '__main__':
- p1 = Point(10, 20)
- p2 = Point(5, 10)
- p_diff = p1 - p2
- print(p_diff.x, p_diff.y) # 输出: 5 10
-
- # 10、电信号强度的差值计算
- class SignalStrength:
- def __init__(self, value):
- self.value = value
- def __sub__(self, other):
- return self.value - other.value
- if __name__ == '__main__':
- s1 = SignalStrength(100)
- s2 = SignalStrength(80)
- diff = s1 - s2
- print(diff) # 输出: 20
-
- # 11、综合案例
- class Account:
- def __init__(self, account_number, balance):
- self.account_number = account_number
- self.balance = balance
- def deposit(self, amount):
- self.balance += amount
- def withdraw(self, amount):
- if amount > self.balance:
- raise ValueError("Insufficient funds")
- self.balance -= amount
- def __sub__(self, other):
- # 假设这是一个转账操作,从self账户向other账户转账
- # 转账金额是self账户的当前余额
- if self.balance < 0:
- raise ValueError("Source account has negative balance")
- if not isinstance(other, Account):
- raise TypeError("Unsupported operand type")
- # 转账操作
- transfer_amount = self.balance
- self.withdraw(transfer_amount) # 从self账户扣款
- other.deposit(transfer_amount) # 向other账户存款
- # 返回一个包含两个账户新余额的元组
- return (self.balance, other.balance)
- def __str__(self):
- return f"Account {self.account_number} with balance {self.balance}"
- if __name__ == '__main__':
- account_a = Account("123456", 1000)
- account_b = Account("789012", 500)
- # 转账操作
- transfer_result = account_a - account_b
- print(f"After transfer, account_a: {account_a}, account_b: {account_b}")
- print(f"Transfer result: {transfer_result}")
- # After transfer, account_a: Account 123456 with balance 0, account_b: Account 789012 with balance 1500
- # Transfer result: (0, 1500)
73、__subclasscheck__方法:
73-1、语法
- __subclasscheck__(self, subclass, /)
- Check if a class is a subclass
73-2、参数
73-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
73-2-2、subclass(必须):表示你正在检查是否为self子类的类。
73-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
73-3、功能
用于在Python的issubclass()函数中检查一个类是否是另一个类的子类。
73-4、返回值
返回一个布尔值。
73-5、说明
73-5-1、如果subclass被视为self的子类(根据自定义逻辑),则该方法应返回True。
73-5-2、如果subclass不被视为self的子类,则该方法应返回False。
73-6、用法
- # 073、__subclasscheck__方法:
- # 1、简单的子类检查
- class Meta(type):
- def __subclasscheck__(cls, subclass):
- print(f"Checking if {subclass.__name__} is a subclass of {cls.__name__}")
- return super().__subclasscheck__(subclass)
- class Base(metaclass=Meta):
- pass
- class Derived(Base):
- pass
- print(issubclass(Derived, Base)) # 输出: Checking if Derived is a subclass of Base
-
- # 2、基于注解的子类检查
- class Meta(type):
- def __subclasscheck__(cls, subclass):
- if hasattr(subclass, 'annotation') and subclass.annotation == 'special':
- return True
- return super().__subclasscheck__(subclass)
- class Base(metaclass=Meta):
- pass
- class Derived(Base):
- annotation = 'special'
- class AnotherDerived(Base):
- pass
- print(issubclass(Derived, Base)) # 输出: True
- print(issubclass(AnotherDerived, Base)) # 输出: True
-
- # 3、自定义继承关系
- class Meta(type):
- def __subclasscheck__(cls, subclass):
- if subclass.__name__ == 'SpecialDerived':
- return True
- return super().__subclasscheck__(subclass)
- class Base(metaclass=Meta):
- pass
- class SpecialDerived:
- pass
- class NormalDerived(Base):
- pass
- print(issubclass(SpecialDerived, Base)) # 输出: True,尽管SpecialDerived不是Base的直接子类
- print(issubclass(NormalDerived, Base)) # 输出: True
-
- # 4、基于元类的抽象基类检查
- from abc import ABCMeta, abstractmethod
- class AbstractMeta(ABCMeta):
- def __subclasscheck__(cls, subclass):
- if not super().__subclasscheck__(subclass):
- return False
- for method in cls.__abstractmethods__:
- if not any(method in base.__dict__ for base in subclass.__mro__):
- return False
- return True
- class AbstractBase(metaclass=AbstractMeta):
- @abstractmethod
- def required_method(self):
- pass
- class GoodDerived(AbstractBase):
- def required_method(self):
- pass
- class BadDerived(AbstractBase):
- pass
- print(issubclass(GoodDerived, AbstractBase)) # 输出: True
- print(issubclass(BadDerived, AbstractBase)) # 输出: True
-
- # 5、标记子类为“已注册”
- class RegistryMeta(type):
- registry = []
- def __subclasscheck__(cls, subclass):
- if super().__subclasscheck__(subclass):
- if subclass not in cls.registry:
- cls.registry.append(subclass)
- print(f"Registered {subclass.__name__}")
- return True
- class Base(metaclass=RegistryMeta):
- pass
- class Derived1(Base):
- pass
- class Derived2(Base):
- pass
- print(Base.registry)
评论记录:
回复评论: