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

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

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

这是我看过的一些相关问题:

Why does python use 'magic methods'?
Is there a reason Python strings don't have a string length method?

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

为什么Python为什么将方法用于某些功能(例如list.index()),却对其他功能(例如len(list))使用?

主要原因是历史。函数用于一组类型通用的操作,
甚至可以用于根本没有方法的对象
(例如元组)。具有可以
使用时很容易应用于无定形的对象集合
Python的功能特性(map(),apply()等)。

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

尽管很有趣,但是上面并没有真正说明采用哪种策略。

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

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

最后,from Guido himself:

谈论能力/接口使我想到了一些我们的
“流氓”特殊方法名称。在《语言参考》中,“
类可以实现由特殊调用的某些操作
语法(例如算术运算或下标和切片)
用特殊名称定义方法。”但是所有这些方法
具有特殊名称,例如__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)对于某些操作,前缀符号的读取效果比
postfix-前缀(和infix!)操作在
喜欢表示法在视觉帮助下的数学
数学家思考问题。比较我们的简单性
将像x*(a+b)这样的公式重写为x*a + x*b
使用原始的OO符号做同样的事情。

(b)当我读到写着len(x)的代码时,我知道它正在询问
东西的长度。这告诉我两件事:结果是
整数,参数是某种容器。与此相反的,
当我阅读x.len()时,我必须已经知道x是某种
容器实现接口或从类继承
具有标准的len()。看到我们偶尔会有的困惑
未实现映射的类具有get()keys()
方法,或者不是文件的东西都具有write()方法。

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

我答应解释的Python基本原理的第二点是原因
为什么我选择特殊的方法查看__special__而不是
special。我期待类可能要进行的许多操作
覆盖某些标准(例如__add____getitem__),有些则不是
标准(例如,很长时间,咸菜的__reduce__
代码)。我不希望这些特殊操作使用普通
方法名称,因为然后是预先存在的类或由
对于所有特殊方法都没有百科全书的用户,
可能会意外地定义了他们本不想执行的操作
实施,可能会造成灾难性的后果。伊凡·克斯蒂奇
在他的信息中更简洁地解释了这一点。
全部写下来。

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

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

在这种情况下,我的猜测是仅基于Guido的第一点实现get_king_moves。但这仍然留下很多未解决的问题,例如,使用类似的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:
@布莱恩说了什么。如果它在实例上运行并且需要访问类实例内部的数据,则它应该是成员函数。

Python GPU资源利用 - python

我有一个Python脚本在某些深度学习模型上运行推理。有什么办法可以找出GPU资源的利用率水平?例如,使用着色器,float16乘法器等。我似乎在网上找不到太多有关这些GPU资源的文档。谢谢! 参考方案 您可以尝试在像Renderdoc这样的GPU分析器中运行pyxthon应用程序。它将分析您的跑步情况。您将能够获得有关已使用资源,已用缓冲区,不同渲染状态上…

Python sqlite3数据库已锁定 - python

我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…

Python exchangelib在子文件夹中读取邮件 - python

我想从Outlook邮箱的子文件夹中读取邮件。Inbox ├──myfolder 我可以使用account.inbox.all()阅读收件箱,但我想阅读myfolder中的邮件我尝试了此页面folder部分中的内容,但无法正确完成https://pypi.python.org/pypi/exchangelib/ 参考方案 您需要首先掌握Folder的myfo…

python-docx应该在空单元格已满时返回空单元格 - python

我试图遍历文档中的所有表并从中提取文本。作为中间步骤,我只是尝试将文本打印到控制台。我在类似的帖子中已经看过scanny提供的其他代码,但是由于某种原因,它并没有提供我正在解析的文档的预期输出可以在https://www.ontario.ca/laws/regulation/140300中找到该文档from docx import Document from…

Python ThreadPoolExecutor抑制异常 - python

from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED def div_zero(x): print('In div_zero') return x / 0 with ThreadPoolExecutor(max_workers=4) as execut…