在Python中,我应该在什么时候使用函数而不是方法? [英] In Python, when should I use a function instead of a method?

查看:139
本文介绍了在Python中,我应该在什么时候使用函数而不是方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The Zen of Python states that there should only be one way to do things- yet frequently I run into the problem of deciding when to use a function versus when to use a method.

让我们看一个简单的例子 - 一个ChessBoard对象。让我们说,我们需要一些方法来获得所有的法律王移动在板上。我们写ChessBoard.get_king_moves()或get_king_moves(chess_board)?

Let's take a trivial example- a ChessBoard object. Let's say we need some way to get all the legal King moves available on the board. Do we write ChessBoard.get_king_moves() or get_king_moves(chess_board)?

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

Here are some related questions I looked at:

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

我得到的答案大部分是不确定的:

The answers I got were largely inconclusive:


方法的一些功能(例如list.index())但函数为其他(例如len(list))?



主要原因是历史。函数用于那些对于一组类型是通用的操作,并且
意在工作,即使对于没有方法在所有
(例如元组)的对象。当你使用
Python的功能特性(map(),apply()等)时,有一个函数可以很容易地将
应用于一个无定形的对象集合。

Why does Python use methods for some functionality (e.g. list.index()) but functions for other (e.g. len(list))?

The major reason is history. Functions were used for those operations that were generic for a group of types and which were intended to work even for objects that didn’t have methods at all (e.g. tuples). It is also convenient to have a function that can readily be applied to an amorphous collection of objects when you use the functional features of Python (map(), apply() et al).

事实上,实现len(),max(),min()作为内置函数实际上比将它们实现为每个类型的方法更少的代码。
一个人可以讨论个别案例,但它是Python的一部分,而
现在太晚了,不能进行这样的根本改变。这些函数有
,以避免大量的代码损坏。

In fact, implementing len(), max(), min() as a built-in function is actually less code than implementing them as methods for each type. One can quibble about individual cases but it’s a part of Python, and it’s too late to make such fundamental changes now. The functions have to remain to avoid massive code breakage.

虽然有趣,


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

This is one of the reasons - with custom methods, developers would be free to choose a different method name, like getLength(), length(), getlength() or whatsoever. Python enforces strict naming so that the common function len() can be used.

稍微有趣。我的看法是,函数在某种意义上,Pythonic版本的接口。

Slightly more interesting. My take is that functions are in a sense, the Pythonic version of interfaces.

最后,来自Guido本人



在谈到能力/接口让我想起一些我们的
流氓特殊方法名称。在语言参考中,它说:A
类可以实现由特殊
语法(如算术运算或下标和切片)调用
定义具有特殊名称的方法的某些操作。 。但是有所有这些方法
有特殊名称像 __ len __ __ unicode __ ,似乎是
提供了对内置函数的好处,而不是为
支持语法。可能在基于接口的Python中,这些
方法将变成ABC上的常规命名方法,因此
__ len __ 将变为

Talking about the Abilities/Interfaces made me think about some of our "rogue" special method names. In the Language Reference, it says, "A class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names." But there are all these methods with special names like __len__ or __unicode__ which seem to be provided for the benefit of built-in functions, rather than for support of syntax. Presumably in an interface-based Python, these methods would turn into regularly-named methods on an ABC, so that __len__ would become

class container:
  ...
  def len(self):
    raise NotImplemented

虽然,再考虑一下,我不明白为什么 all 句法
操作不会在特定的ABC上调用适当的通常命名的方法
。例如,< 可能会调用
object.lessthan comparable.lessthan )。因此,另一个
的好处是能够离开这个
mangled-name oddness,这似乎改善了HCI。

Though, thinking about it some more, I don't see why all syntactic operations wouldn't just invoke the appropriate normally-named method on a specific ABC. "<", for instance, would presumably invoke "object.lessthan" (or perhaps "comparable.lessthan"). So another benefit would be the ability to wean Python away from this mangled-name oddness, which seems to me an HCI improvement.

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

Hm. I'm not sure I agree (figure that :-).

有两个Python原理,我想解释一下

There are two bits of "Python rationale" that I'd like to explain first.

首先,我选择len(x)over x.len()为HCI原因( def
__len __
更晚了)。有两个相互交织的原因,实际上,HCI:

First of all, I chose len(x) over x.len() for HCI reasons (def __len__() came much later). There are two intertwined reasons actually, both HCI:

(a)对于某些操作,前缀符号只是读取比
postfix - 前缀(和中缀! )操作在
数学中有悠久的传统,它喜欢符号,视觉效果帮助
数学家思考问题。比较我们
将如 x *(a + b)的公式重写为 x * a + x * b <

(a) For some operations, prefix notation just reads better than postfix -- prefix (and infix!) operations have a long tradition in mathematics which likes notations where the visuals help the mathematician thinking about a problem. Compare the easy with which we rewrite a formula like x*(a+b) into x*a + x*b to the clumsiness of doing the same thing using a raw OO notation.

(b)当我读取代码时,表示 len(x)知道它要求
的长度。这告诉我两件事:结果是一个
整数,参数是某种容器。相反,当我读取 x.len()时,
,我已经知道 x 是某种类型的
容器实现一个接口或继承一个类
有一个标准 len()。看到我们偶尔会遇到的不是实现映射的
a类有 get() keys()
方法,或不是文件的文件具有 write()方法。

(b) When I read code that says len(x) I know that it is asking for the length of something. This tells me two things: the result is an integer, and the argument is some kind of container. To the contrary, when I read x.len(), I have to already know that x is some kind of container implementing an interface or inheriting from a class that has a standard len(). Witness the confusion we occasionally have when a class that is not implementing a mapping has a get() or keys() method, or something that isn't a file has a write() method.

以另一种方式说同样的事情,我看到'len'作为一个内置的
操作。我不喜欢失去。我不能肯定地说,你是否意味着,但是'def len(self):...肯定听起来像你
想降级为一个普通的方法。我强烈-1。

Saying the same thing in another way, I see 'len' as a built-in operation. I'd hate to lose that. I can't say for sure whether you meant that or not, but 'def len(self): ...' certainly sounds like you want to demote it to an ordinary method. I'm strongly -1 on that.

我承诺解释的第二个Python理由是原因
为什么我选择特殊的方法看起来 __ special __ ,而不仅仅是
特殊。我预计很多操作,类可能希望
覆盖,一些标准(例如 __ add __ __ getitem __ ),一些不是这样的
标准(例如pickle的 __减少__ 很长一段时间没有支持C
的代码)。我不想让这些特殊的操作使用普通的
方法名,因为那时预先存在的类,或者由
用户编写的没有所有特殊方法的百科全书内存的类,
将是容易意外地定义操作,这并不意味着实现
,可能带来灾难性的后果。

The second bit of Python rationale I promised to explain is the reason why I chose special methods to look __special__ and not merely special. I was anticipating lots of operations that classes might want to override, some standard (e.g. __add__ or __getitem__), some not so standard (e.g. pickle's __reduce__ for a long time had no support in C code at all). I didn't want these special operations to use ordinary method names, because then pre-existing classes, or classes written by users without an encyclopedic memory for all the special methods, would be liable to accidentally define operations they didn't mean to implement, with possibly disastrous consequences. Ivan Krstić explained this more concise in his message, which arrived after I'd written all this up.

-
这是一个非常简单的例子。 --Guido van Rossum(主页: http://www.python.org/~guido/

我的理解是,在某些情况下,前缀符号只是更有意义(即,Duck.quack比嘎嘎更有意义从语言的角度看)。再次,函数允许接口。

My understanding of this is that in certain cases, prefix notation just makes more sense (ie, Duck.quack makes more sense than quack(Duck) from a linguistic standpoint.) and again, the functions allow for "interfaces".

在这种情况下,我的猜测是实现get_king_moves仅仅基于Guido的第一点。但是,这仍然有很多开放的问题,比如说,实现一个堆栈和队列类,类似的push和pop方法 - 应该是函数还是方法? (这里我将猜测函数,因为我真的想要发出一个push-pop接口)

In such a case, my guess would be to implement get_king_moves based solely on Guido's first point. But that still leaves a lot of open questions regarding say, implementing a stack and queue class with similar push and pop methods- should they be functions or methods? (here I would guess functions, because I really want to signal a push-pop interface)

TLDR:有人可以解释什么策略决定什么时候使用函数方法应该是?

TLDR: Can someone explain what the strategy for deciding when to use functions vs. methods should be?

推荐答案

我的一般规则是这是是对对象还是对象执行的操作?

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

if it is done by the object, it should be a member operation. If it could apply to other things too, or is done by something else to the object then it should be a function (or perhaps a member of something else).

当引入编程,它是传统的(虽然实现不正确)描述对象的真实世界的对象,如汽车。

When introducing programming, it is traditional (albeit implementation incorrect) to describe objects in terms of real-world objects such as cars. You mention a duck, so let's go with that.

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

在对象是真实的东西的上下文中,类似地,为对象可以做的任何事情添加一个类方法是正确的。所以说,我想杀了一只鸭,我添加一个
.kill()鸭?不......只要我知道动物不自杀。因此,如果我想杀死一只鸭子,我应该这样做:

In the context of the "objects are real things" analogy, it is "correct" to add a class method for anything which the object can do. So say I want to kill off a duck, do I add a .kill() to the duck? No... as far as I know animals do not commit suicide. Therefore if I want to kill a duck I should do this:

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

为了避免这种比喻,我们为什么要使用方法和类?因为我们想要包含数据,希望以一种可以在将来重用和可扩展的方式来构造我们的代码。这使我们的封装的概念,这是OO设计的那么珍贵。

Moving away from this analogy, why do we use methods and classes? Because we want to contain data and hopefully structure our code in a manner such that it will be reusable and extensible in the future. This brings us to the notion of encapsulation which is so dear to OO design.

封装主体是真正的结果:作为一个设计师,你应该隐藏一切关于实现和类的内部,它不是绝对必要的任何用户或其他开发者访问。因为我们处理类的实例,所以减少为此实例上的操作是至关重要的 。如果操作不是特定于实例的,那么它不应该是成员函数。

The encapsulation principal is really what this comes down to: as a designer you should hide everything about the implementation and class internals which it is not absolutely necessarily for any user or other developer to access. Because we deal with instances of classes, this reduces to "what operations are crucial on this instance". If an operation is not instance specific, then it should not be a member function.

TL; DR
什么@Bryan说过。如果它对一个实例操作并且需要访问类实例内部的数据,那么它应该是一个成员函数。

TL;DR: what @Bryan said. If it operates on an instance and needs to access data which is internal to the class instance, it should be a member function.

这篇关于在Python中,我应该在什么时候使用函数而不是方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆