关于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'>

 

posted @ 2017-05-12 21:29  BlackMatrix  阅读(41454)  评论(1编辑  收藏  举报