Python中的静态方法?

有没有可能在Python中有静态方法,这样我就可以调用它们而不初始化一个类,如:

1
ClassName.StaticMethod ( )

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

1
2
3
4
5
6
class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print x

MyClass.the_static_method(2) # outputs 2

注意,有些代码可能使用旧的方法来定义静态方法,使用staticmethod作为函数而不是修饰符。只有当您必须支持Python的旧版本(2.2和2.3)时,才应该使用它

1
2
3
4
5
6
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),只是没有使用nice装饰器语法

最后,要节约使用staticmethod() !在Python中很少需要静态方法,而且我见过很多次使用静态方法时,单独的"顶级"函数会更清晰。

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

A static method does not receive an implicit first argument. To declare a static method, use this idiom:

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

The @staticmethod form is a function decorator – see the description of function definitions in Function definitions for details.

It can be called either on the class (such as C.f()) or on an instance (such as C().f()). The instance is ignored except for its class.

Static methods in Python are similar to those found in Java or C++. For a more advanced concept, see classmethod().

For more information on static methods, consult the documentation on the standard type hierarchy in The standard type hierarchy.

New in version 2.2.

Changed in version 2.4: Function decorator syntax added.


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

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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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",例如:

1
rex.rollCall(-1)

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

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

这就是装饰的作用:如果我们在"rollCall"方法之前加上

1
@staticmethod

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

1
rex.rollCall(-1)

是可行的。然后,在方法定义之前插入@staticmethod可以阻止实例将自己作为参数发送。

您可以通过尝试以下代码来验证这一点,其中包含和不包含注释掉的@staticmethod行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)


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

1
2
3
4
5
6
7
>>> class C:
...     @staticmethod
...     def hello():
...             print"Hello World"
...
>>> C.hello()
Hello World

您实际上不需要使用@staticmethod装饰器。只是声明一个方法(不期望self参数)并从类中调用它。decorator只在您希望能够从实例中调用它时才会出现(这不是您想要做的)

大多数情况下,你只是使用函数…


Static methods in Python?

Is it possible to have static methods in Python so I can call them
without initializing a class, like:

1
ClassName.StaticMethod()

是的,静态方法可以这样创建(尽管对方法使用下划线而不是CamelCase更符合python风格):

1
2
3
4
5
class ClassName(object):

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

上面的代码使用装饰器语法。这个语法等价于

1
2
3
4
5
6
class ClassName(object):

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

    static_method = staticmethod(static_method)

正如你所描述的:

1
ClassName.static_method()

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

您描述的另一个选项是classmethod,不同之处在于classmethod将类作为隐式第一个参数获取,如果子类化了,则将子类作为隐式第一个参数获取。

1
2
3
4
5
class ClassName(object):

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

注意,cls不是第一个参数的必要名称,但是如果使用其他参数,大多数经验丰富的Python程序员都会认为它做得很糟糕。

它们通常用作替代构造函数。

1
new_instance = ClassName.class_method()

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

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

也许最简单的选择就是把这些函数放到类外:

1
2
3
4
5
6
7
8
9
10
11
12
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 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

1
2
3
4
5
# interactive.py
from garden import trim, bunch, Foo

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

1
2
3
4
5
6
7
8
9
10
# 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。我更喜欢从这个开始,把它转换成一个包,并创建模块,只有野兽真正值得他们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 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

1
2
3
4
5
6
class Stuff(object):
    @staticmethod
    def show():
        print("Done")

Stuff.show()


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

使用staticmethod ()

1
2
3
4
5
6
7
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

使用@staticmethod

1
2
3
4
5
6
7
8
class Arithmetic:

# create add static method
@staticmethod
def add(x, y):
    return x + y

print('Result:', Arithmetic.add(15, 10))

输出:

结果:25


在Python 3:

下面是使用非静态方法的另一个好处,因为它们是静态方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Account:
# here is the main class

# a non-static method
def login(self, url, email, password):
    # as a convention self is passed as a
    # placeholder for the class Object itself

    self.login_url = url
    self.user_email = email
    self.user_password = password

    print(self.login_url, self.user_email, self.user_password)

调用一个非静态方法作为静态

1
2
3
4
5
6
7
"""
* Referencing the Account.login(self, url, email, password)
* Just call the `Account` object as the self
"""

Account.login(Account,"https://example.com/login","[email protected]","password_example")

:$ https://example.com/login email@example.com password_example


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

1
2
3
4
5
6
7
8
9
10
11
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中的所有函数都以某种方式作用于复数。