python - staticmethod使用時機 - staticmethod self




@staticmethod和@classmethod有什麼區別? (16)

@staticmethod修飾的函數和用@staticmethod修飾的函數有@staticmethod @classmethod


Python中的@staticmethod和@classmethod有什麼區別?

您可能已經看過像這個偽代碼的Python代碼,它演示了各種方法類型的簽名,並提供了一個文檔字符串來解釋每個:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

正常實例方法

首先,我將解釋a_normal_instance_method 。 這恰恰稱為“ 實例方法 ”。 當使用實例方法時,它被用作部分函數(與在源代碼中查看時為所有值定義的總函數相反),即,當使用時,第一個參數被預定義為實例。對象,具有所有給定的屬性。 它具有綁定到它的對象的實例,並且必須從對象的實例調用它。 通常,它將訪問實例的各種屬性。

例如,這是一個字符串的實例:

', '

如果我們使用實例方法, join這個字符串,加入另一個迭代,它顯然是實例的一個函數,除了是可迭代列表的函數, ['a', 'b', 'c']

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

綁定方法

可以通過點查找綁定實例方法以供稍後使用。

例如,這str.join方法綁定到':'實例:

>>> join_with_colons = ':'.join 

之後我們可以將它用作已經綁定了第一個參數的函數。 這樣,它就像實例上的部分函數一樣:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

靜態方法

靜態方法不將實例作為參數。

它與模塊級功能非常相似。

但是,模塊級功能必須存在於模塊中,並專門導入到使用它的其他位置。

但是,如果它附加到對象,它也將通過導入和繼承方便地跟隨對象。

靜態方法的一個例子是str.maketrans ,它是從Python 3中的string模塊移出的。它使轉換錶適合str.translate 。 從字符串的實例中使用時看起來確實很傻,如下所示,但是從string模塊導入函數是相當笨拙的,能夠從類中調用它是很好的,就像在str.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

在python 2中,您必須從越來越不實用的字符串模塊中導入此函數:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

類方法

類方法類似於實例方法,因為它採用隱式的第一個參數,但不是採用實例,而是採用類。 通常這些用作替代構造函數以獲得更好的語義用法,並且它將支持繼承。

內置類方法的最典型示例是dict.fromkeys 。 它被用作dict的替代構造函數(非常適合當你知道你的鍵是什麼並且想要它們的默認值時。)

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

當我們繼承dict時,我們可以使用相同的構造函數,它創建子類的實例。

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

有關替代構造函數的其他類似示例,請參閱pandas源代碼 ,另請參閱有關classmethodstaticmethod的官方Python文檔。


@classmethod:可用於創建對該類創建的所有實例的共享全局訪問.....如更新多個用戶的記錄....我特別發現它在創建單例時也使用了ful ...: )

@static方法:與關聯的類或實例無關......但為了便於閱讀,可以使用靜態方法


關於如何在Python中使用靜態,類或抽象方法的權威指南是本主題的一個很好的鏈接,並總結如下。

@staticmethod函數只不過是在類中定義的函數。 它可以在不首先實例化類的情況下調用。 它的定義是通過繼承不可變的。

  • Python不必實例化對象的綁定方法。
  • 它簡化了代碼的可讀性,並且它不依賴於對象本身的狀態;

@classmethod函數也可以在不實例化類的情況下調用,但它的定義遵循Sub類,而不是Parent類,通過繼承,可以被子類覆蓋。 那是因為@classmethod函數的第一個參數必須始終是cls (class)。

  • 工廠方法 ,用於為類創建實例,例如使用某種預處理。
  • 調用靜態方法的靜態方法 :如果在幾個靜態方法中拆分靜態方法,則不應對類名進行硬編碼,而應使用類方法

@staticmethod只是禁用默認函數作為方法描述符。 classmethod將您的函數包裝在一個可調用的容器中,該容器將對所屬類的引用作為第一個參數傳遞:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

事實上, classmethod具有運行時開銷,但可以訪問擁有類。 或者,我建議使用元類並將類方法放在該元類上:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

staticmethod是一種對調用它的類或實例一無所知的方法。 它只是獲取傳遞的參數,沒有隱含的第一個參數。 它在Python中基本沒用 - 您可以使用模塊函數而不是靜態方法。

另一方面,類方法是一種方法,它將調用它的類或它所調用的實例的類作為第一個參數傳遞。 當您希望該方法成為該類的工廠時,這很有用:因為它獲取了作為第一個參數調用的實際類,所以即使涉及子類,也可以始終實例化正確的類。 例如,觀察dict.fromkeys()在子類上調用時如何返回子類的實例:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 

官方python文檔:

@classmethod

類方法接收類作為隱式的第一個參數,就像實例方法接收實例一樣。 要聲明一個類方法,請使用此習語:

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

@classmethod表單是一個函數decorator - 有關詳細信息,請參閱函數定義中的函數定義說明。

它可以在類(例如Cf() )或實例(例如C().f() )上調用。 除了類之外,該實例被忽略。 如果為派生類調用類方法,則派生類對象將作為隱含的第一個參數傳遞。

類方法與C ++或Java靜態方法不同。 如果需要,請參閱本節中的staticmethod()

@staticmethod

靜態方法不會接收隱式的第一個參數。 要聲明靜態方法,請使用此慣用法:

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

@staticmethod表單是一個函數decorator - 有關詳細信息,請參閱函數定義中的函數定義說明。

它可以在類(例如Cf() )或實例(例如C().f() )上調用。 除了類之外,該實例被忽略。

Python中的靜態方法與Java或C ++中的靜態方法類似。 有關更高級的概念,請參閱本節中的classmethod()


也許一些示例代碼會有所幫助:注意fooclass_foostatic_foo的調用簽名的static_foo

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)

    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)"%(cls,x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)"%x    

a=A()

下面是對象實例調用方法的常用方法。 對象實例a隱式傳遞為第一個參數。

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

使用classmethods ,對象實例的類隱式傳遞為第一個參數而不是self

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

您也可以使用該類調用class_foo 。 實際上,如果您將某些東西定義為類方法,那可能是因為您打算從類而不是類實例中調用它。 A.foo(1)會引發一個TypeError,但是A.class_foo(1)工作正常:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

人們發現類方法的一個用途是創建可繼承的替代構造函數 。

對於staticmethodsself (對象實例)和cls (類)都不會作為第一個參數隱式傳遞。 它們的行為類似於普通函數,除了您可以從實例或類中調用它們:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Staticmethod用於將與類有邏輯連接的函數分組到類中。

foo只是一個函數,但是當你調用a.foo時,你不僅僅得到函數,你得到函數的“部分應用”版本,對象實例作為函數的第一個參數綁定。 foo需要2個參數,而a.foo只需要1個參數。

afoo 。 這就是下面的“綁定”一詞的含義:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

使用a.class_fooa不綁定到class_foo ,而是將類A綁定到class_foo

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

在這裡,使用a.static_foo方法,即使它是一個方法, a.static_foo只返回一個沒有參數綁定的良好'ole函數。 static_foo需要1個參數,而a.static_foo需要1個參數。

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

當然,當你用類A調用static_foo時會發生同樣的事情。

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

在Python 2.4中添加了@decorators如果您使用的是python <2.4,則可以使用classmethod()和staticmethod()函數。

例如,如果要創建工廠方法(一個函數根據它獲取的參數返回類的不同實現的實例),您可以執行以下操作:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

還要注意這是使用classmethod和靜態方法的一個很好的例子。靜態方法顯然屬於類,因為它在內部使用類Cluster。 classmethod只需要有關類的信息,而不需要對象的實例。

使_is_cluster_for方法成為類方法的另一個好處是,子類可以決定更改它的實現,可能因為它非常通用並且可以處理多種類型的集群,所以只檢查類的名稱是不夠的。


我將嘗試使用示例來解釋基本差異。

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - 我們可以直接調用static和classmethods而無需初始化

# A.run_self() #  wrong
A.run_static()
A.run_class()

2-靜態方法不能調用self方法但可以調用其他靜態方法和class方法

3-靜態方法屬於類,根本不使用對象。

4-類方法不是綁定到對象而是綁定到類。


我的貢獻證明了@classmethod@staticmethod和實例方法之間的區別,包括實例如何間接調用@staticmethod 。 但是,不是從實例間接調用@staticmethod ,而是將其@staticmethod私有可能更“ @staticmethod ”。 從私有方法中獲取某些東西並沒有在這裡展示,但它基本上是相同的概念。

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

我開始用C ++學習編程語言,然後是Java,然後是Python,所以這個問題也困擾了我,直到我理解了每個的簡單用法。

類方法: Python與Java不同,C ++沒有構造函數重載。 所以要實現這一點,你可以使用classmethod 。 下面的例子將解釋這一點

我們假設我們有一個Person類,它接受兩個參數first_namelast_name並創建Person的實例。

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

現在,如果需求只需要使用單個名稱創建一個類,只需要一個first_name就不能在python中執行類似的操作。

當您嘗試創建對象(實例)時,這將給您一個錯誤。

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

但是,您可以使用@classmethod實現相同的@classmethod ,如下所述

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

靜態方法::這很簡單,它沒有綁定到實例或類,你只需使用類名稱調用它。

因此,在上面的示例中,您需要驗證first_name不應超過20個字符,您只需執行此操作即可。

@staticmethod  
def validate_name(name):
    return len(name) <= 20

你可以簡單地使用Class Name調用

Person.validate_name("Gaurang Shah")

要決定是否使用@staticmethod@classmethod你必須查看你的方法。 如果您的方法訪問類中的其他變量/方法,則使用@classmethod 。 另一方面,如果你的方法沒有接觸到類的任何其他部分,那麼使用@staticmethod。

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

關於staticmethod vs classmethod的另一個考慮因素是繼承。 假設您有以下課程:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

然後你想要覆蓋子類中的bar()

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

這有效,但請注意,現在子類( Foo2 )中的bar()實現無法再利用該類特定的任何內容。 例如,假設Foo2有一個名為magic()的方法,你想在bar()Foo2實現中使用它:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

這裡的解決方法是在bar()調用Foo2.magic() ,但隨後你會重複自己(如果Foo2的名稱發生了變化,你將不得不記得更新那個bar()方法)。

對我來說,這是對開放/封閉原則的輕微違反,因為在Foo做出的決定正在影響您在派生類中重構公共代碼的能力(即它不太適合擴展)。 如果bar()是一個classmethod我們會沒事的:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

給: In Foo2 MAGIC


分析@staticmethod 字面上提供不同的見解。

類的常規方法是隱式動態方法,它將實例作為第一個參數。
相反,staticmethod不會將實例作為第一個參數,因此稱為“靜態”

靜態方法確實是這樣的正常函數,與類定義之外的函數相同。
幸運的是,它只是為了在應用它的地方站得更近,或者你可以滾動來找到它。


顧名思義,類方法用於更改類而不是對象。要對類進行更改,它們將修改類屬性(而不是對象屬性),因為這是更新類的方式。這就是類方法將類(通常用'cls'表示)作為第一個參數的原因。

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

另一方面,靜態方法用於執行未綁定到類的功能,即它們不會讀取或寫入類變量。因此,靜態方法不會將類作為參數。使用它們使類可以執行與類的目的無直接關係的功能。

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

#!/usr/bin/python
#coding:utf-8

class Demo(object):
    def __init__(self,x):
        self.x = x

    @classmethod
    def addone(self, x):
        return x+1

    @staticmethod
    def addtwo(x):
        return x+2

    def addthree(self, x):
        return x+3

def main():
    print Demo.addone(2)
    print Demo.addtwo(2)

    #print Demo.addthree(2) #Error
    demo = Demo(2)
    print demo.addthree(2)


if __name__ == '__main__':
    main()




python-decorators