当前位置: 首页 > 面试题库 >

在Python中,什么时候应该使用函数而不是方法?

宗意蕴
2023-03-14
问题内容

Python的Zen指出,只有一种方法可以做事情-但我经常遇到决定何时使用函数以及何时使用方法的问题。

让我们举一个简单的例子-
ChessBoard对象。假设我们需要某种方式使董事会上所有合法的King举动均可用。我们写ChessBoard.get_king_moves()还是get_king_moves(chess_board)?

我得到的答案基本上没有定论:

为什么Python使用方法来实现某些功能(例如list.index()),却使用其他方法的功能(例如len(list))?

主要原因是历史。函数用于那些对一组类型通用的操作,即使对于根本没有方法的对象(例如,元组),这些操作也可以使用。使用Python的功能部件(map(),apply()等)时,具有可以轻松应用于对象的不定形集合的函数也很方便。

实际上,将len(),max(),min()实现为内置函数实际上比将它们实现为每种类型的方法要少。人们可能会质疑个别情况,但这是Python的一部分,现在进行这样的基本更改为时已晚。必须保留功能以避免大量代码损坏。

有趣的是,以上内容并未真正说明采用哪种策略。

这是原因之一-
使用自定义方法,开发人员可以自由选择其他方法名称,例如getLength(),length(),getlength()或其他名称。Python强制执行严格的命名,以便可以使用通用函数len()。

稍微有趣一点。我认为函数在某种意义上是接口的Pythonic版本。

最后,来自Guido本人:

谈论能力/接口使我想到了一些“流氓”特殊方法名称。
它在《语言参考》中说:“类可以通过定义具有特殊名称的方法来实现某些由特殊语法调用的操作(例如算术运算或下标和切片)。”
但是,所有这些方法都有特殊的名称,例如__len____unicode__,它们似乎是为内置函数的目的提供的,而不是为了支持语法。大概在基于接口的Python中,这些方法将在ABC上变成常规命名的方法,因此
__len__将成为

class container:

  ...

  def len(self):

    raise NotImplemented

虽然,再想一想,我不明白为什么 所有的 句法运算都不会仅仅在特定的ABC上调用适当的通常命名的方法。 “ <”举例来说,大概会调用“
object.lessthan”(或者是“
comparable.lessthan“)。因此,另一个好处是能够使Python摆脱这种乱七八糟的名字的怪异,在我看来这似乎是对HCI的改进

嗯 我不确定我是否同意(图:-)。

我首先要解释“ Python基本原理”的两个方面。

首先,出于HCI的原因,我选择了len(x)而不是x.len()(def __len__()后来出现了)。实际上,HCI有两个相互交织的原因:

(a)对于某些运算,前缀表示法比后缀读得更好-
前缀(和infix!)运算符在数学中具有悠久的传统,喜欢在视觉上帮助数学家思考问题的表示法。比较与我们改写像公式简单x*(a+b)x*a + x*b使用原始OO符号做同样的事情的笨拙。

(b)当我读到说的代码时,len(x)知道
那是在问某物的长度。这告诉我两件事:结果是整数,参数是某种容器。相反,当我阅读时x.len(),我必须已经知道这x是一种实现接口或从具有standard的类继承的容器len()。当未实现映射的类具有get()keys()
方法,或者不是文件的某些具有方法时,我们有时会感到困惑write()

用另一种方式说同样的事情,我将’len’视为内置 操作 。我不想失去那个。我不能肯定地说出您的意思,但是“ def
len(self):…”听起来确实像是您想将其降级为普通方法。我对此坚决为-1。

我答应解释的Python基本原理的第二点是为什么我选择了特殊的外观__special__而不是仅仅
选择外观的原因special。我期待类可能要覆盖的许多操作,一些标准(例如__add____getitem__),某些不那么标准(例如,泡菜__reduce__很长一段时间完全不支持C代码)。我不希望这些特殊操作使用普通的方法名称,因为那样的话,预先存在的类或用户没有为所有特殊方法存储百科全书的用户编写的类可能会意外地定义它们本不打算实现的操作,可能会造成灾难性的后果。伊万·科斯蒂奇(IvanKrstić)在他的信息中对此进行了更为简洁的解释,在我将所有内容写完之后,这些信息才得以体现。

--Guido van Rossum(主页:http
://www.python.org/~guido/ )

我对此的理解是,在某些情况下,前缀表示法更有意义(即,从语言的角度来看,Duck.quack比quack(Duck)更有意义。)而且,该函数还允许使用“接口”。

在这种情况下,我的猜测是仅基于Guido的第一点实现get_king_moves。但这仍然存在很多悬而未决的问题,例如,使用类似的push和pop方法实现堆栈和队列类-
它们应该是函数还是方法?(在这里我会猜测函数,因为我真的很想发信号通知push-pop接口)

TLDR:有人可以解释决定何时使用函数还是方法的策略是什么?


问题答案:

我的一般规则是- 是在对象上执行操作还是由对象执行操作?

如果是由对象完成的,则应该是成员操作。如果它也可以应用于其他事物,或者由对象的其他事物完成,那么它应该是一个函数(或其他事物的成员)。

引入编程时,传统上(尽管实现不正确)以现实世界中的对象(例如汽车)来描述对象。您提到一只鸭子,让我们开始吧。

class duck: 
    def __init__(self):pass
    def eat(self, o): pass 
    def crap(self) : pass
    def die(self)
    ....

在“对象是真实的事物”类比的上下文中,为对象可以执行的任何操作添加类方法是“正确的”。所以说我想杀死一只鸭子,是否要在鸭子上添加.kill()?不,据我所知,动物不会自杀。因此,如果我想杀死一只鸭子,我应该这样做:

def kill(o):
    if isinstance(o, duck):
        o.die()
    elif isinstance(o, dog):
        print "WHY????"
        o.die()
    elif isinstance(o, nyancat):
        raise Exception("NYAN "*9001)
    else:
       print "can't kill it."

远离这种类比,为什么我们要使用方法和类?因为我们要包含数据并希望以某种方式构造我们的代码,以便将来可以重用和扩展它。这使我们想到了面向对象设计非常重要的封装概念。

封装原理实际上就是它的含义:作为设计人员,您应该隐藏有关实现和类内部的所有内容,对于任何用户或其他开发人员而言,都不一定要访问它。因为我们处理类的实例,所以这简化为“什么操作对于
该实例 至关重要”。如果操作不是实例特定的,则它不应是成员函数。

TL; DR :@Bryan说了什么。如果它在实例上运行并且需要访问类实例内部的数据,则它应该是成员函数。



 类似资料:
  • 问题内容: 我是一名C ++程序员,偶尔使用MySQL处理数据库,但是我的SQL知识非常有限。但是,我当然愿意改变这一点。 目前,我正尝试仅通过SQL查询对数据库中的数据进行分析(!)。但是我将放弃,而是将数据导入C 并使用C 代码进行分析。 我已经与同事讨论了这一点,他们也促使我使用C ++,他说SQL并不是用于复杂的分析,而是主要用于导入(从现有表中)和导出(到新表中)数据,还有更多内容。例如

  • 问题内容: 我已经用python编程了大约两年了。主要是数据资料(熊猫,mpl,numpy),还有自动化脚本和小型Web应用程序。我试图成为一个更好的程序员,并增加我的python知识,而困扰我的一件事是我从未使用过一个类(除了为小型Web应用程序复制随机烧瓶代码外)。我通常理解它们是什么,但是我似乎无法为为什么在一个简单的函数中需要它们的问题而wrap之以鼻。 为了使我的问题更具针对性:我编写了

  • 通过和,我们将获得两种非常相似的在ES6中编写函数的方法。在其他语言中,lambda函数通常以匿名的方式区别于其他语言,但在ECMAScript中,任何函数都可以是匿名的。这两种类型都有独特的使用域(即当需要显式绑定或显式不绑定时)。在这两个领域之间,有很多情况下,任何一种表示法都可以。 ES6中的箭头函数至少有两个限制: null null

  • 问题内容: 我知道他们两个都禁用了Nagle的算法。 我什么时候应该/不应该使用它们中的每一个? 问题答案: 首先,不是所有人都禁用Nagle的算法。 Nagle的算法用于减少有线中更多的小型网络数据包。该算法是:如果数据小于限制(通常是MSS),请等待直到收到先前发送的数据包的ACK,同时累积用户的数据。然后发送累积的数据。 这将对telnet等应用程序有所帮​​助。但是,在发送流数据时,等待A

  • 问题内容: 在该类中,有两个字符串,和。 有什么不同?我什么时候应该使用另一个? 问题答案: 如果你的意思是和则: 用于在文件路径列表中分隔各个文件路径。考虑在上的环境变量。您使用a分隔文件路径,因此在上将是;。 是或用于拆分到特定文件的路径。例如在上,或

  • 问题内容: 编码要发送到Web服务器的查询字符串时-您何时使用以及何时使用或: 使用转义: 要么 使用encodeURI()/ encodeURIComponent() 问题答案: escape() 不要使用它! 在B.2.1.2节中定义了转义,并且附件B的引言中指出: …本附件中指定的所有语言功能和行为均具有一个或多个不良特征,在没有遗留用法的情况下,将从本规范中删除。… …程序员不应该使用或编