模块函数 vs 静态方法 vs 类方法 vs 无修饰符: 哪个习惯用法更 Python 化?

我是一名 Java 开发人员,时不时地会玩弄 Python。我最近偶然发现了 这篇文章,它提到了 Java 程序员在学习 Python 时常犯的错误。第一个引起了我的注意:

Java 中的静态方法不能转换为 Python 类方法。当然,它或多或少会产生相同的效果,但是类方法的目标实际上是做一些在 Java 中通常不可能做到的事情(比如继承一个非默认的构造函数)。Java 静态方法的惯用翻译通常是模块级函数,而不是类方法或静态方法。(静态 final 字段应该转换为模块级常量。)

这并不是一个性能问题,但是一个 Python 程序员如果必须使用这样的 Java 习惯用法代码,那么输入 Foo 将会非常恼火。只是 Foo.some 函数的 Foo.some 方法。但是请注意,调用 classmethod 会涉及额外的内存分配,而调用 staticmethod 或函数则不会。

哦,还有那些福。酒吧。Baz 属性链也不是免费的。在 Java 中,这些虚名是由编译器查找的,所以在运行时有多少虚名并不重要。在 Python 中,查找是在运行时进行的,因此每个点都要计数。(请记住,在 Python 中,“扁平比嵌套好”,尽管它更多地与“可读性计数”和“简单比复杂好”相关,而不是与性能相关。)

我发现这有点奇怪,因为 静力学方法的文档说:

Python 中的静态方法类似于 Java 或 C + + 中的静态方法。还可以参见 classmethod ()获得对创建替代类构造函数有用的变量。

更令人费解的是这个代码:

class A:
def foo(x):
print(x)
A.foo(5)

在 Python 2.7.3中预期会失败,但在3.2.3中可以正常工作(尽管不能在 A 的实例上调用该方法,只能在类上调用该方法)

所以有三种实现静态方法的方法(如果算上使用 classmethod 的话,有四种) ,每种方法都有细微的差别,其中一种似乎没有文档记录。这似乎与 Python 的口头禅 应该有一种——最好只有一种——显而易见的方法来做到这一点。的哪个成语是最 Python 的?每种方法的优点和缺点是什么?

以下是我目前了解到的情况:

模组功能:

  • 避免 Foo. Foo.f ()问题
  • 与其他替代方案相比,更容易污染模块的命名空间
  • 没有遗产

静态法:

  • 将与类相关的函数保留在类内部和模块命名空间之外。
  • 允许对类的实例调用函数。
  • 子类可以重写该方法。

类方法:

  • 与 staticmethod 相同,但也将类作为第一个参数传递。

正则方法(仅 Python 3) :

  • 与 staticmethod 相同,但不能对类的实例调用该方法。

我是不是想太多了? 这不是问题吗? 请帮帮我!

30779 次浏览

The most straightforward way to think about it is to think in terms of what type of object the method needs in order to do its work. If your method needs access to an instance, make it a regular method. If it needs access to the class, make it a classmethod. If it doesn't need access to the class or the instance, make it a function. There is rarely a need to make something a staticmethod, but if you find you want a function to be "grouped" with a class (e.g., so it can be overridden) even though it doesn't need access to the class, I guess you could make it a staticmethod.

I would add that putting functions at the module level doesn't "pollute" the namespace. If the functions are meant to be used, they're not polluting the namespace, they're using it just as it should be used. Functions are legitimate objects in a module, just like classes or anything else. There's no reason to hide a function in a class if it doesn't have any reason to be there.

Great answer by BrenBarn, but I would change 'If it doesn't need access to the class or the instance, make it a function' to:

'If it doesn't need access to the class or the instance...but is thematically related to the class (typical example: helper functions and conversion functions used by other class methods or used by alternate constructors), then use staticmethod

else make it a module function

This is not really an answer, but rather a lengthy comment:

Even more puzzling is that this code:

        class A:
def foo(x):
print(x)
A.foo(5)

Fails as expected in Python 2.7.3 but works fine in 3.2.3 (although you can't call the method on an instance of A, only on the class.)

I'll try to explain what happens here.

This is, strictly speaking, an abuse of the "normal" instance method protocol.

What you define here is a method, but with the first (and only) parameter not named self, but x. Of course you can call the method in an instance of A, but you'll have to call it like this:

A().foo()

or

a = A()
a.foo()

so the instance is given to the function as first argument.

The possibility to call regular methods via the class has always been there and works by

a = A()
A.foo(a)

Here, as you call the method of the class rather than on the instance, it doesn't get its first parameter given automaticvally, but you'll have to provide it.

As long as this is an instance of A, everything is ok. Giving it something else is IMO an abuse of the protocol, and thus the difference between Py2 and Py3:

In Py2, A.foo gets transformed to an unbound method and thus requires its first argument be an instance of the class it "lives" in. Calling it with something else will fail.

In Py3, this check has been dropped and A.foo is just the original function object. So you can call it with everything as first argument, but I wouldn't do it. The first parameter of a method should always be named self and have the semantics of self.

The best answer depends on how the function is going to be used. In my case, I write application packages that will be used in Jupyter notebooks. My main goal is to make things easy for the user.

The main advantage of function definitions is that the user can import their defining file using the "as" keyword. This allows the user to call the functions in the same way that they would call a function in numpy or matplotlib.

One of the disadvantages of Python is that names cannot be protected against further assignment. However, if "import numpy as np" appears at the top of the notebook, it's a strong hint that "np" should not be used as a common variable name. You can accomplish the same thing with class names, obviously, but user familiarity counts for a lot.

Inside the packages, however, I prefer to use static methods. My software architecture is object oriented, and I write with Eclipse, which I use for multiple target languages. It's convenient to open the source file and see the class definition at the top level, method definitions indented one level, and so on. The audience for the code at this level is mainly other analysts and developers, so it's better to avoid language-specific idioms.

I don't have a lot of confidence in Python namespace management, especially when using design patterns where (say) an object passes a reference to itself so that the called object can call a method defined on the caller. So I try not to force it too far. I use a lot of fully qualified names and explicit instance variables (with self) where in other languages I could count on the interpreter or the compiler managing the scope more closely. It's easier to do this with classes and static methods, which is why I think they are the better choice for complex packages where abstraction and information hiding are most useful.