前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第27天面向对象之反射,绑定方法,特定的

第27天面向对象之反射,绑定方法,特定的

作者头像
py3study
发布2020-01-20 11:50:16
4980
发布2020-01-20 11:50:16
举报
文章被收录于专栏:python3python3

绑定方法与非绑定方法

代码语言:javascript
复制
绑定方法(其实并没有这样的说法,只是为了解释三个装饰器而说出来的比较容易记名词)
  绑定方法的核心就是,谁绑定的就应该给谁来调用,谁来调用我们就会将谁作为第一个参数传递进去。
  绑定对象方法:应该被对象来调用,python会自动的将对象当做第一个参数传递进来,__init__方法就是这个道理
  绑定类的方法:应该被类来调用,python会自动的将类当做第一个参数传递进来。

非绑定方法
  既不绑定对象,也不绑定类,就是一个普通的函数,不会自动传值。
三个装饰器
  classmathod: 指定这个方法是绑定类的方法
  staticmethod:指定这个方法是非绑定方法
  property: 和绑定方法没什么关系,只是他也是一个装饰器,就放在这里来一块说了

注意:绑定方法理论上是谁绑定的就由谁来调用,但是并不是强制的。

如何用绑定方法和非绑定方法

1. 对象绑定方法特点

代码语言:javascript
复制
1. 对象去调用的时候,自动传值
2. 类去调用的时候,就是一个普通的函数,需要自己去传值
代码语言:javascript
复制
class Foo:
    # 类中的函数如果没有加入装饰器默认的就是绑定对象的方法
    def func1(self):
        print('func1', self)

    # 通过装饰器classmethod定义了一个绑定类的方法
    @classmethod
    def func2(cls):
        print('func2', cls)

    # 通过装饰器staticmethod定义了一个非绑定方法
    @staticmethod
    def func3(x, y):
        print('func3', x, y)

obj = Foo()
# 1. 绑定对象的方法应该用对象去调用
# 2. 对象调用的时候会自动的将对象作为第一个参数传递进去
obj.func1()

# 如果用类去调用,就相当于一个普通的函数,需要自己去传值
Foo.func1(obj)

View Code

2. 类绑定方法的特点

代码语言:javascript
复制
无论是对象还是累调用的时候都是自动的把类当做第一个参数传递进去
代码语言:javascript
复制
class Foo:
    # 类中的函数如果没有加入装饰器默认的就是绑定对象的方法
    def func1(self):
        print('func1', self)

    # 通过装饰器classmethod定义了一个绑定类的方法
    @classmethod
    def func2(cls):
        print('func2', cls)

    # 通过装饰器staticmethod定义了一个非绑定方法
    @staticmethod
    def func3(x, y):
        print('func3', x, y)

obj = Foo()
obj.func2()
Foo.func2()

# 结果:打印出来的结果是一样的
# func2 <class '__main__.Foo'>
# func2 <class '__main__.Foo'>

View Code

3. 非绑定方法的特点

代码语言:javascript
复制
无论是类还是对象去调用的时候都是作为普通函数,不会进行自动的传值
代码语言:javascript
复制
class Foo:
    # 类中的函数如果没有加入装饰器默认的就是绑定对象的方法
    def func1(self):
        print('func1', self)

    # 通过装饰器classmethod定义了一个绑定类的方法
    @classmethod
    def func2(cls):
        print('func2', cls)

    # 通过装饰器staticmethod定义了一个非绑定方法
    @staticmethod
    def func3(x, y):
        print('func3', x, y)



obj = Foo()
obj.func3(1, 2)
Foo.func3(1, 2)
# 打印出来的结果也是一样的
# func3 1 2
# func3 1 2

View Code

应用场景

  类中的方法为什么要这么麻烦分这么多的类型呢?当然是有运用场景了,在写类中方法的时候具体的应用设置成绑定方法还是非绑定方法主要取决于我们方法代码中是否要用到对象或者是类。下面举一个小小的案例说明一下

代码语言:javascript
复制
class Mysql:
    # 之前的时候我们都是直接用init函数,但是现在有了几个不同类型的方法,我们就要考虑一下为什么要使用默认的对象绑定方法
    # 因为__init__函数中我们要用到对象,并且要给对象赋予不同的属性,所以我们把这个方法设置成了对象绑定方法
    def __init__(self, ip, port):
        self.id = self.create_id()
        self.ip = ip
        self.port = port
    # 这个查询方法也一样,我们需要通过传递一个对象来查看对象的属性,所以设置成了对象绑定方法
    def tell_info(self):
        print('<%s:%s>' %(self.ip, self.port))
    # 因为代码体中要用到类,所以定义这个方法为类绑定方法
    @classmethod
    def from_conf(cls):
        return cls(settings.IP, settings.PORT)
    
    # 因为此方法没有用到对象或者类,所以定义成一个静态方法
    @staticmethod
    def create_id():
        import uuid
        return uuid.uuid4()
    
import settings
# 这种方法是之后经常要用到的另外一种创建对象的方法
obj = Mysql.from_conf()

绑定方法和非绑定方法的应用案例

步骤一:现在我要定义一个mysql类,创建对象的时候我需要给它一个端口和ip,这个时候我可能会这样写

代码语言:javascript
复制
class Mysql:
    # 之前的时候我们都是直接用init函数,但是现在有了几个不同类型的方法,我们就要考虑一下为什么要使用默认的对象绑定方法
    # 因为__init__函数中我们要用到对象,并且要给对象赋予不同的属性,所以我们把这个方法设置成了对象绑定方法
    def __init__(self, name, age):
        self.name = name
        self.age = age 

obj = Mysql('1.1.1.1', 3306)

步骤二: 我现在我觉得直接输入值去创建对象太麻烦了, 我要从文件中读取值然后传递进来,因此我们创建了一个settings文件里面存的是ip和port,因此代码变成了这样

代码语言:javascript
复制
class Mysql:
    # 之前的时候我们都是直接用init函数,但是现在有了几个不同类型的方法,我们就要考虑一下为什么要使用默认的对象绑定方法
    # 因为__init__函数中我们要用到对象,并且要给对象赋予不同的属性,所以我们把这个方法设置成了对象绑定方法
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port
    # 这个查询方法也一样,我们需要通过传递一个对象来查看对象的属性,所以设置成了对象绑定方法
    def tell_info(self):
        print('<%s:%s>' %(self.ip, self.port))
import settings
obj = Mysql(settings.IP, settings.PORT)

步骤三: 现在每次通过传递参数去创建对象的方式我还是觉得太麻烦了,因此想着能不能重新创建一个方法对创建对象对象重新封装一下

代码语言:javascript
复制
# 应该写入这样一个函数,把之前创建对象的表达式放进去,但是这样子有一个问题,也就是当类名发生变化的时候,这个函数就不能用了
def from_conf():
    return Mysql(settings.IP, settings.PORT)
# 为了解决上面的问题,作为一个参数传递进去,这个参数本质上一个类,因此写到类中应该是一个绑定类的方法
def from_conf(xx):
    return xx(settings.IP, settings.PORT)
代码语言:javascript
复制
class Mysql:
    # 之前的时候我们都是直接用init函数,但是现在有了几个不同类型的方法,我们就要考虑一下为什么要使用默认的对象绑定方法
    # 因为__init__函数中我们要用到对象,并且要给对象赋予不同的属性,所以我们把这个方法设置成了对象绑定方法
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port
    # 这个查询方法也一样,我们需要通过传递一个对象来查看对象的属性,所以设置成了对象绑定方法
    def tell_info(self):
        print('<%s:%s>' %(self.ip, self.port))
    # 因为代码体中要用到类,所以定义这个方法为类绑定方法
    @classmethod
    def from_conf(cls):
        return cls(settings.IP, settings.PORT)
import settings
# 这种方法是之后经常要用到的另外一种创建对象的方法
obj = Mysql.from_conf()

步骤四: 除了上面的需求之外,我还希望我们可以给对象增加一个id值

代码语言:javascript
复制
class Mysql:
    # 之前的时候我们都是直接用init函数,但是现在有了几个不同类型的方法,我们就要考虑一下为什么要使用默认的对象绑定方法
    # 因为__init__函数中我们要用到对象,并且要给对象赋予不同的属性,所以我们把这个方法设置成了对象绑定方法
    def __init__(self, ip, port):
        self.id = self.create_id()
        self.ip = ip
        self.port = port
    # 这个查询方法也一样,我们需要通过传递一个对象来查看对象的属性,所以设置成了对象绑定方法
    def tell_info(self):
        print('<%s:%s>' %(self.ip, self.port))
    # 因为代码体中要用到类,所以定义这个方法为类绑定方法
    @classmethod
    def from_conf(cls):
        return cls(settings.IP, settings.PORT)
    
    # 因为此方法没有用到对象或者类,所以定义成一个静态方法
    @staticmethod
    def create_id():
        import uuid
        return uuid.uuid4()
    
import settings
# 这种方法是之后经常要用到的另外一种创建对象的方法
obj = Mysql.from_conf()

三种方法的使用场景案例

property装饰器

代码语言:javascript
复制
property装饰器
    主要作用是用来对于一些需要计算得到的属性,我们必须要通过一个方法去获得,但是这样用户去使用的时候就会添加一些不必要的障碍,为了解决这样的问题,通过property装饰器把类的方法属性转换成数据属性,使用者可以直接进行使用

案例:

代码语言:javascript
复制
BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86

我们首先想到的方法肯定是通过以下的方式:

代码语言:javascript
复制
class Bmi:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height
        self.bmi = weight / (height ** 2)  # 通过设置一个属性来直接计算bmi的值

    def tell_info(self):
        print('bmi:<%s>' % self.bmi)

b = Bmi('egon', 60, 1.5)
print(b.bmi)

问题一: 上面的这样操作看似是没有问题的,但是当我们进行修改其中的一个值得时候会发现bmi并没有跟着变化。为什么会出现这个问题,因为在bmi是在定义阶段的时候就已经计算出来了,之后对体重或者身高进行操作的时候并不会更改这个bmi的值,因为两者并没有什么关系了,现在。因此我们需要通过一个方法来计算这个方法,并且每次都通过这个方法去获取bmi的值。

代码语言:javascript
复制
class Bmi:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height
        self.bmi = weight / (height ** 2)

    def tell_info(self):
        print('bmi:<%s>' % self.bmi)

b = Bmi('egon', 60, 1.4)
print(b.bmi)
b.weight = 50
print(b.bmi)
# 问题所在
# 结果:
# 30.612244897959187
# 30.612244897959187

问题

代码语言:javascript
复制
class Bmi:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    def bmi(self):
        return self.weight / (self.height ** 2)

    def tell_info(self):
        print('bmi:<%s>' % self.bmi)

b = Bmi('egon', 60, 1.4)
print(b.bmi())  
b.weight = 50
print(b.bmi())

问题二:上面数据确实根据体重的改变而改变了,但是每次使用者在调用的时候都要家上一个括号,麻烦死了,这个时候就是property的用处了,它可以把函数属性转换成一个数据属性,使用者可以直接调用

代码语言:javascript
复制
class Bmi:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height
    
    # 通过property装饰器将方法转换成数据
    @property
    def bmi(self):
        return self.weight / (self.height ** 2)

    def tell_info(self):
        print('bmi:<%s>' % self.bmi)

b = Bmi('egon', 60, 1.4)
print(b.bmi)  # 使用者就像是使用数据属性的时候一样直接使用
b.weight = 50
print(b.bmi)

反射

代码语言:javascript
复制
反射就是通过字符串的形式处理类的属性
    hasattr    # 判断是否存在此属性
    setattr   #  设置属性
    getattr  # 获得属性
    delattr  # 删除属性
代码语言:javascript
复制
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def func1(self):
        print('func1')
    def tell_info(self):
        print('%s:%s' %(self.name, self.age))
obj = Foo('egon', 11)

# hasattr,参数一为属性,参数二为字符串式的属性名
print(hasattr(obj, 'name'))
print(hasattr(obj, 'named'))
# getattr 参数一为属性,参数二为字符串式的属性名
res = getattr(obj, 'name')
res1 = getattr(obj, 'func1')
print(res)
print(res1())
getattr(obj, 'func2', None)
# setattr 参数一为属性,参数二为字符串式的属性名,参数三是设置的属性的值
print(setattr(obj, 'name', 'EGON'))
print(setattr(obj, 'func1', 'aaa'))
print(obj.__dict__)
# delattr 参数一为属性,参数二为字符串式的属性名
delattr(obj, 'func1')
print(obj.__dict__)

反射使用方法

内置方法

代码语言:javascript
复制
__str__   自定义打印对象的值
__del__  在删除对象之前释放操作系统资源

__str__介绍

代码语言:javascript
复制
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
obj = Foo('egon', 11)
print(obj)
# 结果
# <__main__.Foo object at 0x02E01B10>

问题:通过打印对象出来的结果,我们会发现,这个信息对于我们来说信息量不大,我们能不能重新定义对象打印出来的结果:

代码语言:javascript
复制
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):
        print('=======>')

obj = Foo('egon', 11)
print(1)      # 从结果中我们看出来,首先执行打印了1
print(obj)   # 当打印对象的时候会去触发执行__str__方法,然后打印出=====>但是此时没有返回值,报错了
print(2)
# 结果
# 1
# =======>

因此,我们设置__str__的值为这样就可以自定义我们的输出了

代码语言:javascript
复制
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):
        return '<%s:%s>' %(self.name, self.age)

obj = Foo('egon', 11)
print(obj)
# 结果
# <egon:11>

__del__介绍

代码语言:javascript
复制
class Foo:
    def __init__(self, name, age, file_path):
        self.name = name
        self.age = age
        # 这个系统资源并不会跟着对象的删除而释放掉,因此我们需要定义__del__释放系统资源
        self.f = open(file_path, 'rt', encoding='utf-8')
    def __del__(self):
        self.f.close()
    def __str__(self):
        return '<%s:%s>' %(self.name, self.age)

obj = Foo('egon', 11, 'a')
print(obj)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-03-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档