Sharing

2012年4月29日 星期日

type of method in Python


之前在 Study Python 時, 乎略掉了一個東西, 叫 classmethod, 一開始一直以為他就是在 class 中的 method 的預設型式, 直到最近才發現, 原來 python 總共有三種型式的 class


  • instancemethod 
  • classmethod
  • staticmetho

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

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

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


如果寫過 Java, 大概可以輕易了解 instance method 及 static method 的差別, 如果該 function 不需要創造一個 instance 就可以使用, 類似 utility 的話, 那就使用 static method, 同時方便使用者使用, 也方便在 method 的整理, 可以把他歸類到某個 Class 之下.

不過 classmethod 和 staticmethod 有什麼差別就比較難理解. 兩者同樣都不需要 instance 就可以直接執行, 唯一的差別是在於第一個參數, classmethod 第一個參數保留 class 資訊, staticmethod 則完全不保留, 所以最表面的說法, 當你這個 function 需要用到 class 資訊時, 就應該用 classmethod, 否則就用 staticmethod. 不過這樣講還是太抽像, 無法想像有什麼狀況是一定需要用到 class 資訊.

從網路上找了一些範例,


第一個狀況是 "make it called for static method for a derived class", 某些狀況你寫了個工具, 但這個工具還是必須要根據使用者真實的 class 類別來做操作, 那這個狀況下, 你會希望真的執行到繼承 class 的函式, 而不是自己的. 可以幫你助你實作 static method 時, 把共同的部份抽出來, 需要差異化的部份就由各個類別自行去實作, 以下面這個為例, 共同的部份寫在 m2(), 差異化的部份寫在 m1()

class A(object):
    @staticmethod
    def m1(): 
        print "A.m1"

    @classmethod
    def m2(cls):
        cls.m1()

class B(object):
    @staticmethod
    def m1(): 
        print "B.m1"


SQLAlchemy 也有這樣的例子,
http://hg.sqlalchemy.org/sqlalchemy/file/230819db717a/lib/sqlalchemy/dialects/mssql/pymssql.py



第二個狀況是 Factory methods (alternative constructors) are indeed a classic example of class methods. 因為 python 沒有 overloading, 不像 Java 可以同時有多種 initialize 的方式, 所以有這種需求時, 就必須利用 classmethod 的特性來做到這件事. 我想 Design Pattern 中有一些需要控制 Constructor / Destructor 時, (Singleton, Number Control) 可能都是這樣實作.

class ABC(object):

        @classmethod
        def from_file(cls, file):
                instance = cls("file")
                instance.file = file
                return instance

        @classmethod
        def from_console(cls, io):
                raise NotImplemented()

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

class UniqueIdentifier(object):

    value = 0

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

    @classmethod
    def produce(cls):
        instance = cls(cls.value)
        cls.value += 1
        return instance

class FunkyUniqueIdentifier(UniqueIdentifier):

    @classmethod
    def produce(cls):
        instance = super(FunkyUniqueIdentifier, cls).produce()
        instance.name = "Funky %s" % instance.name
        return instance


Reference:
http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python

http://stackoverflow.com/questions/38238/what-are-class-methods-in-python-for

http://video.google.com/videoplay?docid=7760178035196894549

沒有留言: