模块函数vs静态方法vs类方法vs无装饰器:哪个习语更pythonic?
我是一个Java开发人员,他是用Python来玩弄和开发的。 我最近偶然发现了这篇文章 ,其中提到了Java程序员拿起Python时所犯的常见错误。 第一个引起了我的注意:
Java中的静态方法不会转换为Python类方法。 哦,当然,它的效果差不多,但是classmethod的目标实际上是做一些在Java中通常不可能的事情(比如inheritance一个非默认的构造函数)。 Java静态方法的惯用翻译通常是模块级函数,而不是类方法或静态方法。 (而静态最终字段应该转换为模块级别的常量。
这不是一个性能问题,但是一个Python程序员必须像这样使用Java-idiom代码,当inputFoo.Foo.someMethod的时候,应该只是Foo.someFunction而非常恼火。 但是请注意,调用一个classmethod涉及一个额外的内存分配,调用静态方法或函数不会。
哦,所有那些Foo.Bar.Baz属性链也不是免费的。 在Java中,这些虚线名称是由编译器查找的,所以在运行时,它们有多less个并不重要。 在Python中,查找在运行时发生,所以每个点都是重要的。 (请记住,在Python中,“Flat比嵌套更好”,但它与“Readability counts”和“Simple比复杂”更相关,而不是关于性能。
我发现这有点奇怪,因为staticmethod的文档说:
Python中的静态方法类似于Java或C ++中的方法。 另请参见classmethod(),该类对于创build备用类构造函数非常有用。
更令人费解的是,这个代码:
class A: def foo(x): print(x) A.foo(5)
在Python 2.7.3中失败,但在3.2.3中正常工作(虽然你不能在A的实例上调用方法,只能在类上调用)。
所以有三种方法来实现静态方法(如果使用classmethod方法,则有四种方法),每种方法都有细微的差别,其中一种看似没有logging。 这似乎与Python的口头禅不一致。 应该有一个 – 最好只有一个 – 明显的方法来做到这一点。 哪个成语是Pythonic最? 各有什么优点和缺点?
以下是我目前所了解的内容:
模块function:
- 避免Foo.Foo.f()问题
- 污染模块的名称空间比替代品多
- 没有inheritance
静态方法:
- 在类中保持与类有关的function,并在模块名称空间之外。
- 允许在类的实例上调用该函数。
- 子类可以覆盖该方法。
类方法:
- 与staticmethod相同,但也传递类作为第一个参数。
常规方法(仅限Python 3) :
- 与静态方法相同,但不能调用该类的实例的方法。
我是否在意这个? 这是一个非问题? 请帮忙!
思考这个问题的最直接的方法就是考虑方法需要什么types的对象来完成它的工作。 如果您的方法需要访问实例,请将其作为常规方法。 如果需要访问课堂,则将其作为课堂教学。 如果不需要访问类或实例,则将其作为函数。 很less有需要做一些静态的方法,但是如果你发现你想要一个函数与一个类“分组”(比如,所以它可以被覆盖),即使它不需要访问类,我猜你可以把它变成一个静态的方法。
我会补充说,在模块级别放置函数不会“污染”命名空间。 如果要使用这些函数,它们不会污染命名空间,它们就像应该使用它一样使用它。 函数是模块中的合法对象,就像类或其他东西一样。 如果没有任何理由在课堂上,那么没有理由在课堂上隐藏一个函数。
BrenBarn很好的回答,但我会改变'如果它不需要访问类或实例,使其成为函数'来:
'如果它不需要访问类或实例…但是与主题相关(典型示例:其他类方法使用的辅助函数和转换函数,或者由其他构造函数使用),则使用staticmethod
否则使其成为模块function
这不是一个真正的答案,而是一个冗长的评论:
更令人费解的是,这个代码:
class A: def foo(x): print(x) A.foo(5)
在Python 2.7.3中失败,但在3.2.3中正常工作(虽然你不能在A的实例上调用方法,只能在类上调用)。
我会尽力解释这里发生的事情。
严格来说,这是对“正常”实例方法协议的滥用。
你在这里定义的是一个方法,但是第一个(也是唯一的)参数没有命名为self
,而是x
。 当然你可以在A
的实例中调用这个方法,但是你必须像这样调用它:
A().foo()
要么
a = A() a.foo()
所以实例被赋予作为第一个参数的函数。
通过课程调用常规方法的可能性一直在那里,并且由它来工作
a = A() A.foo(a)
在这里,当你调用类的方法而不是实例时,它不会自动给出它的第一个参数,但是你必须提供它。
只要这是A
一个实例,一切都可以。 给它别的东西是国际海事组织滥用协议,因此Py2和Py3的区别:
在Py2中, A.foo
被转换为一个未绑定的方法,因此要求它的第一个参数是它“存在”类的一个实例。用其他方法调用它将会失败。
在Py3中,这个检查已经被删除, A.foo
只是原来的函数对象。 所以你可以把它作为第一个参数来调用,但我不会这样做。 方法的第一个参数应该总是被命名为self
并且具有self
的语义。