首页 文章

Python中的静态方法?

提问于
浏览
1450

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

ClassName.StaticMethod ( )

9 回答

  • 1761

    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()
    

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


    可以在描述时使用的另一个选项是 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'])
    
  • 76

    是的,使用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中很少需要使用静态方法,并且我已经看到它们多次使用,其中单独的"top-level"函数会更清晰 .


    The following is verbatim from the documentation:

    静态方法不会收到隐含的第一个参数 . 要声明一个静态方法,请使用此习语:class C:
    @staticmethod
    def f(arg1,arg2,...):...
    @staticmethod表单是一个函数装饰器 - 有关详细信息,请参阅函数定义中的函数定义说明 . 它可以在类(例如C.f())或实例(例如C() . f())上调用 . 除了类之外,该实例被忽略 . Python中的静态方法与Java或C中的静态方法类似 . 有关更高级的概念,请参阅classmethod() . 有关静态方法的详细信息,请参阅标准类型层次结构中标准类型层次结构的文档 . 版本2.2中的新功能 . 版本2.4中已更改:添加了函数装饰器语法 .

  • -1

    在Python 3中:

    使用非静态方法是另一个好处,因为它们是静态方法 .

    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(login_url, user_email, user_password)
    

    将非静态方法称为静态方法

    """
    * Referencing the Account.login(self, url, email, password)
    * Just call the `Account` object as the self 
    """
    Account.login(Account, "https://example.com/login", "email@example.com", "password_example")
    
    :$ https://example.com/login email@example.com password_example
    
  • 48

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

    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装饰器 .

  • 9

    是的,看看staticmethod装饰者:

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

    除了static method objects行为的特殊性之外,在组织模块级代码时,您可以使用它们获得某种美感 .

    # 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
    

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

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

    # 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
    
  • 30

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

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

  • 0

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

    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中的所有函数都以某种方式处理复数 .

  • 9

    我认为Steven is actually right . 要回答原始问题,那么,为了 Build 一个类方法,简单地假设第一个参数不是一个调用实例,然后确保你只从类中调用该方法 .

    (注意,这个答案指的是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)
    

相关问题