關於Python的函數(Method)與方法(Function)


先上結論:

  1. 函數(function)是Python中一個可調用對象(callable), 方法(method)是一種特殊的函數。
  2. 一個可調用對象是方法和函數,和這個對象無關,僅和這個對象是否與類或實例綁定有關(bound method)。
  3. 實例方法,在類中未和類綁定,是函數;在實例中,此實例方法與實例綁定,即變成方法。
  4. 靜態方法沒有和任何類或實例綁定,所以靜態方法是個函數。
  5. 裝飾器不會改變被裝飾函數或方法的類型。
  6. 類實現__call__方法,其實例也不會變成方法或函數,依舊是類的實例。
  7. 使用callalble() 只能判斷對象是否可調用,不能判斷是不是函數或方法。
  8. 判斷對象是函數或方法應該使用type(obj)。

 

下面,使用一些例子,對上述結論進行檢測、驗證。

測試的例子中,我們創建一個裝飾器、一個函數及一個類,這個類包含:實例方法、類方法、靜態方法及被裝飾器裝飾的方法。

完整代碼: https://github.com/blackmatrix7/python-learning/blob/master/function_/method_func.py

def test_decorator(func):
    """
    裝飾器,測試使用,無功能
    :param func:
    :return:
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper


def the_function():
    """
    函數
    :return: 
    """
    pass


class TheClass:

    def __call__(self, *args, **kwargs):
        return self

    @classmethod
    def class_method(cls):
        """
        類方法
        :return: 
        """
        pass

    def instance_method(self):
        """
        實例方法
        """
        return self

    @staticmethod
    def static_method():
        """
        靜態方法
        :return: 
        """
        pass

    @test_decorator
    def decorated_func(self):
        pass

 

先對類方法和實例方法的類型進行檢測(注釋部分為輸出結果,下同)。

從運行結果上看,類方法和實例方法都是方法(method)。

同時,通過直接打印類方法和實例方法,可以得知它們都是綁定方法(bound method)。

    print('class_method type {type} '.format(type=type(TheClass.class_method)))
    # class_method type <class_ 'method'>
    print('instance_method type {type} '.format(type=type(the_class.instance_method)))
    # instance_method type <class_ 'method'>
    print(TheClass.class_method)
    # <bound method TheClass.class_method of <class '__main__.TheClass'>>
    print(the_class.instance_method)
    # <bound method TheClass.instance_method of <__main__.TheClass object at 0x00000275DEB3FC50>>

如果僅通過上述運行結果,就得出類方法和實例方法都是方法,那么就錯了。

再看下面的代碼,同一個對象instance_method,之前還是方法(method),現在已經變成函數(function)。

    print('instance_method type {type} '.format(type=type(TheClass.instance_method)))
    # instance_method type <class 'function'>
    print(TheClass.instance_method)
    # <function TheClass.instance_method at 0x00000275DEB3D840>

第二段代碼,和第一段代碼的不同之處:第一段代碼是通過實例,去訪問實例方法;而第二段代碼,是通過類去訪問實例方法。

同一個可調用對象,僅僅是訪問的方式不同,就能從方法變為函數。

因為,在類中的實例方法,並沒有和類建立綁定關系,所以它是方法。當類進行實例化時,會將實例方法,綁定到類創建出的實例上,此時實例方法與實例形成綁定關系,從函數變為方法。

所以,可以得到開頭的第2、3條結論:

一個可調用對象是方法和函數,和這個對象無關,僅和這個對象是否與類或實例綁定有關(bound method)。

實例方法,在類中未和類綁定,是函數;在實例中,此實例方法與實例綁定,即變成方法。

 

接着對靜態方法進行檢測,有了之前的結論,就很容易理解為什么靜態方法是函數而不是方法:因為它不會和類或實例進行綁定。

    print('static_method type {type} '.format(type=type(the_class.static_method)))
    # static_method type <class_ 'function_'>
    print('static_method type {type} '.format(type=type(TheClass.static_method)))
    # static_method type <class 'function'>
    print(TheClass.static_method, the_class.static_method, sep='\n')
    # <function TheClass.static_method at 0x0000024BC5EAD950>
    # <function TheClass.static_method at 0x0000024BC5EAD950>

 

而對於一個函數,因為不會和任何類或實例綁定(除非使用MethodType將函數綁定到某個實例上),必然不是方法。

    print('the_function type {type} '.format(type=type(the_function)))
    # the_function type <class_ 'function_'>

 

對於裝飾器,本身也不會改變被裝飾對象的類型

    # 裝飾器本身也是個函數
    print('test_decorator type {type} '.format(type=type(test_decorator)))
    # test_decorator type <class_ 'function_'>

    # 將裝飾器裝飾器到實例方法上
    # 檢查被裝飾的方法的類型
    print('decorated_func type {type} '.format(type=type(the_class.decorated_func)))
    # decorated_func type <class_ 'method'>
    # 從測試結果得知,裝飾器不會影響被裝飾方法或函數的類型

 

如果一個類,實現__call__方法,那么其實例會變為可調用對象,但這個這個實例依舊不是函數或方法

    # 如果類實現__call__方法
    # 執行結果True 其實例變為可調用對象
    print('class_instance callable {callable} '.format(callable=callable(the_class)))
    # 實例的類型依舊是這個類,而不會變成函數或方法
    print('class_instance type {type} '.format(type=type(the_class)))
    # class_instance type <class_ '__main__.TheClass'>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM