Python札记33_绑定方法和非绑定方法

作者: 皮皮大 | 来源:发表于2019-07-06 20:15 被阅读1次

    在类中除了属性还有方法,通常情况下通过实例来进行调用类的方法。本篇札记讲解四个方面进行:

    • 绑定方法
    • 非绑定方法
    • 静态方法
    • 类方法

    非绑定方法 unbound method

    通过类的名字直接来调用方法对象(函数对象),叫做非绑定方法。其中Foo是类,bar是类中的方法。类名字---->方法对象

    image.png
    class Foo:
        def bar(self):  # 第一个参数必须是self
            print("this is a method of class")
    f = Foo()   # 调用类
    f.bar()     # 调用类中方法(类中的函数也称之为方法)
    this is a method of class
    
    • 类中的函数也称之为方法
    • 当建立了实例f之后,当实例调用这个方法bar()的时候,Python解释器把实例已经作为第一个参数隐式地传给了该方法。
    • self就是一个实例!!!!不需要显式地写出self参数
    # 实例显式地传给方法
    Foo.bar(f)  
    this is a method of class
    
    Foo.bar()  # 通过类直接调用方法,不传递参数报错,缺少self
    
    • 实例化之后,self和实例f是一样的
    • 通常在类中使用self,在实例中使用f
    • 如果用类直接调用方法则会报错:
    image.png

    绑定方法 bound method

    绑定方法指的是通过实例得到方法对象的过程。实例---->方法对象

    描述器

    在类的属性中有个__dict__的特殊属性,用来查看内部信息:

    Foo.__dict__["bar"]   # bar 是函数对象
    
    image.png
    • Python中有几个比较特殊的方法:__get__()、__set__()、__deleta__(),通常将具有这些方法的对象称之为描述器。描述器的使用如下:
      descr.get(self, obj, type=None)--->value
      descr.set(self, obj,value)--->None
      descr.delete(self, obj)--->None
    • 描述器的使用:
      Foo.dict['bar'].get(None, Foo)
    image.png

    结果:描述器的返回结果和Foo.bar是相同的。其中self指定为None,表示没有给定实例。

    Foo.__dict__["bar"].__get__(f, Foo) # 给定实例f;与实例得到方法对象结果相同
    
    image.png

    类方法

    lass Foo:   # 定义一个类
        lang = 'Java'  # 定义类的属性lang = "java"
        def __init__(self):  # 初始化方法__init__
            self.lang = 'python' # 实例属性,self是实例对象,属性数lang = "python"
            
    def get_class_attr(cls):  
        return cls.lang   # 引用的对象具有lang属性
        
    if __name__ == "__main__":
        print("Foo.lang(类属性值)", Foo.lang)  # 输出类lang属性值
        r = get_class_attr(Foo)   # 调用get_class_attr()方法,其中参数Foo刚好有lang属性
        print("get class attr:", r)  
        f = Foo()  # 创建实例f
        print("instance attribute(实例属性值)", f.lang)  # 参数实例的lang属性值
        
    

    结果:

    Foo.lang(类属性值) Java
    get class attr: Java
    instance attribute(实例属性值) python
    

    缺陷:

    • get_class_attr()方法传入的参数不是随意的,必须具有lang属性
    • 此做法使得函数和类的耦合性太强,不利于后期维护,考虑将函数和类融为一体,直接将函数放在类里面。

    使用装饰器来解决上面的问题

    class Foo:   # 定义一个类
        lang = 'Java'  # 定义类的属性lang = "java"
        def __init__(self):  # 初始化方法__init__
            self.lang = 'python' # 实例属性,self是实例对象,属性数lang = "python"
            
        # 修改部分,加上装饰器,@classmethod装饰的函数第一个参数不是self
        @classmethod
        def get_class_attr(cls):  
            return cls.lang   # 引用的对象具有lang属性
        
    if __name__ == "__main__":
        print("Foo.lang(类属性值)", Foo.lang)  # 输出类lang属性值
        r = get_class_attr(Foo)   # 调用get_class_attr()方法,其中参数Foo刚好有lang属性
        print("get class attr:", r)  
        f = Foo()  # 创建实例f
        print("instance attribute(实例属性值):", f.lang)  # 参数实例的lang属性值
        print("instance get_class_attr:", f.get_class_attr())
    
    Foo.lang(类属性值) Java
    get class attr: Java
    instance attribute(实例属性值): python
    instance get_class_attr: Java
    

    结果:通过实例和类执行get_class_attr()方法得到的结果都是Java

    • 类方法:在类里面定义的方法,该方法由@classmethod所装饰第一个参数cls(约定俗称第一个参数为cls)的引用对象为类本身`。

    静态方法

    下面是一个有待优化的代码,因为类Foo里面使用了外面定义的函数select函数,不便于维护

    import random
    
    def select(n):
        a = random.randint(0, 100)    # 生成一个随机数
        return a - n > 0
    
    class Foo:
        def __init__(self, name):     # 初始化函数
            self.name = name    # 实例self的 name属性为name
            
        def get_name(self, age):    # 第一参数必须是self
            if select(age) is True:     #   就是返回值大于0
                return self.name   
            else:
                return "this name is secret."
        
    if __name__ == "__main__":
        f = Foo("Peter")
        name = f.get_name(27)
        print(name)
    

    优化代码如下

    import random
    
    class Foo:   # 定义类,首字母大写 
        def __init__(self, name):  # 初始化函数
            self.name = name   # self实例的name属性为name
        
        def get_name(self, age):   # 将原来在外面的函数放在里面
            if self.select(age):   # 直接通过实例进行调用;也可以通过类进行调用:Foo.select()
                return self.name   # 返回实例self的name属性值
            else:
                return "the name is secret"
            
       # 增加装饰器 
        @staticmethod
        def select(n):   # 虽然在类里面,但是第一个参数不是self
            a = random.randint(1,100)
            return a - n > 0
        
    if __name__ == "__main__":
        f = Foo("luolaoshi")
        name = f.get_name(22)
        print(name)
    

    小结:

    • 将原来在类外面的函数放到里面
    • 移动到里面的方法的第一个参数不是self
    • 移到类的命名空间之内,前面必须加上@staticmethod进行装饰
    • 调用可以通过实例或者进行调用

    下面对上面的四种方法进行一个小结:

    • 绑定方法:实例--->对象方法
    • 非绑定方法:类--->对象方法
    • 类方法:@classmethod,将方法写在类里面,该方法的第一个参数不是self,通常用cls。
    • 静态方法:@staticmethod,将方法移动到类里面,位于类的命名空间之内。第一个参数不是self;方法可以通过实例或类进行调用。

    相关文章

      网友评论

        本文标题:Python札记33_绑定方法和非绑定方法

        本文链接:https://www.haomeiwen.com/subject/hvsmhctx.html