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

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

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

非绑定方法 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_绑定方法和非绑定方法)