专栏首页python3第27天面向对象之反射,绑定方法,特定的

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

绑定方法与非绑定方法

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

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

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

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

1. 对象绑定方法特点

1. 对象去调用的时候,自动传值
2. 类去调用的时候,就是一个普通的函数,需要自己去传值
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. 类绑定方法的特点

无论是对象还是累调用的时候都是自动的把类当做第一个参数传递进去
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. 非绑定方法的特点

无论是类还是对象去调用的时候都是作为普通函数,不会进行自动的传值
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

应用场景

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

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,这个时候我可能会这样写

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,因此代码变成了这样

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)

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

# 应该写入这样一个函数,把之前创建对象的表达式放进去,但是这样子有一个问题,也就是当类名发生变化的时候,这个函数就不能用了
def from_conf():
    return Mysql(settings.IP, settings.PORT)
# 为了解决上面的问题,作为一个参数传递进去,这个参数本质上一个类,因此写到类中应该是一个绑定类的方法
def from_conf(xx):
    return xx(settings.IP, settings.PORT)
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值

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装饰器

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

案例:

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

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

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的值。

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

问题

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的用处了,它可以把函数属性转换成一个数据属性,使用者可以直接调用

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)

反射

反射就是通过字符串的形式处理类的属性
    hasattr    # 判断是否存在此属性
    setattr   #  设置属性
    getattr  # 获得属性
    delattr  # 删除属性
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__)

反射使用方法

内置方法

__str__   自定义打印对象的值
__del__  在删除对象之前释放操作系统资源

__str__介绍

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

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

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__的值为这样就可以自定义我们的输出了

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__介绍

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)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • bs4爬虫实战四--获取音悦台榜单

    本次爬虫使用随机proxy和headers抵抗反爬虫机制,来获取音悦台网站公布的MV榜单.

    py3study
  • python通过线程实现定时器timer

    下面介绍以threading模块来实现定时器的方法。  使用前先做一个简单试验: 

    py3study
  • Django(三):HttpReques

      当一个请求连接进来时,django会创建一个HttpRequest对象来封装和保存所有请求相关的信息,并且会根据请求路由载入匹配的视图函数。每个请求的视图函...

    py3study
  • bs4爬虫实战四--获取音悦台榜单

    本次爬虫使用随机proxy和headers抵抗反爬虫机制,来获取音悦台网站公布的MV榜单.

    py3study
  • Python基础入门_5面向对象基础

    第五篇主要介绍 Python 的面向对象基础知识,也就是类的介绍,包括类方法和属性、构造方法、方法重写、继承等,最后给出两道简单的练习题。

    kbsc13
  • What?废柴, 模拟登陆,代码控制滑动验证真的很难吗?Are you kidding???

    在前边的python接口自动化的时候,我们由于博客园的登录机制的改变,没有用博客园的登录测试接口。那么博客园现在变成了滑动验证登录,而且现在绝大多数的登录都变成...

    北京-宏哥
  • 教你制作可移动的导航栏

    Dwyane
  • vn.py入门-低买高卖示例

    本文用一个例子来介绍vnpy的用法。从项目创建开始,到一个简单策略的设计。 这个例子连接到CTP接口,每秒检查一下目标合约的价格,若低于指定价格则买入,若高于指...

    用Python的交易员
  • 再议Python协程——从yield到asyncio

    协程,英文名Coroutine。 前面介绍Python的多线程,以及用多线程实现并发(参见这篇文章【浅析Python多线程】),今天介绍的协程也是常用的并发手段...

    用户1432189
  • Python学习——数据模型/特殊方法

    数据模型其实是对Python框架的描述,它规范了这门语言自身构架模块的接口,这些模块包括但不限于序列、迭代器、函数、类和上下文管理器。简单来说,数据模型就是Py...

    陆勤_数据人网

扫码关注云+社区

领取腾讯云代金券