Python中的反射与attr内置函数

一. 反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)
在Python中反射的表现形式是:
通过字符串的形式操作对象相关的属性
在Python中通过四个函数实现反射:

函数名 意义
hasattr(obj,name) 判断object中有没有一个name字符串对应的方法或属性
getattr(obj,name, default) 检查obj.__dict__中有没有name这个键值,有则不做任何处理,没有则报错
setattr(obj,name,value) 等价于obj.name=value
delattr(obj,name) 等价于del obj.name
class People:
    country = 'China'

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def eat(self):
        print('%s is eating' % self.name)

    def sleep(self):
        print('% is sleeping' % self.name)

p1=People('Jack', 'male')

#检测是否含有某属性
print(hasattr(p1, 'name'))
print(hasattr(p1, 'eat'))

>>True
>>True

#获取属性
n = getattr(p1, 'name')
print(n)
func = getattr(p1, 'eat')
func()
getattr(p1, 'aaaaaaaa') #报错
print(getattr(p1, 'aaaaaaaa', 'not exist'))
>>Jack
>>Jack is eating
>>not exist

#设置属性
setattr(p1, 'Joe', 'female')
setattr(p1, 'run', lambda self: self.name+' is running')
print(p1.__dict__)
print(p1.run(p1))
>>{'name': 'Jack', 'gender': 'male', 'Joe': 'female', 'run': <function <lambda> at 0x0000021D2B3E3E18>}
>>Jack is running

#删除属性
print(p1.__dict__)
delattr(p1, 'name')
print(p1.__dict__)
>>  {'name': 'Jack', 'gender': 'male'}
>>{'gender': 'male'}
#只能删除对象的属性,如果是类那么只能删类的方法,如果是类实例化的对象只能删实例化对象的属性不能删类的,比如说执行delattr(p1, 'eat')会报错,如果删除的属性不存在也报错

反射当前模块成员

import sys


def s1():
    print('s1')


def s2():
    print('s2')


this_module = sys.modules[__name__]

print(hasattr(this_module, 's1'))
print(getattr(this_module, 's2'))

使用反射的用途

可插拔

可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用
建立``ftpClient.py`模块

class FtpClient:    # 仅定义,不实现具体方法
    def __init__(self, addr):
        print('正在连接服务器[%s]' % addr)
        self.addr = addr

建立ftpServer.py模块

from ftpClient import FtpClient

f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get=getattr(f1,'get')
    func_get()
else:
    print('not exist')

>>正在连接服务器[192.168.1.1]
not exist

动态导入模块

通过字符串名称导入模块

import importlib
t = importlib.import_module('time')
print(t.time())

二. attr内置函数

__setattr__, __delattr__,__getattr__

class Foo:
    def __init__(self, name):
        self.name = name

    def __setattr__(self, key, value):
        print('running __setattr__')
        self.__dict__[key] = value

    def __getattr__(self, item):
        print('running __getattr__')
        pass

    def __delattr__(self, item):
        print('running __delattr__')
        self.__dict__.pop(item)

f = Foo('Jack')
print(f.__dict__)

f.name = 'Jax'
print(f.__dict__)

del f.name

print(f.__dict__)

f.xxx    #调用不存在的属性时才会触发__getattr__

>>running __setattr__
>>{'name': 'Jack'}
>>running __setattr__
>>{'name': 'Jax'}
>>running __delattr__
>>{}
>>running __getattr__ 

三. 定制自己的数据类型

包装

对已有的类型具有的功能进行扩展

class List(list):
    def append(self, p_object):
        if not isinstance(p_object, str):
            raise TypeError('must be str')
        super().append(p_object)

    @property
    def mid(self):  # 增加属性
        index = len(self) // 2
        return self[index]


l = List()
l.append('oo')
l.append('jj')
l.insert(0, 'ii')
print(l)
print(l.mid)

授权

授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖getattr方法

import time

class FileHandle:
    def __init__(self, filename, mode='r', encoding='utf-8'):
        self.file = open(filename, mode, encoding=encoding)
        self.mode = mode
        self.encoding = encoding

    def write(self, line):
        print('running write operation')
        t = time.strftime('%Y-%m-%d %X')
        self.file.write('[%s] %s' % (t, line))

    def __getattr__(self, item):
        return getattr(self.file, item)

f = FileHandle('a.txt', 'w+')

f.write('777')    # 通过授权实现执行write函数时,为所写内容添加时间
f.seek(0)
print(f.read())
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容