题 Python中的静态方法?


是否有可能在Python中使用静态方法,因此我可以在不初始化类的情况下调用它们,例如:

ClassName.StaticMethod ( )

1392
2018-04-09 21:23


起源




答案:


是的,使用了 静态方法 装饰

class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print x

MyClass.the_static_method(2) # outputs 2

请注意,某些代码可能会使用定义静态方法的旧方法 staticmethod 作为一个功能而不是装饰者。只有在你必须支持古老版本的Python(2.2和2.3)时才应该使用它

class MyClass(object):
    def the_static_method(x):
        print x
    the_static_method = staticmethod(the_static_method)

MyClass.the_static_method(2) # outputs 2

这与第一个例子完全相同(使用 @staticmethod),只是没有使用漂亮的装饰器语法

最后,使用 staticmethod() 谨慎!在Python中很少需要静态方法,我已经看到它们多次使用单独的“顶级”函数会更清晰。


以下是文档中的逐字记录:

静态方法不会接收隐式的第一个参数。要声明静态方法,请使用此惯用法:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

@staticmethod表单是一个函数 装饰  - 参见函数定义的描述 功能定义 详情。

它可以在类上调用(例如 C.f())或在一个实例上(例如 C().f())。除了类之外,该实例被忽略。

Python中的静态方法与Java或C ++中的静态方法类似。有关更高级的概念,请参阅 classmethod()

有关静态方法的更多信息,请参阅有关标准类型层次结构的文档 标准类型层次结构

版本2.2中的新功能。

版本2.4中已更改:添加了函数装饰器语法。


1699
2018-04-09 21:24



为何添加 @staticmethod 装饰或使用函数指针,当你可以简单地避免第一个 self 参数?好吧,对于一个对象 a,你将无法打电话 a.your_static_method(),这是其他语言允许的,但它无论如何被认为是一种不好的做法,编译器总是警告它 - SomethingSomething
我使用python 2.7.12,与@staticmethod装饰器我无法调用第一个静态方法定义。它的givin错误: TypeError:'staticmethod'对象不可调用 - Supratim Samantray
Supratim Samantray,你确定吗?因为 这里 它清楚地表明它可以在2.4之后使用。 - realslimshanky


我觉得 史蒂文其实是对的。要回答原始问题,那么,为了设置一个类方法,只需假设第一个参数不是一个调用实例,然后确保只调用该类中的方法。

(请注意,这个答案指的是Python 3.x.在Python 2.x中你会得到一个 TypeError 用于在类本身上调用方法。)

例如:

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    def rollCall(n): #this is implicitly a class method (see comments below)
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))

fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)

在此代码中,“rollCall”方法假定第一个参数不是实例(如果它是由实例而不是类调用的话)。只要从类而不是实例调用“rollCall”,代码就可以正常工作。如果我们尝试从一个实例调用“rollCall”,例如:

rex.rollCall(-1)

但是,它会引发异常,因为它会发送两个参数:本身和-1,而“rollCall”只定义为接受一个参数。

顺便说一下,rex.rollCall()会发送正确数量的参数,但也会引发异常,因为当函数期望n为数字时,n表示一个Dog实例(即rex)。

这是装饰的地方: 如果我们在“rollCall”方法之前使用

@staticmethod

然后,通过明确声明该方法是静态的,我们甚至可以从实例中调用它。现在,

rex.rollCall(-1)

会工作。然后,在方法定义之前插入@staticmethod会阻止实例将自身作为参数发送。

您可以通过在注释掉@staticmethod行的情况下尝试以下代码来验证这一点。

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    @staticmethod
    def rollCall(n):
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))


fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)
rex.rollCall(-1)

176
2018-04-18 09:16



第一个例子(没有@staticmethod的类调用)在Python 2.7上对我不起作用。我得到“TypeError:unbound方法rollCall()必须使用Dog实例作为第一个参数调用(改为使用int实例)” - tba
这不起作用。 Dog.rollCall(-1)和rex.rollCall(-1)都返回相同的值 TypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead) - Mittenchops
@MestreLion我认为这不是一个巨大的好处,因为它所做的只是混淆静态和实例方法,让一个看起来像另一个。如果您知道某个方法是静态的,则应该将其作为静态方法访问。无论您在何处使用该方法,该方法都不需要或使用您的实例这一事实应该是不言而喻的。我总是用 T.my_static_method() 要么 type(my_t_instance).my_static_method(),因为这更清楚,并且很明显我正在调用静态方法。 - Asad Saeeduddin


是的,看看 静态方法 装饰:

>>> class C:
...     @staticmethod
...     def hello():
...             print "Hello World"
...
>>> C.hello()
Hello World

74
2018-04-09 21:24





你真的不需要使用 @staticmethod 装饰。只是声明一个方法(不期望self参数)并从类中调用它。装饰器只是为了你想要能够从一个实例调用它(这不是你想要做的)

大多数情况下,你只是使用功能...


43
2018-04-10 16:00



要么这不起作用,要么不清楚你的意思。如果发布了一个工作示例,我将删除我的downvote。 - Eric Wilson
这不起作用,尽管它应该是有意义的。傻蟒......;) - weberc2
这在课堂上不起作用。 Python将通过 self 作为第一个参数,除非你告诉它不要。 (见:装饰者) - Navin
实际上这在2.7和3.X中是合法的(在3.2中被罚款)。那是你 不能 在2.7及以下版本中没有@staticmethod装饰器的类的上下文中调用方法。在3.2它工作,并将插入一个 self 适当取决于它的调用方式。测试用例: pastebin.com/12DDV7DB。 - spinkus
技术上准确,但一个可怕的解决方案。该 staticmethod decorator允许在类和实例上调用该函数(此解决方案在实例上调用函数时失败)。 - Ethan Furman


Python中的静态方法?

是否可以在Python中使用静态方法,以便我可以调用它们   没有初始化类,如:

ClassName.StaticMethod()

是的,可以像这样创建静态方法(虽然它更多一些 Python化 使用下划线代替CamelCase方法):

class ClassName(object):

    @staticmethod
    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

以上使用装饰器语法。这个语法相当于

class ClassName(object):

    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

    static_method = staticmethod(static_method)

这可以像你描述的那样使用:

ClassName.static_method()

静态方法的内置示例是 str.maketrans() 在Python 3中,它是一个函数 string Python 2中的模块。


您可以使用的另一个选项是 classmethod,不同之处在于classmethod将类作为隐式的第一个参数获取,如果是子类,则它将子类作为隐式的第一个参数。

class ClassName(object):

    @classmethod
    def class_method(cls, kwarg1=None):
        '''return a value that is a function of the class and kwarg1'''

注意 cls 不是第一个参数的必需名称,但是如果您使用其他任何东西,大多数有经验的Python编码器都会认为它很糟糕。

这些通常用作替代构造函数。

new_instance = ClassName.class_method()

一个内置的例子是 dict.fromkeys()

new_dict = dict.fromkeys(['key1', 'key2'])

28
2018-01-24 04:34





除了如何的特殊性 静态方法对象 表现,在组织模块级代码时,你可以与他们一起打击某种美。

# garden.py
def trim(a):
    pass

def strip(a):
    pass

def bunch(a, b):
    pass

def _foo(foo):
    pass

class powertools(object):
    """
    Provides much regarded gardening power tools.
    """
    @staticmethod
    def answer_to_the_ultimate_question_of_life_the_universe_and_everything():
        return 42

    @staticmethod
    def random():
        return 13

    @staticmethod
    def promise():
        return True

def _bar(baz, quux):
    pass

class _Dice(object):
    pass

class _6d(_Dice):
    pass

class _12d(_Dice):
    pass

class _Smarter:
    pass

class _MagicalPonies:
    pass

class _Samurai:
    pass

class Foo(_6d, _Samurai):
    pass

class Bar(_12d, _Smarter, _MagicalPonies):
    pass

...

# tests.py
import unittest
import garden

class GardenTests(unittest.TestCase):
    pass

class PowertoolsTests(unittest.TestCase):
    pass

class FooTests(unittest.TestCase):
    pass

class BarTests(unittest.TestCase):
    pass

...

# interactive.py
from garden import trim, bunch, Foo

f = trim(Foo())
bunch(f, Foo())

...

# my_garden.py
import garden
from garden import powertools

class _Cowboy(garden._Samurai):
    def hit():
        return powertools.promise() and powertools.random() or 0

class Foo(_Cowboy, garden.Foo):
    pass

它现在变得更加直观和自我记录,其中某些组件应该被使用,并且它理想地用于命名不同的测试用例,并且具有直接的方法来测试模块如何映射到纯粹主义者测试下的实际模块。

我经常发现应用这种方法来组织项目的实用程序代码是可行的。很多时候,人们立即赶紧创造一个 utils 包装并最终有9个模块,其中一个有120个LOC,其余的最多是24个LOC。我更喜欢从这开始并将其转换为包并仅为真正值得它们的野兽创建模块:

# utils.py
class socket(object):
    @staticmethod
    def check_if_port_available(port):
        pass

    @staticmethod
    def get_free_port(port)
        pass

class image(object):
    @staticmethod
    def to_rgb(image):
        pass

    @staticmethod
    def to_cmyk(image):
        pass

9
2017-12-29 19:47





也许最简单的选择就是将这些函数放在类之外:

class Dog(object):
    def __init__(self, name):
        self.name = name

    def bark(self):
        if self.name == "Doggy":
            return barking_sound()
        else:
            return "yip yip"

def barking_sound():
    return "woof woof"

使用此方法,可以在类中保留修改或使用内部对象状态(具有副作用)的函数,并且可以将可重用的实用程序函数移出到外部。

假设这个文件被调用了 dogs.py。要使用这些,你打电话 dogs.barking_sound() 代替 dogs.Dog.barking_sound

如果你真的需要静态方法成为类的一部分,你可以使用 静态方法 装饰。


8
2017-07-30 16:10