我身边有机器学习和数据科学家,Python是他们的首选语言。然而,并非他们中的每个人都是经验丰富的Python开发人员,他们也不太可能掌握Python必须提供的所有强大功能。这当然可以理解,但同时也很不幸。为什么?因为了解语言的来龙去脉需要编写代码...这就是为什么我想给正在提高Python技能的人一些帮助,这样你就可以写出更棒的代码,也许会给你的小伙伴或同事留下深刻印象,并且玩得更开心!具体来说,在这篇文章中,我想谈谈如何在Python中使用魔术方法编写令人惊叹的类,让我们开始吧。什么是魔法方法魔法方法首先是一个方法,一个属于类的函数。它们可以是实例方法或类方法。您可以轻松识别它们,因为它们都以双下划线开头和结尾,即它们看起来都像__actual_name__。重要的是,魔法方法不能直接调用!当然,您可以这样做并编写类似YourClass().__actual_name__()的内容,但请不要直接调用。那么魔法方法是怎么调用的呢?它们会在适当的时候被调用,例如调用str(YourClass())会调用魔法方法__str__或者YourClass()+YourClass()会调用__add__,如果你已经实现了这两个魔法方法。那么,魔术方法有什么用呢?它们允许我们编写与python内置方法一起工作的类,从而产生更具可读性和更少冗余的代码。为了强调魔术方法的有用性并了解在进行机器学习或数据科学时如何使用它们可以获益,让我们举一个具体的例子。示例:具有自定义范围的datetime类以下代码显示了如何使用魔术方法编写类似于内置范围函数的DateTimeRange类。timedelta(seconds=1)):self._start=startself._end=end_self._step=stepdef__iter__(self)->Iterable[datetime]:point=self._startwhilepointint:returnil((self._end-self._start)/self._step)def__contains__(self,item:datetime)->bool:mod=divmod(item-self._start,self._step)#divmod返回tuple(x//y,x%y).不变:div*y+mod==x.returnitem>=self._startanditem日期时间:n_steps=itemifitem>=0elselen(self)+itemreturn_value=self._start+n_steps*self._stepifreturn_valuenotinself:raiseIndexError()returnreturn_valuedef__str__(self):returnf"DatetimeRange[{self._start},{self._end})withstep{自我。_step}"defmain():my_range=DateTimeRange(datetime(2021,1,1),datetime(2021,12,1),timedelta(days=12))print(my_range)print(f"{len(my_range)==len(list(my_range))=}")print(f"{my_range[-2]inmy_range=}")print(f"{my_range[2]+timedelta(seconds=12)inmy_range=}")forrinmy_range:print(r)#do_something(r)if__name__=='__main__':main()先看运行结果:看到运行结果后,或许能更快的理解DateTimeRange类的功能,有很多代码很多,不过不用担心,我会解释一下,一般来说,上面的代码实现了六个不同的魔术方法:1.__init__方法,你一定知道,这个方法主要用来初始化你的类的实例属性。在这里,我们将作用域的类的开头连同end和step一起传递给DateTimeRange。2、__iter__方法。在for循环或list(DateTimeRange())时调用。这可能是最重要的一个,因为它生成了所有的元素在我们的日期时间范围内。这是一个函数on是一个所谓的生成器函数,它一次创建一个元素,将其交给调用者,并允许调用者对其进行处理。它会这样做,直到它到达范围的末尾。在查看yield关键字时,您可以轻松识别生成器函数。此语句暂停函数以保存其所有状态,然后从那里继续进行后续调用。这允许您一次获取一个元素并使用它,而无需将每个元素都保存在内存中。当范围比较大时,将所有内容都放在内存中会变得非常占用内存。例如,执行list(DateTimeRange(datetime(1900,1,1),datetime(2000,1,1))会将3184617600个日期时间放入内存。太大了,但是,使用生成器,您可以轻松地处理一个元素一个。3.现在你已经看到它不是列表或元组。但是,为了像处理列表或元组一样处理此DateTimeRange类,我添加了其他三个魔术方法,即__len__、__contains__和__getitem__。使用__len__,您可以通过调用len(my_range)找出有多少元素属于您的范围。这会变得很有用,例如,当遍历所有元素并想知道从所有可用元素中处理了多少元素时。它还可能会告诉你,嘿,我有很多数据要处理,请喝杯咖啡。使用__contains__,您可以使用my_range中的内置语法元素检查某个元素是否属于您的范围。给定实现的好处在于,这是使用纯数学完成的,无需将给定元素与范围内的所有元素进行比较。这意味着检查元素是否在您的范围内是一个常量时间操作,不依赖于实际范围实例的大小。同样,这对于我们在处理数据时经常看到的大范围来说非常方便。使用__getitem__您可以使用索引语法从对象中检索项目。因此,我们范围的最后一个元素可以通过my_range[-1]获得。通常,可以使用__getitem__编写非常干净和可读的界面。4、__str__方法的作用是将类的实例转换成字符串。当一个实例被转换为一个字符串时,这个方法会被自动调用,例如__str__在调用print(my_range)或str(my_range)时被调用。最后,本文分享如何通过魔术方法编写一个非常优雅的类。魔术方法可以在Python内置的函数或操作中被自动调用,让我们写出可读性和易用性更好的类,就像这篇文章中的DateTimeRange。本文转载自微信公众号“Python7号”,可通过以下二维码关注。转载本文请联系Python七号公众号。