<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么 classmethod 比 staticmethod 更受寵?

          共 5158字,需瀏覽 11分鐘

           ·

          2021-12-29 16:05

          我們知道,classmethod 和 staticmethod 都可以作為函數(shù)的裝飾器,都可用于不涉及類的成員變量的方法,但是你查一下 Python 標(biāo)準(zhǔn)庫就會知道 classmethod 使用的次數(shù)(1052)要遠(yuǎn)遠(yuǎn)多于 staticmethod(539),這是為什么呢?

          這就要從 staticmethod 和 classmethod 的區(qū)別說起。

          1、從調(diào)用形式上看,二者用法差不多

          先說下什么是類,什么是實例,比如說 a = A(),那么 A 就是類,a 就是實例。

          從定義形式上看,clasmethod 的第一個參數(shù)是 cls,代表類本身,普通方法的第一個參數(shù)是 self,代表實例本身,staticmethod 的參數(shù)和普通函數(shù)沒有區(qū)別。

          從調(diào)用形式上看,staticmethod 和 classmethod 都支持類直接調(diào)用和實例調(diào)用。

          class?MyClass:
          ????def?method(self):
          ????????"""
          ????????Instance?methods?need?a?class?instance?and
          ????????can?access?the?instance?through?`self`.
          ????????"""

          ????????return?'instance?method?called',?self

          ????@classmethod
          ????def?classmethod(cls):
          ????????"""
          ????????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`.
          ????????"""

          ????????return?'class?method?called',?cls

          ????@staticmethod
          ????def?staticmethod():
          ????????"""
          ????????Static?methods?don't?have?access?to?`cls`?or?`self`.
          ????????They?work?like?regular?functions?but?belong?to
          ????????the?class's?namespace.
          ????????"""

          ????????return?'static?method?called'

          #?All?methods?types?can?be
          #?called?on?a?class?instance:
          >>>?obj?=?MyClass()
          >>>?obj.method()
          ('instance?method?called',?0x1019381b8>)
          >>>?obj.classmethod()
          ('class?method?called',?<class?MyClass?at?0x101a2f4c8>)
          >>>?obj.staticmethod()
          'static?method?called'

          #?Calling?instance?methods?fails
          #?if?we?only?have?the?class?object:

          >>>?MyClass.classmethod()
          ('class?method?called',?<class?MyClass?at?0x101a2f4c8>)
          >>>?MyClass.staticmethod()
          'static?method?called'

          2、先說說 staticmethod。

          如果一個類的函數(shù)上面加上了 staticmethod,通常就表示這個函數(shù)的計算不涉及類的變量,不需要類的實例化就可以使用,也就是說該函數(shù)和這個類的關(guān)系不是很近,換句話說,使用 staticmethod 裝飾的函數(shù),也可以定義在類的外面。我有時候會糾結(jié)到底放在類里面使用 staticmethod,還是放在 utils.py 中單獨寫一個函數(shù)?比如下面的 Calendar 類:

          class?Calendar:
          ????def?__init__(self):
          ????????self.events?=?[]

          ????def?add_event(self,?event):
          ????????self.events.append(event)

          ????@staticmethod
          ????def?is_weekend(dt:datetime):
          ????????return?dt.weekday()?>?4

          if?__name__?==?'__main__':
          ????print(Calendar.is_weekend(datetime(2021,12,27)))
          ????#output:?False

          里面的函數(shù) is_weekend?用來判斷某一天是否是周末,就可以定義在?Calendar 的外面作為公共方法,這樣在使用該函數(shù)時就不需要再加上 Calendar 這個類名。

          但是有些情況最好定義在類的里面,那就是這個函數(shù)離開了類的上下文,就不知道該怎么調(diào)用了,比如說下面這個類,用來判斷矩陣是否可以相乘,更易讀的調(diào)用形式是 Matrix.can_multiply:

          from?dataclasses?import?dataclass
          @dataclass
          class?Matrix:
          ????shape:?tuple[int,?int]?#?python3.9?之后支持這種類型聲明的寫法

          ????@staticmethod
          ????def?can_multiply(a,?b):
          ????????n,?m?=?a.shape
          ????????k,?l?=?b.shape
          ????????return?m?==?k

          3、再說說 classmethod。

          首先我們從 clasmethod 的形式上來理解,它的第一個參數(shù)是 cls,代表類本身,也就是說,我們可以在 classmethod 函數(shù)里面調(diào)用類的構(gòu)造函數(shù) cls(),從而生成一個新的實例。從這一點,可以推斷出它的使用場景:

          • 當(dāng)我們需要再次調(diào)用構(gòu)造函數(shù)時,也就是創(chuàng)建新的實例對象時
          • 需要不修改現(xiàn)有實例的情況下返回一個新的實例

          比如下面的代碼:

          class?Stream:

          ????def?extend(self,?other):
          ????????#?modify?self?using?other
          ????????...

          ????@classmethod
          ????def?from_file(cls,?file):
          ????????...

          ????@classmethod
          ????def?concatenate(cls,?*streams):
          ????????s?=?cls()
          ????????for?stream?in?streams:
          ????????????s.extend(stream)
          ????????return?s

          steam?=?Steam()

          當(dāng)我們調(diào)用 steam.extend 函數(shù)時候會修改 steam 本身,而調(diào)用 concatenate 時會返回一個新的實例對象,而不會修改 steam 本身。

          4、本質(zhì)區(qū)別

          我們可以嘗試自己實現(xiàn)一下 classmethod 和 staticmethod 這兩個裝飾器,來看看他們的本質(zhì)區(qū)別:

          class?StaticMethod:
          ????def?__init__(self,?func):
          ????????self.func?=?func

          ????def?__get__(self,?instance,?owner):
          ????????return?self.func

          ????def?__call__(self,?*args,?**kwargs):??#?New?in?Python?3.10
          ????????return?self.func(*args,?**kwargs)


          class?ClassMethod:
          ????def?__init__(self,?func):
          ????????self.func?=?func

          ????def?__get__(self,?instance,?owner):
          ????????return?self.func.__get__(owner,?type(owner))

          class?A:
          ????def?normal(self,?*args,?**kwargs):
          ????????print(f"normal({self=},?{args=},?{kwargs=})")

          ????@staticmethod
          ????def?f1(*args,?**kwargs):
          ????????print(f"f1({args=},?{kwargs=})")

          ????@StaticMethod
          ????def?f2(*args,?**kwargs):
          ????????print(f"f2({args=},?{kwargs=})")

          ????@classmethod
          ????def?g1(cls,?*args,?**kwargs):
          ????????print(f"g1({cls=},?{args=},?{kwargs=})")

          ????@ClassMethod
          ????def?g2(cls,?*args,?**kwargs):
          ????????print(f"g2({cls=},?{args=},?{kwargs=})")


          def?staticmethod_example():
          ????A.f1()
          ????A.f2()

          ????A().f1()
          ????A().f2()

          ????print(f'{A.f1=}')
          ????print(f'{A.f2=}')

          ????print(A().f1)
          ????print(A().f2)

          ????print(f'{type(A.f1)=}')
          ????print(f'{type(A.f2)=}')


          def?main():
          ????A.f1()
          ????A.f2()

          ????A().f1()
          ????A().f2()

          ????A.g1()
          ????A.g2()

          ????A().g1()
          ????A().g2()

          ????print(f'{A.f1=}')
          ????print(f'{A.f2=}')

          ????print(f'{A().f1=}')
          ????print(f'{A().f2=}')

          ????print(f'{type(A.f1)=}')
          ????print(f'{type(A.f2)=}')


          ????print(f'{A.g1=}')
          ????print(f'{A.g2=}')

          ????print(f'{A().g1=}')
          ????print(f'{A().g2=}')

          ????print(f'{type(A.g1)=}')
          ????print(f'{type(A.g2)=}')

          if?__name__?==?"__main__":
          ?main()

          上面的類 StaticMethod?的作用相當(dāng)于裝飾器 staticmethod,類ClassMethod 相當(dāng)于裝飾器 classmethod。代碼的執(zhí)行結(jié)果如下:

          可以看出,StaticMethodClassMethod 的作用和標(biāo)準(zhǔn)庫的效果是一樣的,也可以看出 classmethod 和 staticmethod 的區(qū)別就在于 classmethod 帶有類的信息,可以調(diào)用類的構(gòu)造函數(shù),在編程中具有更好的擴(kuò)展性。

          最后的話

          回答本文最初的問題,為什么 classmethod 更受標(biāo)準(zhǔn)庫的寵愛?是因為 classmethod 可以取代 staticmethod 的作用,而反過來卻不行。也就是說凡是使用 staticmethod 的地方,把 staticmethod 換成 classmethod,然后把函數(shù)增加第一個參數(shù) cls,后面調(diào)用的代碼可以不變,反過來卻不行,也就是說 classmethod 的兼容性更好。

          另一方面,classmethod 可以在內(nèi)部再次調(diào)用類的構(gòu)造函數(shù),可以不修改現(xiàn)有實例生成新的實例,具有更強的靈活性和可擴(kuò)展性,因此更受寵愛,當(dāng)然這只是我的拙見,如果你有不同的想法,可以留言討論哈。

          如果有幫助,還請點贊、在看、轉(zhuǎn)發(fā)、關(guān)注支持,感謝閱讀。

          關(guān)注公眾號后,回復(fù)「送書」抽兩本《代碼大全2》,就這兩天了不要錯過哦。


          瀏覽 59
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产成人无码免费视频 | 91在线成人免费 | 天天AV天天爽 | 欧美国产大屌操疼 | 黄色性爱免费视频 |