跳至主要内容

Dunder 或魔法方法

Dunder or Magic Methods in Python

Dunder(双下划线)或魔法方法是 Python 中的特殊方法,允许自定义类和对象。这些方法被称为魔法,因为它们可以以意外的方式更改代码的行为。理解和实现这些方法可以极大地增强 Python 程序的功能和灵活性。

构造对象和表达式

在 Python 中,对象是类的实例,这些类定义了对象的属性和方法。在 Python 中创建对象的过程涉及定义一个类,该类指定对象的结构和行为,然后创建该类的实例。

在 Python 中定义类

要在 Python 中定义,请使用 class 关键字,后跟类名。例如,以下代码定义了一个名为 Person 的简单类

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_hello(self):
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")

__init__ 方法是一个特殊方法,当创建类的实例时调用该方法。它初始化对象属性。

Python __init__ 魔法方法

__init__ 方法是一个特殊的魔法方法,当创建类的实例时调用该方法。它初始化对象属性。在上面的示例中,__init__ 方法采用两个参数 nameage,用于初始化对象的 nameage 属性。

在 Python 中创建类的实例

要创建类的实例,请像调用函数一样调用类,传递 __init__ dunder 方法所需的任何参数。例如,以下代码创建了 Person 类的两个实例

# Defining a car class
class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
    
    def describe_car(self):
        print(f"The car is a {self.year} {self.make} {self.model}.")
    
    
# Creating an instance of Car class
car1 = Car("Honda", "Accord", 2021)

# Calling the describe_car method
car1.describe_car()

# Output: The car is a 2021 Honda Accord.
# Defining a book class
class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
    
    def describe_book(self):
        print(f"The book '{self.title}' is written by {self.author} and has {self.pages} pages.")
        

# Creating an instance of Book class
book1 = Book("The Alchemist", "Paulo Coelho", 208)

# Calling the describe_book method
book1.describe_book()

# Output: The book 'The Alchemist' is written by Paulo Coelho and has 208 pages.

创建迭代器对象

迭代器是一个对象,它支持对项目集合进行顺序迭代(循环),一次一个项目。在 Python 中,可以使用类或函数创建迭代器对象。

Python 生成器类

可以使用 Python 中的生成器类创建迭代器。生成器类是一种对象类型,用于使用 yield 语句创建可迭代对象。

class MyGenerator:
    def __init__(self):
        self.num = 0

    def __iter__(self):
        return self
    
    def __next__(self):
        if self.num <= 5:
            value = self.num
            self.num += 1
            return value
        else:
            raise StopIteration
def my_generator():
    num = 0
    while num <= 5:
        yield num
        num += 1

# Using the generator class
gen = MyGenerator()
for x in gen:
    print(x)

# Using the function generator
gen = my_generator()
for x in gen:
    print(x)

在此示例中,MyGenerator 是一个从内置 object 类继承的生成器类。它定义了一个 __init__() 方法,该方法将 num 属性初始化为 0。它还定义了一个 __iter__() 方法,该方法返回迭代器对象(在本例中为 self),以及一个 __next__() 魔法方法,该方法生成序列中的下一个值。

还可以使用Python 函数生成器创建迭代器。函数生成器是包含 yield 语句的函数。

在此示例中,my_generator 函数是一个函数生成器,它使用 yield 语句生成序列中的下一个值。

在以上两个示例中,可以如下创建迭代器对象

迭代时,这两个代码示例都将输出值 012345

处理属性引用

属性引用用于访问 Python 中对象的属性。可以使用点表示法语法访问它们,也可以使用 getattr() 函数动态访问它们。

getattr() 函数接受两个参数 - 需要访问其属性的对象和作为字符串的属性名称。如果找不到该属性,则会引发 AttributeError


class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

my_dog = Dog("Max", "German Shepherd")
print(my_dog.name) ### Output

my_cat = {"name": "Fluffy", "breed": "Persian"}
cat_name = getattr(my_cat, "name")
print(cat_name) ### Output

在第一种情况下,我们创建一个 Dog 类,并使用点表示法语法访问 name 属性。

在第二种情况下,我们创建一个字典对象 my_cat,并使用 getattr() 函数动态访问 name 属性。我们将属性值存储在 cat_name 中并将其打印出来。

使用魔法方法将对象表示为字符串

在 Python 中,我们可以使用 __repr__() dunder 方法将对象表示为字符串。当我们使用 repr() 函数或使用 print() 函数打印对象时,将调用此方法。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __repr__(self):
        return f"Point({self.x}, {self.y})"
        
p = Point(2, 3)
print(p)  ### Output

在上面的代码中,我们定义了一个具有 xy 属性的 Point 类。我们还定义了一个 __repr__() dunder 方法,该方法返回 Point 对象的字符串表示形式。当我们打印 p 对象时,它会调用 __repr__() 魔术方法以获取其字符串表示形式。

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        
    def __repr__(self):
        return f"Car(make={self.make}, model={self.model}, year={self.year})"
        
c = Car("Toyota", "Camry", 2021)
print(c)  ### Output

在此示例中,我们定义了一个具有 makemodelyear 属性的 Car 类。我们还定义了一个 __repr__() 方法,该方法返回 Car 对象的字符串表示形式。当我们打印 c 对象时,它会调用 __repr__() dunder 方法以获取其字符串表示形式。

使用 Dunder 方法清理对象

在 Python 中,当不再需要对象时,会自动对其进行垃圾回收。但是,有时可能需要为对象定义其他清理操作。可以使用 __del__ 方法来实现此目的,该方法在对象即将被销毁时调用。

此 dunder 方法对于释放资源(例如文件、网络连接或其他未由 Python 自动管理的系统级对象)非常有用。

class MyClass:
    def __init__(self):
        self.file = open('example.txt', 'r')

    def __del__(self):
        self.file.close()

在此示例中,MyClass 构造函数创建一个文件对象并将其存储在 file 实例变量中。当对象被销毁时,将调用 __del__ 方法,该方法关闭文件。

使用 Dunder 方法执行比较

Python 提供了多种比较值、变量或表达式的途径。一些用于执行比较的常用运算符包括 ==!=><>=<=inis

Python 比较字符串

__lt__() 方法用于在 Python 中实现小于比较运算符。如果第一个字符串小于第二个字符串,则返回 True,否则返回 False

string1 = "apple"
string2 = "banana"
if string1.__lt__(string2):
    print("string1 is less than string2")
else:
    print("string1 is greater than or equal to string2")

# Output:
#string1 is less than string2
fruits = ["apple", "banana", "orange", "kiwi"]
sorted_fruits = sorted(fruits, key=lambda x: x.__lt__("c"))
print(sorted_fruits)
# Output:

# ['orange', 'kiwi', 'apple', 'banana']

在上面的示例中,我们根据每个字符串的第一个字符是否小于或大于 c,按升序对水果列表进行了排序。lambda x: x.__lt__(c) 返回 True(如果 x 的第一个字符小于 c)或 False(否则)。

与我们一起贡献!

不要犹豫,在 GitHub 上为 Python 教程做出贡献:创建分支、更新内容并发出拉取请求。

Profile picture for user AliaksandrSumich
Python 工程师,第三方 Web 服务集成专家。
更新:2024-05-03 - 21:52
Profile picture for user angarsky
已审阅并批准