什么样才算是Pythonic的代码?

本文深入探讨Python中的魔术方法,如__len__、__getitem__、__enter__等,展示如何利用这些方法编写更Pythonic的代码,提升代码的一致性和优雅性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      如果说Python最迷人的特性是什么,我的回答一定是“一致性”。什么是“一致性”?举个例子,Python中“万物皆对象”,没有Java那种“原生类型”和“封装类型”所造成的割裂感。这种一致性体现在了Python的方方面面,是Python的设计哲学。个人认为,能保持这种一致性的代码,就能称为“Pythonic”。那么怎样才能写出这样的代码呢?一个方式就是采用“魔术方法”(magic method)。

      所谓魔术方法,表现形式上,就是以双下划线开头和结尾的方法,最常见的就是__init__() 方法。魔术方法是供解释器调用的,我们一般不应该直接使用魔术方法,也不应该自创一个非标准的魔术方法;也就是说,我们写出的代码中,不应该有类似于ClassA.__foo__() 的使用方式。

     下面我们来看一下有哪些常见的魔术方法,以及如何使用这些方法,使代码更加 “Pythonic”

1. __len__

求一个对象的长度,Python提供了一个内建的方法:len() 。无论是字符串对象,还是list 对象,都可以用len(obj)来获取长度(作为对比,Java中求数组的长度用lengh属性,求String对象的长度则为length()方法,显得很“割裂”)。其实在使用len()时,解释器会调用对象中的__len()__方法。所以为保持一致性,最好也能用len()求自定义对象的长度,我们可以在类的内部,实现__len()__方法。

Class Train:
    def __init__(self):
        self.length = 50
        
    def __len__(self):
        return self.length

我们定义了一个 Trian 类,并实现了__len()__方法,那么就可以用len()来求对象的长度:

>>> train = Train()
>>> print(len(train))
>>> 50

这样的结果是,对于求长度这个操作,自定义的类的行为和内置类型(str, list等)完全一样,使用者不用了解是用train.get_size() 还是 train.length 或者其他什么方法、属性,只需调用len()这个通用的方法。当然,如果你非要实现类似于 get_size() 的方法,也可以获取对象的长度,但远没有使用len()简洁优雅。这就是上文所说的”一致性“。

2. __getitem__ 与 __setitem__

对字典对象的取值或修改操作,都会用到[]符号,这背后实际上就是__getitem____setitem__方法,所以我们可以自行实现这两个方法,以获得和字典对象类似的行为。

class Box:
    def __setitem__(self, key, value):
        setattr(self, key, value)
        
    def __getitem__(self, key):
        try:
            return getattr(self, key)
        except AttributeError:
            raise KeyError(key)
 
 >>> b=Box()
 >>> b["name"]="box"
 >>> b["name"]
 'box'

例子中,Box对象实现了__getitem____setitem__方法,用中括号操作符时,表现行为和内置的字典类型一致。

3. __enter__ 与 __exit__

这一对魔术方法用于实现上下文管理器。比如我们打开一个文件时,为了防止忘记关闭,往往用上下文管理器的方式:

with open("test.txt") as f:
    pass

对于自己创建的对象,可以实现__enter____exit__方法,其中在with语句开始时,__enter__方法被调用,退出with语句块时,__exit__方法被调用。我们来看一个实例,来自Python的requests库源码:

# requests.sessions
class Session(SessionRedirectMixin):
...
    def __enter__(self):
        return self
       
    def __exit__(self,*args):
    	self.close()
...

Session类的__enter__方法返回自身实例,__exit__方法调用关闭方法,确保退出时能正确关闭会话,所以用with语法调用时,无需关注会话关闭的问题。使用方法同open函数:

>>> with Session() as s:
>>>     s.get('https://httpbin.org/get')
4. __call__

在Python中,只要是可调用对象,就能像函数一样调用该对象,其行为跟普通函数没有区别,其实函数本身就是一个可调用对象。如果自己想构建一个可调用对象,实现__call__方法即可。我们看一下来自 Werkzeug 库的 Response 对象:

class Response(object): 
    ...
    def __call__(self, environ, start_response):
        app_iter, status, headers = self.get_wsgi_response(environ)
	start_response(status, headers)
	return app_iter

该类实现了__call__方法,当调用该类的对象时,就像调用一个普通函数,传入__call__方法定义的参数:

>>> response = Response()
# 像函数一样调用 response:
>>> response(environ,start_response)
5. __iter__ 与 __next__

这一对魔术方法与迭代器可迭代对象密切相关。简单来说,实现了__iter__方法的即是可迭代对象,实现了__next__方法的即为迭代器。我们可以利用 for 循环来遍历可迭代对象,在实际使用中更加方便:

>>> for i in MyIterable():
...    print(i)
...

关于迭代器与可迭代对象,可以参考我的另一篇文章Python中的迭代器与可迭代对象,这里不再赘述。

6. 小结

我们在编写代码的时候,应该考虑到语言本身的特性,尽量贴近这种特性。对于Python,我们应该多利用魔术方法,写出更加优雅、一致的代码,提供符合Python习惯的接口。魔术方法还有很多,比如跟运算符相关的就有一大串,本文仅仅是列出了常见的几个,仅供参考。

资源下载链接为: https://pan.quark.cn/s/f989b9092fc5 在 Android 应用开发中,开发一款仿 OPPO 手机计算器的应用是极具实践价值的任务,它融合了 UI 设计、事件处理以及数学逻辑等多方面的技术要点。当前的“最新版仿 OPPO 手机计算器--android.rar”压缩包中,提供了该计算器应用的源代码,这为开发者深入学习 Android 编程提供了宝贵的资源。 UI 设计是构建此类计算器应用的基石。OPPO 手机的计算器界面以清晰的布局和良好的用户交互体验著称,其中包括数字键、运算符键以及用于显示结果的区域等关键元素。开发者需借助 Android Studio 中的 XML 布局文件来定义这些界面元素,可选用 LinearLayout、GridLayout 或 ConstraintLayout 等布局管理器,并搭配 Button 控件来实现各个按键功能。同时,还需考虑不同分辨率屏幕和设备尺寸的适配问题,这通常涉及 Density Independent Pixel(dp)单位的应用以及 Android 尺寸资源的合理配置。 事件处理构成了计算器的核心功能。开发者要在每个按钮的点击事件中编写相应的处理代码,通常通过实现 OnClickListener 接口来完成。例如,当用户点击数字键时,相应的值会被添加到显示区域;点击运算符键时,则会保存当前操作数并设定运算类型。而对于等号(=)按钮,需要执行计算操作,这往往需要借助栈数据结构来存储操作数和运算符,并运用算法解析表达式以完成计算。 数学逻辑的实现则是计算器功能的关键体现。在 Android 应用中,开发者可以利用 Java 内置的 Math 类,或者自行设计算法来完成计算任务。基本的加减乘除运算可通过简单的算术操作实现,而像求幂、开方等复杂运算则需调用 Math 类的相关方法。此外
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值