社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 魔法方法:用 Dunder 函数编写更简洁的代码

IT服务圈儿 • 2 月前 • 84 次点击  
来源丨经授权转自 数据STUDIO

作者丨云朵君

了解 Python 的 dunder 方法(__str__ __add____eq__等)可以深入了解如何让你的代码更简洁、更直观、更强大。

你是否曾在 Python 中打印过一个对象并得到类似这样的结果?

是的……非常有帮助,对吧?

我还记得第一次遇到这种情况的情形。我只是想查看对象的内容,Python 却给我一个神秘的内存地址。我完全不知道发生了什么。结果发现,Python 并没有想象中的那么难——只是我没有学会让对象如何正确地表示自己。

其实这就是dunder 方法发挥作用的地方。

这些双下划线函数(__str____repr____add__等等)可以让你自定义对象的行为。你能够利用他们接入 Python 内置行为并根据 自己的需求进行调整的方式。

如果你到现在还没有使用他们,那么相比你写Python代码还是比较困难的。下面就跟云朵君一起学习下dunder 方法吧~

Dunder 方法到底是什么?

Dunder(“双下划线”的缩写)方法是内置的 Python 函数,可让你控制对象的行为方式

  • 想要让你的对象像普通字符串一样打印吗?使用__str__
  • 需要比较类的两个实例吗?使用__eq__
  • 想要你的对象与能够使用+么?使用__add__

这些方法与 Python 内置类型的底层方法相同。你只需要在自己的类中重写它们即可。

丑陋的打印输出(__str____repr__

这是一个简单的类:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

现在,尝试打印一个实例:

b = Book("1984""George Orwell")
print(b)

你得到了什么?

这看起来是不太有用。

重写__str____repr__

我喜欢将这两种方法视为一种用于与人类交互,一种用于调试

  • __str__→ 我想要打印什么?
  • __repr__→ 这在调试控制台中应该看起来是什么样子?

以下是我通常实现它们的方式:




    
class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        return f'"{self.title}" by {self.author}'

    def __repr__(self):
        return f'Book(title="{self.title}", author="{self.author}")'

现在,检查一下:

b = Book("1984""George Orwell")
print(b)
"1984" by George Orwell

好多了。

使对象像数字一样(__add____sub__等)

我第一次学习这个的时候就被惊到了。居然可以让自定义对象像数字一样处理,用于加减乘除!

假设我正在创建一个Wallet类。我希望它能实现以下功能:

wallet1 = Wallet( 50 ) 
wallet2 = Wallet( 30 ) 
total = wallet1 + wallet2   # 应该添加余额

这是因为 ,除非我告诉 Python 遇到 +时,我的类要做什么,要不然它根本不知道。

重写__add__

class Wallet:
    def __init__(self, balance):
        self.balance = balance

    def __add__(self, other):
        if isinstance(other, Wallet):
            return Wallet(self.balance + other.balance)
        return NotImplemented

现在:

w1 = Wallet(50)
w2 = Wallet(30)
w3 = w1 + w2
print(w3.balance)  # 80

它确实有效。

而且它并不仅限于加法 — — 你可以对减法 ( __sub__)、乘法 ( __mul__) 甚至比较 ( __lt__ __gt__) 执行相同的操作。

比较对象(__eq____gt____lt__

这是我早期遇到的另一个问题。

假设我有两个具有相同余额的钱包实例:

w1 = Wallet(50)
w2 = Wallet(50)
print(w1 == w2)

你会认为 Python 会说True,对吗?

不。它返回False

这是因为,默认情况下,Python 不会比较对象内容——它会检查它们是否是内存中完全相同的对象

重写 __eq__

class Wallet:
    def __init__(self, balance):
        self.balance = balance

     def __eq__(self, other):
        return self.balance == other.balance if isinstance(other, Wallet) else False

现在:

w1 = Wallet(50)
w2 = Wallet(50)
print(w1 == w2)  # True

好多了。

使对象可迭代(__iter____next__

我记得在尝试构建交易历史跟踪器时,我曾为此苦苦挣扎。我想像这样循环遍历我的交易:

class TransactionHistory:
    def __init__(self, transactions):
        self.transactions = transactions
        self.index = 0

history = TransactionHistory(["Deposit $100""Withdraw $50""Deposit $200"])
for transaction in history:
    print(transaction)

但事实并非如此——Python 没有它:

重写__iter____next__

class TransactionHistory:
    def  __init__(self, transactions):
        self.transactions = transactions
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.transactions):
            raise StopIteration
        transaction = self.transactions[self.index]
        self.index += 1
        return transaction

history = TransactionHistory(["Deposit $100""Withdraw $50""Deposit $200"])
for transaction in history:
    print(transaction)

现在它的行为就像一个列表。

Deposit $100
Withdraw $50
Deposit $200

写在最后

Dunder 方法不仅仅是为了好玩——它们让你的类感觉像是内置的 Python 类型

  • 如果没有__str__,你的对象打印出来就像垃圾一样。
  • 如果没有__eq__,Python 就不知道两个对象何时“相等”。
  • 如果没有 __iter__,循环遍历对象就无法进行。

它们功能强大、易于使用,一旦你开始使用它们,你就会想知道如果没有它们你该如何编码。

1、面试官:HashMap怎么解决哈希冲突?
2、京东面试官揪着问的 InnoDB如何用MVCC和Next-Key Lock实现RR隔离?看完顿悟!
3、第三代 React 来了,怎么玩?
4、系统卡住阻塞了,我们要如何排查呢?
5、彻底理解mmap的本质

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/186309