使用静态方法有啥好处?
Posted
技术标签:
【中文标题】使用静态方法有啥好处?【英文标题】:What is the advantage of using static methods?使用静态方法有什么好处? 【发布时间】:2011-01-27 03:57:00 【问题描述】:我用代码在python中遇到了未绑定方法错误
import random
class Sample(object):
'''This class defines various methods related to the sample'''
def drawSample(samplesize,List):
sample=random.sample(List,samplesize)
return sample
Choices=range(100)
print Sample.drawSample(5,Choices)
在这里阅读了许多有用的帖子后,我想出了如何在上面添加@staticmethod
以使代码正常工作。我是蟒蛇新手。有人可以解释一下为什么要定义静态方法吗?或者,为什么不是所有的方法都定义为静态方法?
【问题讨论】:
这是一个奇怪的问题。静态方法是设计必需。这不是一个“优势”的东西。你使用它们是因为你必须这样做。具有静态方法是类的设计特征。您是在问什么是静态方法吗?我认为这个问题可以重新措辞,以更清楚地定义您需要知道的内容。 不,我不想知道它们是什么。我想知道的是为什么它是“必需品”,从其他人给出的答案中已经很清楚了。那是您何时定义它而不是非静态方法。谢谢。 @S.Lott:什么时候需要使用静态方法而不是使用普通的类方法?据我所知,类方法可以做静态方法可以做的所有事情。静态方法确实具有本文其他地方列出的“优势”,但我看不出为什么不能在可以使用静态方法的任何地方使用类方法的任何原因,因此使其成为必需品。 【参考方案1】:staticmethod
的替代选项是:classmethod
、instancemethod
和 function
。如果您不知道这些是什么,请向下滚动到最后一部分。如果staticmethod
比这些替代方案中的任何一个更好,则取决于它的编写目的。
Python 静态方法的优点
如果您不需要访问类或实例的属性或方法,staticmethod
比classmethod
或instancemethod
更好。这样就很清楚(来自@staticmethod
装饰器)类和实例的状态没有被读取或修改。但是,使用 function
会使这种区别更加清晰(请参阅缺点)。
staticmethod
的调用签名与classmethod
或instancemethod
的调用签名相同,即<instance>.<method>(<arguments>)
。因此,如果以后需要或在派生类中需要,它可以很容易地被 三个 之一替换。简单的function
无法做到这一点。
可以使用staticmethod
代替function
来明确它主观上属于一个类并防止命名空间冲突。
Python 静态方法的缺点
它不能访问实例或类的属性或方法。staticmethod
的调用签名与classmethod
或instancemethod
的调用签名相同。这掩盖了staticmethod
实际上并未读取或修改任何对象信息的事实。这使得代码更难阅读。为什么不直接使用function
?
如果您需要从定义它的类/实例外部调用staticmethod
,则很难重复使用它。如果有任何重复使用的潜力,function
是更好的选择。
staticmethod
很少使用,因此阅读包含 staticmethod
的代码的人可能需要更长的时间来阅读它。
Python 中静态方法的替代方案
要讨论staticmethod
的优势,我们需要知道替代方案是什么以及它们之间的区别。
staticmethod
属于一个类,但不能访问或修改任何实例或类信息。
它有三种选择:
classmethod
可以访问调用者的类。
instancemethod
可以访问调用者的实例及其类。
function
与类无关。它的功能最接近staticmethod
。
这是代码中的样子:
# function
# has nothing to do with a class
def make_cat_noise(asker_name):
print('Hi %s, mieets mieets!' % asker_name)
# Yey, we can make cat noises before we've even defined what a cat is!
make_cat_noise('JOey') # just a function
class Cat:
number_of_legs = 4
# special instance method __init__
def __init__(self, name):
self.name = name
# instancemethod
# the instance (e.g. Cat('Kitty')) is passed as the first method argument
def tell_me_about_this_animal(self, asker_name):
print('Hi %s, This cat has %d legs and is called %s'
% (asker_name, self.number_of_legs, self.name))
# classmethod
# the class (e.g. Cat) is passed as the first method argument
# by convention we call that argument cls
@classmethod
def tell_me_about_cats(cls, asker_name):
print("Hi %s, cats have %d legs."
% (asker_name, cls.number_of_legs))
# cls.name # AttributeError because only the instance has .name
# self.name # NameError because self isn't defined in this namespace
# staticmethod
# no information about the class or the instance is passed to the method
@staticmethod
def make_noise(asker_name):
print('Hi %s, meooow!' % asker_name)
# class and instance are not accessible from here
# one more time for fun!
make_cat_noise('JOey') # just a function
# We just need the class to call a classmethod or staticmethod:
Cat.make_noise('JOey') # staticmethod
Cat.tell_me_about_cats('JOey') # classmethod
# Cat.tell_me_about_this_animal('JOey') # instancemethod -> TypeError
# With an instance we can use instancemethod, classmethod or staticmethod
mycat = Cat('Kitty') # mycat is an instance of the class Cat
mycat.make_noise('JOey') # staticmethod
mycat.tell_me_about_cats('JOey') # classmethod
mycat.tell_me_about_this_animal('JOey') # instancemethod
【讨论】:
【参考方案2】:这不是你的实际问题的重点,但既然你说你是一个 python 新手,也许它会有所帮助,而且没有其他人完全站出来明确地说出来。
我永远不会通过将方法设为静态方法来修复上述代码。我要么放弃这门课,只写一个函数:
def drawSample(samplesize,List):
sample=random.sample(List,samplesize)
return sample
Choices=range(100)
print drawSample(5,Choices)
如果你有很多相关的函数,你可以将它们分组到一个模块中——即将它们都放在同一个文件中,例如命名为sample.py
;那么
import sample
Choices=range(100)
print sample.drawSample(5,Choices)
或者我会在类中添加一个__init__
方法并创建一个具有有用方法的实例:
class Sample(object):
'''This class defines various methods related to the sample'''
def __init__(self, thelist):
self.list = thelist
def draw_sample(self, samplesize):
sample=random.sample(self.list,samplesize)
return sample
choices=Sample(range(100))
print choices.draw_sample(5)
(我还更改了上面示例中的大小写约定以匹配 PEP 8 推荐的样式。)
Python 的优点之一是它不会强迫您对所有事情都使用类。只有当存在应该与方法相关联的数据或状态时才能使用它们,这就是类的用途。否则,您可以使用函数,这就是函数的用途。
【讨论】:
感谢您的评论。在这种情况下,我确实需要一个课程,因为我想使用绘制的样本。不,我没有使用静态方法,但想了解一下,因为我在查找有关收到的错误消息的信息时遇到了该术语。但是您关于在不定义类的情况下将函数收集到模块中的建议对我需要的其他函数很有帮助。所以谢谢。 +1:这很好地解释了 OP 的一些误解。而且您非常诚实地坦率地说,您实际上没有回答他关于静态方法的问题,而是给出了一个更好的解决方案说他的问题不需要类。【参考方案3】:据我估计,使用@staticmethod
s 与仅在类之外定义函数相比,并没有单一的性能优势,否则它将是@staticmethod
的类。
我要说的唯一证明它们存在的理由就是方便。静态方法在其他流行的编程语言中很常见,那么为什么不是 python 呢?如果您想创建一个行为与您为其创建它的类非常密切相关的函数,但它实际上并没有以一种证明将其概念化为典型的方式来访问/修改类实例的内部数据然后在该类的方法上面加上@staticmethod
,任何阅读您的代码的人都会立即了解该方法的性质及其与类的关系。
我偶尔喜欢做的一件事是将我的班级内部经常使用的功能放入私有@staticmethod
s。这样一来,我的模块所公开的 API 就不会被使用我模块的任何人都不需要看到的方法弄乱,更不用说使用了。
【讨论】:
【参考方案4】:静态方法的用途有限,因为它们无法访问类实例的属性(就像常规方法一样),也无法访问类本身的属性(例如类方法可以)。
因此它们对于日常方法没有用处。
但是,它们可以用于将某些实用程序功能与类组合在一起 - 例如从一种类型到另一种类型的简单转换 - 除了提供的参数(可能还有模块的一些全局属性)之外,不需要访问任何信息。
它们可以放在类之外,但是将它们分组到类中可能是有意义的,它们只适用于那里。
您也可以通过实例或类来引用方法,而不是模块名称,这可能有助于读者理解该方法与哪个实例相关。
【讨论】:
@Curious2learn:不是每个,但一些方法可用作静态方法。考虑一个示例Locale
类,其实例将是语言环境(duh)。 getAvailableLocales()
方法将是此类静态方法的一个很好的示例:它显然属于 Locale 类,同时也不属于任何特定实例。
...它也不是类方法,因为它可能不需要访问任何类的方法。
一位编辑指出,静态方法可以访问类属性,方法是从class_name.attribute
显式向下导航,而不是cls.attribute
或self.attribute
。这适用于“公共”属性。按照惯例,您不应该以这种方式访问用下划线隐藏的属性,或用两个下划线混淆的名称。这也意味着当属性在继承层次结构上发生变化时,您的代码将更加脆弱。
Guido 的一句话有助于决定何时使用静态方法(从不):“我们都知道静态方法有多么有限。(它们基本上是一个意外——回到 Python 2.2 的时候,我发明了新式的类和描述符,本来是想实现类方法的,一开始没看懂,不小心先实现了静态方法,后来去掉就来不及了,只提供类方法。”【参考方案5】:
因为命名空间函数很好(如前所述):
当我想明确说明不改变对象状态的方法时,我使用静态方法。这不鼓励我团队中的人开始在这些方法中更改对象的属性。
当我重构真正烂代码时,我首先尝试创建尽可能多的方法@staticmethod
。这让我可以将这些方法提取到一个类中——尽管我同意,这不是我使用的东西,但它确实有用过几次。
【讨论】:
【参考方案6】:详细解释见this article。
TL;DR
1.它消除了self
参数的使用。
2.它减少了内存使用,因为Python不必为每个实例化的对象实例化bound-method:
>>>RandomClass().regular_method is RandomClass().regular_method
False
>>>RandomClass().static_method is RandomClass().static_method
True
>>>RandomClass.static_method is RandomClass().static_method
True
3.它提高了代码的可读性,表明该方法不依赖于对象本身的状态。
4.它允许方法覆盖,因为如果方法是在模块级别(即在类之外)定义的,则子类将无法覆盖该方法。
【讨论】:
这应该是公认的答案。任何实例上的静态方法和类本身都是同一个对象这一事实是一个真正的优势,尤其是当您有很多实例时(例如,每个实例对应一个可变数据库记录)。 +1 并同意@ZhuoyunWei。只回答解释为什么静态方法有时比类方法更可取的几个原因(尽管 1 和 3 实际上是相同的原因)。 “为什么是静态方法”的最佳答案,但原始问题还问“为什么不是所有方法都是静态的?”。简短的“无法访问实例属性”也将涵盖这一点。 IMO 唯一真正的优势是您可以在子类中覆盖它,但即使这样也会让人感觉您在 OOP 方面做错了什么,在什么用例中您会这样做?跨度> 3. - 如果没有状态,那么为什么首先要创建一个类?模块功能也一样好,代码更少。【参考方案7】:为什么要定义静态方法?
假设我们有一个名为Math
的class
那么
没有人会想要创建class Math
的对象
然后在其上调用ceil
和floor
和fabs
之类的方法。
所以我们将它们设为static
。
例如做
>> Math.floor(3.14)
比
好很多>> mymath = Math()
>> mymath.floor(3.14)
所以它们在某些方面很有用。您无需创建类的实例即可使用它们。
为什么不是所有的方法都定义为静态方法?
他们无权访问实例变量。
class Foo(object):
def __init__(self):
self.bar = 'bar'
def too(self):
print self.bar
@staticmethod
def foo():
print self.bar
Foo().too() # works
Foo.foo() # doesn't work
这就是为什么我们不将所有方法都设为静态的原因。
【讨论】:
但是为什么不包数学呢? Python 有相应的包,您不需要 类定义来创建命名空间。 @extraneon:是的,伙计,我知道,但我想要一些简单而熟悉的解释,所以我使用了Math
。这就是我将M
大写的原因。
OP 没有问什么是静态方法。他问他们有什么优势。您正在解释如何使用它们,而不是它们如何有用。在您的特定示例中,命名空间会更有意义。
-1:很好且正确的解释,但选择的例子很糟糕:你编造的Math
首先没有理由成为一个类,一个模块会更合适。尝试找到一个作为类有意义的类的示例,而不是“没有人想要创建对象”。
而 then 给出了一个合法用例的例子 one(或 some)的类'方法是静态的,但不是全部。如果一个类的所有方法都是静态的,那么类应该是一个模块。如果 none 是静态的,那么您没有回答问题。【参考方案8】:
当您从对象实例调用函数对象时,它成为“绑定方法”并获取实例对象本身作为第一个参数传入。
当您在对象实例上调用classmethod
对象(包装函数对象)时,实例对象的类作为第一个参数传入。
当您调用 staticmethod
对象(包装函数对象)时,不使用隐式第一个参数。
class Foo(object):
def bar(*args):
print args
@classmethod
def baaz(*args):
print args
@staticmethod
def quux(*args):
print args
>>> foo = Foo()
>>> Foo.bar(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got int instance instead)
>>> Foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> Foo.quux(1,2,3)
(1, 2, 3)
>>> foo.bar(1,2,3)
(<Foo object at 0x1004a4510>, 1, 2, 3)
>>> foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> foo.quux(1,2,3)
(1, 2, 3)
【讨论】:
+1:最后很好地解释了静态方法和类方法是什么。尽管您没有解释 为什么 有人会想要使用静态方法,但至少您在一个简单的示例中清楚地解释了两者 比官方文档要好得多的地方。 【参考方案9】:静态方法在 Python 中几乎没有理由存在。您可以使用实例方法或类方法。
def method(self, args):
self.member = something
@classmethod
def method(cls, args):
cls.member = something
@staticmethod
def method(args):
MyClass.member = something
# The above isn't really working
# if you have a subclass
【讨论】:
你说“几乎”。有没有比替代品更好的地方? @Javier:我想不出一个,但可能有一个,否则为什么该方法会包含在 Python 库中? @Javier, @Georg:您非常相信 Python 语料库没有垃圾。 -1:此答案不提供staticmethod
的任何 用例或任何 解释classmethod
是什么或为什么和它如何比静态的“更好”。
@CharlesMerriam:静态方法当然有它们的用途,否则它会在 Python 3 中被删除或弃用(其中大部分遗留问题已被删除)。文档中没有一个单个词来反对staticmethod
来支持您声称它是不成熟的,或者Georg 声称应该使用classmethod
来代替)。【参考方案10】:
静态方法很棒,因为您不必声明该方法所属对象的实例。
python 的站点在这里有一些关于静态方法的精彩文档:http://docs.python.org/library/functions.html#staticmethod
【讨论】:
谢谢大卫。但是为什么不将每个方法都定义为静态方法,因为它们也适用于实例。这样做有什么缺点吗? @Curious2learn:不,使用静态方法您无法访问实例:除了其类之外,该实例被忽略。 这个论点在 Java 中是正确的,其中函数不能独立存在,而是始终在类的上下文中定义。但在 Python 中,您可以拥有函数和静态类函数。这个答案并没有真正说明为什么要选择静态方法而不是不在类中的方法。 @extraneon - 这更多是代码组织偏好的问题;拥有静态方法提供了一个额外的选择。 @Felix - 谢谢。这阐明了为什么每个方法都不应该是静态方法。以上是关于使用静态方法有啥好处?的主要内容,如果未能解决你的问题,请参考以下文章