我可以定义一个可以直接在类实例上调用的静态方法吗?例如。,

MyClass.the_static_method()

当前回答

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

(注意,这个答案指的是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将表示Dog实例(即rex),而函数期望n是数字。

这就是装饰的由来:如果我们在“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)

其他回答

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

(注意,这个答案指的是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将表示Dog实例(即rex),而函数期望n是数字。

这就是装饰的由来:如果我们在“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)

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

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中的所有函数都以某种方式作用于复数。

您可以使用@staticmethoddecorator定义静态方法,如下所示。我在Python中对@classmethod vs@staticmethod的回答中详细解释了@staticmmethod decorator和@classmethoddecorator,并在Python中什么是“实例方法”的回答中解释了实例方法:

class Person:
    @staticmethod # Here
    def test():
        print("Test")

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

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)完全相同,只是没有使用漂亮的decorator语法。

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


以下是文件中的逐字内容:

静态方法不接收隐式第一个参数。要声明静态方法,请使用以下习惯用法:C类:@静态方法定义f(arg1,arg2,…):。。。@staticmethod表单是一个函数装饰器–有关详细信息,请参阅函数定义中的函数定义描述。它既可以在类(如C.f())上调用,也可以在实例(如C().f())中调用。除了它的类之外,该实例将被忽略。Python中的静态方法与Java或C++中的方法类似。有关更高级的概念,请参阅classmethod()。有关静态方法的更多信息,请参阅标准类型层次结构中有关标准类型层次的文档。2.2版中的新增功能。在版本2.4中进行了更改:添加了函数修饰符语法。

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

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。

如果确实需要静态方法作为类的一部分,可以使用staticmethoddecorator。