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

浏览: 1075

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

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

非绑定方法 unbound method

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


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
  • 如果用类直接调用方法则会报错:

绑定方法 bound method

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

描述器

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

Foo.__dict__["bar"]   # bar 是函数对象


  • 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)


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

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


类方法

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;方法可以通过实例或类进行调用。
推荐 0
本文由 皮大大 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册