python对象方法 & 反射

915 阅读5分钟

这是我参与更文挑战的第4天,活动详情查看: 更文挑战

微信公众号搜索【程序媛小庄】,关注半路出家的程序媛如何靠python开发养家糊口~

前言

类实例化得到的对象可以直接调用类中定义的函数,并且可以将对象本身作为第一个参数,那么类能不能也能像对象一样使用类体中的函数而不需要传递第一个参数呢?如果我们使用别人封装好的类,如何判断这个类或者对象是否有某个属性呢?本文就带小伙伴们一探究竟吧!

绑定方法与非绑定方法

首先需要明确的是在类中定义的函数称为方法,它和普通的函数有些不同,类中定义的函数分为两大类,分别是绑定方法和非绑定方法。

绑定方法

绑定方法的特殊之处在于将调用者本身当作第一个参数自动传入,绑定方法分为绑定给类的方法和绑定给类实例化的对象的方法,绑定给对象的方法调用者是对象,自动传入的参数就是对象;绑定给类的方法,调用者是类,自动传入的是类。

在类中正常定义的函数默认是绑定给对象的,而在类内定义的函数上加上装饰器@classmethod,该函数就被绑定给了类。

类方法在被类调用的时候,会自动将类作为第一个参数传递给该类方法,类方法通常用于在__init__的基础上提供额外的初始化实例的方式。需要注意的是,类方法就是专门给类用的,类实例化的对象调用类方法是没有意义的。

class Animal():
    
    def __init__(self, color):
        self.color = color
    
    @classmethod
    def create_new_animal(cls): 
        print('创建一个新的动物种类')
        
>>> Animal.create_new_animal
<bound method Animal.create_new_animal of <class '__main__.Animal'>>

>>> Animal.create_new_animal()  # 类调用类方法会将类本身当做第一个参数传给类方法
创建一个新的动物种类

非绑定方法

为类体中定义的函数加上@staticmethod后,该方法就会变成非绑定方法,也称为静态方法,被该装饰器装饰的函数会变成普通函数,不会绑定给任何对象,也没有了自动传参的效果。

class Animal():
    
    def __init__(self, color):
        self.color = color
    
    @staticmethod
    def create_new_animal():   # 静态方法没有自动传参的效果,就是普通的函数
        print('创建一个新的动物种类')
        
>>> Animal.create_new_animal
<function Animal.create_new_animal at 0x000001DAE53CE670>

>>> Animal.create_new_animal()
创建一个新的动物种类

>>> Animal('white').create_new_animal()
创建一个新的动物种类

反射

反射是指在程序运行过程中可以动态获取对象的属性信息,在程序运行过程中,如果要获取一个不知道有什么属性的对象,可以使用反射来分析出对象的属性。可能有小伙伴说了,可以使用对象.__dict__查看对象的属性呀,确实可以,但是在程序开发中不建议直接操作对象的这种以__开头的内置方法。

在Python中实现反射非常简单,如果想要获取一个不知道存有何种属性的对象的属性,可以通过内置Python解释器内置函数dir获取任意对象的属性列表,列表中的元素是字符串格式。

>>> class Test():
...     test = 'test'
...     def foo(self):
...         print('test foo')
...

>>> dir(Test)
[..., 'foo', 'test']

>>> dir(Test())
[..., 'foo', 'test']

获取了对象的属性列表,需要借助Python解释器内置函数hasattr setattr getattr delattr来操作对象的属性。

t = Test()

# hasattr(obj, '属性名'):判断对象是否有某个属性
>>> hasattr(t, 'test')
True
>>> hasattr(t, 'test1')
False

# setattr(obj, '属性名', '新的属性值'): 修改对象的某个值,如果属性名不存在则为对象添加属性
>>> setattr(t, 'test', 'test1')
>>> setattr(t, 'test1', 'test1')
>>> t.test
'test1'
>>> t.test1
'test1'

# getattr(obj, '属性名')  # 获取对象的属性值
>>> getattr(t, 'foo')
<bound method Test.foo of <__main__.Test object at 0x000001DAE4F9BF40>>
>>> getattr(t, 'test')
'test1'
>>> getattr(t, 'test1')
'test1'

# delattr(obj, '属性名')  # 删除对象的属性
>>> delattr(t, 'test1')
>>> hasattr(t, 'test1')
False

基于反射可以通过字符串十分灵活的操作对象的属性,反射的原理其实就是基于对象.__dict__实现的。

# 1 先通过调用dir功能,查看某一个对象下可以  .  出哪些属性
print(dir(obj))
# 2 通过字符串反射到真正的属性,得到属性值
print(obj.__dict__[dir(obj)[-2]])

内置方法

Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发,我们以常用的__str____del__为例来简单介绍它们的使用。

__str__在打印对象的时候会自动触发,然后将返回值(必须是字符串类型)当作本次打印的结果输出,在自定义类中不存在__str__时,打印对象输出结果是对象的内存地址;在自定义类中存在__str__时,打印对象输出结果是__str__方法的返回值。

# 类中没有__str__方法
class Test:

    def test(self):
        print('test')


t = Test()
print(t)  # <__main__.Test object at 0x000001F9EE618FD0>


# 类中有__str__
class Test:

    def test(self):
        print('test')

    def __str__(self):
        return 'Test对象'
    
t = Test()
print(t)  # Test对象

__del__方法是在对象被删除时自动触发,由于python的垃圾回收机制会自动清理程序中没用的资源,因此如果一个对象只是占用应用程序的资源,没有必要定义__del__方法,但是如果设计到占用系统资源的话比如打开的文件对象,由于关系到操作系统的资源,python的垃圾回收机制派不上用场的时候,就需要为对象创建__del__方法,用于对象被删除后自动触发回收操作系统资源。

class Test:
    def __init__(self):
        self.x = open('a.txt',mode='w')
        # self.x = 占用的是操作系统资源

    def __del__(self):
        print('run')
        # 发起系统调用,告诉操作系统回收相关的系统资源
        self.x.close()

obj = T()
del obj # obj.__del__()