Python中的静态方法?


Answers:


2025

是的,使用staticmethod装饰器

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中更改:添加了函数装饰器语法。


15
@staticmethod当您可以简单地避免使用第一个self参数时,为什么要添加修饰或使用函数指针?好吧,对于一个对象a,您将无法调用a.your_static_method(),这在其他语言中是允许的,但是无论如何它被认为是一种不好的做法,并且编译器始终会发出警告
SomethingSomething

1
我正在使用python 2.7.12和@staticmethod装饰器,无法调用定义的第一个静态方法。其给定的错误:TypeError:'staticmethod'对象不可调用
Supratim Samantray

Supratim Samantray,你确定吗?因为这里明确指出它可以在2.4之后使用。
realslimshanky

11
@SomethingSomething,如果您不使用变量名称“ self”,但不添加装饰器,则第一个参数(例如arg1)仍将是对self实例的引用。自我这个名字只是一个约定。
乔治·穆特索普洛斯

1
@GeorgeMoutsopoulos-普遍同意。但是-无论如何,您都可以将方法调用为ClassName.methodName(),就好像它是静态方法一样,然后self将不会提供任何方法。如您所说,仍然可以将此方法也称为ClassInstance.methodName(),并且self将作为第一个参数提供,而不管其名称如何。
SomethingSomething

220

我认为史蒂文实际上是对的。为了回答最初的问题,然后,为了建立一个类方法,只需假设第一个参数不会成为调用实例,然后确保仅从类中调用该方法即可。

(请注意,此答案是针对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)

8
第一个示例(不带@staticmethod的类调用)在Python 2.7上不起作用。我收到“ TypeError:未绑定方法rollCall()必须以Dog实例作为第一个参数调用(代替int实例)”
tba

2
这是行不通的。Dog.rollCall(-1)和rex.rollCall(-1)都返回相同的值TypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops,2013年

3
@MestreLion我认为这并不是一个巨大的好处,因为它所做的只是混淆静态方法和实例方法,并使一个方法看起来与另一个方法相似。如果您知道某个方法是静态的,则应将其作为静态方法进行访问。无论您在哪里使用该方法,该方法都不需要或不使用实例的事实应该是不言而喻的。我总是使用T.my_static_method()type(my_t_instance).my_static_method(),因为这很清楚,并且可以立即看出我正在调用静态方法。
Asad Saeeduddin 2014年


54

您真的不需要使用 @staticmethod装饰器。只需声明一个方法(不需要self参数)并从类中调用它即可。装饰器仅在您还希望能够从实例调用它的情况下存在(这不是您想要执行的操作)

通常,您只是使用函数而已...


这不适用于python 2.5.2 class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() 输出:来自static2 Traceback的hello <最近一次调用last>:<module>中的文件“ ll.py”,第46行,Dummy.static1()TypeError:未绑定方法static1()必须是以虚拟实例作为第一个参数调用(而不是什么也没叫)
Mahori 2012年

7
这在类内部不起作用。self除非您不这么做,否则Python将作为第一个参数传递。(请参阅:装饰者)
Navin

9
实际上,这在2.7中是错误的,而从3.X开始是合法的(在3.2中经过良好测试)。也就是说,如果没有 2.7及以下版本的@staticmethod装饰器,则无法从类的上下文中调用方法。在3.2中,它可以正常工作,并将self根据其调用方式适当地插入ref。测试用例:pastebin.com/12DDV7DB
spinkus 2014年

2
技术上准确,但解决方案糟糕。该staticmethod装饰允许一个调用的类和实例(调用实例的功能时,该解决方案失败)这两个功能。
伊桑·弗曼

可以使用点表示法像调用对象的任何其他属性一样调用方法。class C: def callme(): print('called'); C.callme()
pouya

32

Python中的静态方法?

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

ClassName.StaticMethod()

是的,可以这样创建静态方法(尽管使用下划线代替CamelCase作为方法要有点Pythonic):

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中,它是stringPython 2中模块中的函数。


您可以使用的另一个选择是classmethod,不同之处是class方法将类作为隐式第一个参数获取,如果将其继承,则将子类作为隐式第一个参数获取。

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'])

11

除了静态方法对象的行为方式的特殊性之外,在组织模块级代码时,还可以利用它们带来某种美感。

# 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,其余模块最多为两个十几个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

10

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

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

如果确实需要静态方法作为类的一部分,则可以使用staticmethod装饰器。


1

因此,静态方法是可以在不创建类对象的情况下调用的方法。例如 :-

    @staticmethod
    def add(a, b):
        return a + b

b = A.add(12,12)
print b

在上面的示例中,方法add是通过类名A而不是对象名来调用的。


-3

Python静态方法可以通过两种方式创建。

  1. 使用staticmethod()

    class Arithmetic:
        def add(x, y):
            return x + y
    # create add static method
    Arithmetic.add = staticmethod(Arithmetic.add)
    
    print('Result:', Arithmetic.add(15, 10))

输出:

结果:25

  1. 使用@staticmethod

    class Arithmetic:
    
    # create add static method
    @staticmethod
    def add(x, y):
        return x + y
    
    print('Result:', Arithmetic.add(15, 10))

输出:

结果:25


你只提供了实例和共享的方式如何,你可以做到这一点。您没有解释这些方法的含义。
zixuan

-4

我不时遇到这个问题。我喜欢的用例和示例是:

jeffs@jeffs-desktop:/home/jeffs  $ python36
Python 3.6.1 (default, Sep  7 2017, 16:36:03) 
[GCC 6.3.0 20170406] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cmath
>>> print(cmath.sqrt(-4))
2j
>>>
>>> dir(cmath)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cos', 'cosh', 'e', 'exp', 'inf', 'infj', 'isclose', 'isfinite', 'isinf', 'isnan', 'log', 'log10', 'nan', 'nanj', 'phase', 'pi', 'polar', 'rect', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau']
>>> 

创建cmath类的对象没有任何意义,因为cmath对象中没有任何状态。但是,cmath是所有以某种方式相关的方法的集合。在上面的示例中,cmath中的所有函数都以某种方式作用于复数。


您没有回答问题,而是提供了一个示例。
zixuan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.