Python类总结-反射及getattr,setattr 类反射的四个基本函数 反射动态获取模块的方法名 _getattr_ _delattr_ _setattr_ setattr, __delattr__的实际使用举例 标准类型的二次包装 授权 getattr和getattribute的区别及联系 反射的妙用-根据URL执行相应目录下面的函数

  • hasattr
  • getattr
  • setattr
  • delattr
#反射
class BlackMedium:
    feature = 'Ugly'
    def __init__(self,name, addr):
        self.name = name
        self.addr = addr
    def sell_house(self):
        print('[%s]正在卖房子,xx才买呢'%self.name)

    def rent_house(self):
        print('[%s]正在卖房子,xx才租呢'%self.name)

b1 = BlackMedium('万成置地','天路园')
print(b1.__dict__)
print(hasattr(b1,'name'))
print(getattr(b1,'name'))
print(hasattr(b1,'sell_house'))
print(getattr(b1,'sell_house'))
func = getattr(b1,'sell_house')
func()
# print(getattr(b1,'sell_housedfa'))#没有则报错
print(getattr(b1,'sell_housedfa','没有这个属性'))#没有则报错

# b1.sb = True 等同于下面
setattr(b1, 'sb', True)
setattr(b1, 'func', lambda x: x + 1)
setattr(b1, 'func1', lambda self: self.name + 'sb')
print(b1.func(2))
print(b1.func1(b1))
print(b1.__dict__)

# del b1.sb 等同于下面
delattr(b1,'sb')
print(b1.__dict__)

>>>

{'name': '万成置地', 'addr': '天路园'}
True
万成置地
True
<bound method BlackMedium.sell_house of <__main__.BlackMedium object at 0x02F5B9B0>>
[万成置地]正在卖房子,xx才买呢
没有这个属性

3
万成置地sb
{'name': '万成置地', 'addr': '天路园', 'sb': True, 'func': <function <lambda> at 0x00A99AE0>, 'func1': <function <lambda> at 0x00A99C00>}
{'name': '万成置地', 'addr': '天路园', 'func': <function <lambda> at 0x00A99AE0>, 'func1': <function <lambda> at 0x00A99C00>}

反射动态获取模块的方法名

class FtpClient:
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr = addr

#另外一个模块
#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get= getattr(f1,'get')
    func_get()
else:
    print('此方法不存在')
    print('处理其他逻辑')

_getattr_

class Foo:
    x = 1
    def __init__(self, y):
        self.y = y

    def __getattr__(self, item):
        print("执行_getattr__")

f1 =  Foo(10)
print(f1.y)
print(getattr(f1,'y'))
f1.sssss#__getattr__调用一个不存在的属性时才会触发

>>>
10
10
执行_getattr__

_delattr_

class Foo:
    x = 1
    def __init__(self, y):
        self.y = y

    def __delattr__(self, item):
        print("执行__delattr__")

f1 =  Foo(10)
del f1.y #删除时就触发__delattr__
del f1.x #删除时就触发__delattr__

>>>
执行__delattr__
执行__delattr__

_setattr_

class Foo:
    x = 1
    def __init__(self, y):
        self.y = y

    def __setattr__(self, key, value):
        print("执行__setattr__")
        # self.key = value#会出现递归错
        self.__dict__[key]=value #必须要这么设置

f1 =  Foo(10)
print(f1.__dict__)
f1.z = 2
print(f1.__dict__)
  • 利用__setattr__,__getattr__限制类的属性必须是字符串类型
class Foo:
    def __init__(self,name):
        self.name = name

    def __getattr__(self,item):
        print("你找的属性[%s]不存在"%item)

    def __setattr__(self, key, value):
        print("执行__setattr__", key, value)
        if type(value) is str:
            print('开始设置')
            self.__dict__[key] = value
        else:
            print('必须是字符串')


f1 = Foo("alex") #只有是字符串才能添加到Foo, Foo的属性必须是字符串
f1.age = 19

>>>
执行__setattr__ name alex
开始设置
执行__setattr__ age 19
必须是字符串
{'name': 'alex'}

setattr, __delattr__的实际使用举例

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

    def __getattr__(self,item):
        print("你找的属性[%s]不存在"%item)

    def __setattr__(self, key, value):
        print("执行__setattr__", key, value)
        if type(value) is str:
            print('开始设置')
            self.__dict__[key] = value
        else:
            print('必须是字符串')

    def __delattr__(self, item):
        print("执行__delattr__", item)
        #del self.item 不能这么用
        self.__dict__.pop(item)

f1 = Foo("alex") #只有是字符串才能添加到Foo, Foo的属性必须是字符串
print(f1.__dict__)
del f1.name
print(f1.__dict__)

标准类型的二次包装

class List(list): #基础list类
    def append(self, p_object):
        if type(p_object) is str:
            # list.append(self,p_object)
            super().append(p_object)
            print("必须追加字符串")
    def show_middle(self):
        mid_index = int(len(self)/2)
        return self[mid_index]

l1 = List("hello*world")
l1.append(11111)
l1.append('test')
print(l1)

# l2 =list("hello*world")
#
# print(l2,type(l2))
# print(l1,type(l1))
# print(l1.show_middle()

>>>
必须追加字符串
['h', 'e', 'l', 'l', 'o', '*', 'w', 'o', 'r', 'l', 'd', 'test']

授权

  • 授权是所有更新的功能都是由新类的某部分来处理,但已存在的功能授权给对象的默认属性
  • 授权的关键就是覆盖__getattr__方法
class Open:
    def __init__(self, filename, mode ='r', encoding = 'utf-8'):
        # self.filename = filename
        self.file=open(filename, mode, encoding=encoding)
        self.mode = mode
        self.encoding = encoding
    def __getattr__(self, item):
        print(item, type(item)) #item是个字符串类型,通过字符串得到一个函数,可以通过getattr
        return getattr(self.file,item) #返回一个方法

    # def read(self):
    #     pass
    # def write(self):
    #     pass

f1=Open('a.txt','w')
print(f1.file)
print(f1.read) #如果类中没有改方法,触发getattr,read传给item

sys_f = open('b.txt','w+')
print(getattr(sys_f,'read'))

>>>
<_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>
read <class 'str'>
<built-in method read of _io.TextIOWrapper object at 0x001ECDB0>
<built-in method read of _io.TextIOWrapper object at 0x001ECE30>
  • 组合授权
#用组合的方式实现文件的操作,即可以进行文件父类的操作(非继承),又可以定制本地的方法

import time
class Open:
    def __init__(self, filename, mode ='r', encoding = 'utf-8'):
        # self.filename = filename
        self.file=open(filename, mode, encoding=encoding) #一定要用这种方法打开文件
        self.mode = mode
        self.encoding = encoding

    def write(self,line):
        # 可以定制文件的write方法
        t = time.strftime('%Y-%m-%d %X')
        # print('--->',line)
        self.file.write('%s %s'% (t,line))

    def __getattr__(self, item):
        # print(item, type(item)) #item是个字符串类型,通过字符串得到一个函数,可以通过getattr
        return getattr(self.file,item) #返回一个方法

f1=Open('a.txt','r+')
print(f1.file)
print(f1.read) #如果类中没有改方法,触发getattr,read传给item

# sys_f = open('b.txt','w+')
# print(getattr(sys_f,'read'))

f1.write('-------------------
') #写入a.txt
f1.write('CPU负载过高 
') #写入a.txt
f1.seek(0) #seek(0)(从头开始)
# 基于对位置的seek
# 0,以文件开头,不限定打开方式。其它的两种限定要以b模式打开
# 1, 相对于上次光标停留的位置
# 2,从文件未尾,倒序。位置要用负数表示
print(f1.read())

getattr和getattribute的区别及联系

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

    def __getattr__(self, item):
        #只有在getattribute抛出AttributeError才执行
        print("执行的是getattr")
        # return self.__dict__[item]
    def __getattribute__(self, item):
        # 默认情况无论是否有该属性都优先执行getattribute
        print("执行的是getattribute")
        raise AttributeError('跑出异常了,去执行__getattr__')
        # 只可以抛出AttributeError才raise到getattr

f1 = Foo(10)
#默认情况无论是否有该属性都优先执行getattribute
f1.xxxx

反射的妙用-根据URL执行相应目录下面的函数


data = input('please input address:') #需要输入account/login
array = data.split('/') #分割为account, login
'''
#实现更加URL不同分发到不同的函数去处理
from backend import account
# 等同于 import backend.account
# backend.account.login()
if data == 'account/login':
    account.login()
elif data == 'account/logout':
    account.logout()    
'''
#根据传入的数列的字符串,通过反射动态调用函数
userspace = __import__('backend.'+array[0]) #backend为目录
model = getattr(userspace, array[0]) #需要多一步getattr
#通过getattr传入函数名称的字符串,调用函数
func = getattr(model, array[1])
func()

>>>
please input address:admin/index
欢迎登陆后台管理

调用backend目录下admin.py下index函数

admin.py
def index():
    print('欢迎登陆后台管理')