简体   繁体   English

为什么在 Python 中选择模块级函数而不是 @staticmethod(根据 Google 风格指南)?

[英]Why choose module level function over @staticmethod in Python (according to Google Style Guide)?

According to Google Python Style Guide, static methods should (almost) never be used:根据 Google Python Style Guide,静态方法应该(几乎)永远不会被使用:

Never use @staticmethod unless forced to in order to integrate with an API defined in an existing library.除非为了与现有库中定义的 API 集成而被迫使用 @staticmethod。 Write a module level function instead改为编写模块级函数

What are the reasons behind such recommendation?这样的推荐背后的原因是什么?

Is this particular to Google only or are there any other (more general) downsides with using static methods in Python?这仅适用于 Google,还是在 Python 中使用静态方法有其他(更一般的)缺点吗?

Especially, what is the best practice if I want to implement an utility function inside of a class that will be called by other public member functions of that class?特别是,如果我想在一个类中实现一个由该类的其他公共成员函数调用的实用程序函数,最佳实践是什么?

class Foo: 
    .......
    def member_func(self): 
        some_utility_function(self.member)

google python style guide 谷歌python风格指南

How to understand the Google Python Style Guide that says:如何理解 Google Python Style Guide 说:

Never use @staticmethod unless forced to in order to integrate with an API defined in an existing library.除非为了与现有库中定义的 API 集成而被迫使用 @staticmethod。 Write a module level function instead改为编写模块级函数

Well, you should understand it as Google's style guide.好吧,您应该将其理解为 Google 的风格指南。 If you're writing Python code for Google, or contributing to a project that conforms to that style guide, or have chosen to use it for a project of your own, the answer is pretty simple: Don't use @staticmethod except when forced to by an API.如果您正在为 Google 编写 Python 代码,或者为符合该样式指南的项目做出贡献,或者选择将其用于您自己的项目,那么答案非常简单:除非被迫,否则不要使用@staticmethod通过 API。

This means there are no judgment-call cases: A utility function inside of a class is not forced to be a @staticmethod by an API, so it should not be a @staticmethod .这意味着没有判断调用情况:类中的实用程序函数不会被 API 强制为@staticmethod ,因此它不应该是@staticmethod

The same is true for some other common 1 reasons for @staticmethod .对于@staticmethod其他一些常见原因1也是@staticmethod If you want a default value for an instance attribute that's meant to hold a callback function… too bad, find another way to write it (eg, a local function defined inside __init__ ).如果你想要一个用于保存回调函数的实例属性的默认值......太糟糕了,找到另一种编写它的方法(例如,在__init__定义的本地函数)。 If you want something that looks like a @classmethod but explicitly doesn't covary with subclasses… too bad, it just can't look like a @classmethod .如果你想要一些看起来像@classmethod但明确不与子类共变的东西......太糟糕了,它看起来不像@classmethod


Of course if you're not following Google's style guide, then you should understand it as just one opinion among many.当然,如果您没有遵循 Google 的风格指南,那么您应该将其理解为众多意见中的一种。 Plenty of Python developers aren't quite as hard against @staticmethod as that guide is.许多 Python 开发人员并不像该指南那样反对@staticmethod Of course Google is a pretty prominent developer of lots of Python code.当然,谷歌是许多 Python 代码的杰出开发者。 On the other hand, Google's style guide was written while imported Java-isms were more of a problem than today.另一方面,谷歌的风格指南是在导入的 Java 主义比今天更成问题的时候编写的。 2 But you probably don't want to think too much about how much weight to give each opinion; 2但你可能不想过多考虑每个意见的权重; instead, when it's important, learn the issues and come up with your own opinion.相反,当它很重要时,了解问题并提出自己的意见。


As for your specific example, as I said in a comment: the fact that you naturally find yourself writing some_utility_function(self.member) instead of self.some_utility_function(self.member) or Foo.some_utility_function(self.member) means that intuitively, you're already thinking of it as a function, not a @staticmethod .至于你的具体例子,正如我在评论中所说:事实上,你很自然地发现自己在写some_utility_function(self.member)而不是self.some_utility_function(self.member)Foo.some_utility_function(self.member)意味着直观,您已经将其视为一个函数,而不是@staticmethod In which case you should definitely write that one as a function, not a @staticmethod .在这种情况下,你绝对应该把它写成一个函数,而不是一个@staticmethod

That may be just the opinion of one guy on the internet, but I think most Python developers would agree in this case.这可能只是互联网上一个人的意见,但我认为大多数 Python 开发人员都会同意这种情况。 It's the times when you do naturally find yourself prefixing self.这是你自然地发现自己在前缀self. before every call when there's a judgment call to make.在每次调用之前,当需要做出判断时。


1. Well, not exactly common . 1. 嗯,并不常见 But they aren't so rare that they never come up.但它们并不罕见,以至于它们永远不会出现。 And they were common enough that, when there was discussion about deprecating @staticmethod for Python 3, someone quickly came up with these two cases, with examples from the standard library, and that was enough for Guido to kill the discussion.而且它们很常见,当有人讨论弃用 Python 3 的@staticmethod ,有人很快就提出了这两种情况,以及来自标准库的示例,这足以让 Guido 扼杀讨论。

2. In Java, there are no module-level functions, and you're forced to write static methods to simulate them. 2.在Java中,没有模块级的函数,你不得不编写静态方法来模拟它们。 And there were a few years where most university CS programs were focused on Java, and a ton of software was written by Java, so tons of people were writing Python classes with way too many @staticmethod s (and getters and setters, and other Java-isms).有几年,大多数大学 CS 程序都专注于 Java,大量软件是由 Java 编写的,因此很多人使用太多的@staticmethod (以及 getter 和 setter,以及其他 Java -主义)。

The way you've written the call to some_utility_function() , it isn't defined on the class anyway.您编写对some_utility_function()调用的方式,无论如何都没有在类中定义。 If it were, you would be using self.some_utility_function() or possibly Foo.some_utility_function() to call it.如果是,您将使用self.some_utility_function()Foo.some_utility_function()来调用它。 So you've already done it the way the style guide recommends.所以你已经按照风格指南推荐的方式做了。

The @classmethod and @staticmethod decorators are used primarily to tell Python what to pass as the first argument to the method in place of the usual self : either the type, or nothing at all. @classmethod@staticmethod装饰器主要用于告诉 Python 将什么作为第一个参数传递给方法来代替通常的self :要么是类型,要么什么都不做。 But if you're using @staticmethod , and need neither the instance nor its type, should it really be a member of the class at all?但是,如果您使用的是@staticmethod ,并且既不需要实例也不需要它的类型,它真的应该是类的成员吗? That's what they're asking you to consider here: should utility functions be methods of a class, when they are not actually tied to that class in any way?这就是他们在这里要求您考虑的问题:实用程序函数是否应该是类的方法,而它们实际上并未以任何方式与该类相关联? Google says no.谷歌说不。

But this is just Google's style guide.但这只是谷歌的风格指南。 They have decided that they want their programmers to prefer module-level functions.他们决定希望他们的程序员更喜欢模块级函数。 Their word is not law.他们的话不是法律。 Obviously the designers of Python saw a use for @staticmethod or they wouldn't have implemented it!显然,Python 的设计者看到了@staticmethod的用途,否则他们就不会实现它! If you can make a case for having a utility function attached to a class, feel free to use it.如果您可以证明将实用函数附加到类,请随意使用它。

My 2¢我的 2 美分

The point is that when you want to do duck-typing polymorphic things, defining module level functions is overkilled, especially if your definitions are very short.关键是当你想做鸭子类型的 多态事情时,定义模块级函数是多余的,尤其是如果你的定义很短。 Eg defining例如定义

class StaticClassA:
    @staticmethod
    def maker(i: int) -> int:
        return 2*i

class StaticClassB:
    @staticmethod
    def maker(i: int) -> float:
        return pow(i, 2)

#[...] say, 23 other classes definitions

class StaticClassZ:
    @staticmethod
    def maker(i: int) -> float:
        return 2*pow(i, 2)

Is clearly smarter than having 26 (from A to Z) classes defined within 26 modules.显然比在 26 个模块中定义 26 个(从 A 到 Z)类更聪明。


A practical example of what I imply with the word "polymorphism" ? 我用“多态性”这个词所暗示的一个实际例子? With the above classes definitions, you can do 有了上面的类定义,你可以做

for class_ in [StaticClassA, StaticClassB, StaticClassZ]: print(class_.maker(6))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM