先大致粗略的说一下反射的概念,不是很准确,后续详细讲解:

  1. 以字符串的形式,导入模块

  2. 以字符串的形式,获取模块内部的函数,并执行

通常我们想在一个模块中导入另外一个模块,则需要通过 import xxx,所示

commons.py文件

NAME = 'XM'

def f1():
print('f1')

下面要想在reflect.py文件中导入commons.py,并调用commons.py文件中的f1函数和NAME变量,通常情况下我们通过下面的方式导入

import commons

# 获取变量
NAME = commons.NAME
# 调用函数
commons.f1()

要想通过字符串的形式导入commons.py模块,通过__import__('xxx')即可,如果commons.py模块不存在,则会报错

commons = __import__('commons')
print(commons)
# 输出
# <module 'commons' from 'xxxxx/commons.py'> NAME = commons.NAME
print(NAME)
# 输出
# XM # 调用函数
commons.f1()
# 输出
# f1

扩展:

如果要想像类似import xx.xxx.xxxx或者from xx.xxx import xxxx这两种形式导入模块,通过上述__import__('xx')已经失效,你会发现通过__import__(xx.xxx.xxxx),只能导入xx,此时在__import__('xx.xxx.xxxx',fromlist=True)即可

上面主要展示怎么以字符串的形式导入模块,是时候展示真正的技能了,之前我们学习内置模块时,有几个模块没有讲,说是放到后面讲,分别是hasattr(obj,name)、getattr(obj,name,default=None)、setattr(x,y,v)、delattr(obj,name)我们下面主要讲一下这4个模块

  1.hasattr(obj,name) 此模块主要判断obj模块是否包含name变量或者函数

ret = hasattr(commons,'f1')
print(ret)
# 输出
# True ret = hasattr(commons,'NAME')
print(ret)
# 输出
# True ret = hasattr(commons,'Name')
print(ret)
# 输出
# False

  2.getattr(obj,name,default=None)此模块主要判断obj模块是否包含name变量或者函数,default参数用于在不存在xx函数或者变量时防止报错

target_func = getattr(commons,'f1')
target_func()
# 输出
# f1 target_property = getattr(commons,'NAME')
print(target_property)
# 输出
# XM target_func = getattr(commons,'f2')
print(target_func)
# 输出
# Traceback (most recent call last):
# File "xxx/reflect.py", line 46, in <module>
# target_func = getattr(commons,'f2')
# AttributeError: module 'commons' has no attribute 'f2' target_func = getattr(commons,'f2',None)
print(target_func)
# 输出
# None

  3.settattr(x,y,v)模块主要用于动态给xx模块注入变量或者函数,可以用x.y = v更加形象的表示

# 先判断commons模块是否存在Name变量
ret = hasattr(commons,'Name')
print(ret)
# 输出
# False setattr(commons,'Name','DXM')
setattr(commons,'f2',lambda x: x+1) ret = hasattr(commons,'Name')
print(ret)
# 输出
# True target_property = getattr(commons,'Name')
print(target_property)
# 输出
# DXM target_func = getattr(commons,'f2')
ret = target_func(1)
print(ret)
# 输出
#

  4.delattr(obj,name)模块主要是删除obj模块的变量或者函数

# 先判断commons模块是否存在Name变量
ret = hasattr(commons,'NAME')
print(ret)
# 输出
# True delattr(commons,'NAME') ret = hasattr(commons,'NAME')
print(ret)
# 输出
# False

反射当前模块成员:

import sys

def s1():
print('s1') def s2():
print('s2') module = sys.modules[__name__] ret = hasattr(module,'s1')
ret2 = getattr(module,'s2')

__setattr__,__getattr__,__delattr__三者的用法

class Foo:
x = 1
def __init__(self,y):
self.y = y def __getattr__(self, item):
print('----> from getattr:您找的属性不存在') def __setattr__(self, key, value):
print('----> from setattr:',key,'=',value)
# self.key = value # 这就无限递归了
self.__dict__[key] = value # 应该使用他 def __delattr__(self, item):
print('----> from delattr:',item)
# del self.item # 需要无限递归
self.__dict__.pop(item) # __setattr__添加/修改属性会触发执行
f1 = Foo(10)
# 因为你重写了__setattr__,凡是赋值就会触发它的运行,你啥都没写,就是根本没有赋值,除非你直接操作属性字典,否则永远无法赋值
print(f1.__dict__)
f1.z = 3
print(f1.__dict__) # __delattr__删除属性的时候回触发
f1.__dict__['a'] = 3 # 可以直接添加/修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__) # __getattr__只有在使用.调用属性,并且属性不存在的时候才会触发
f1.xxxxx
f1.y

__getattribute__用法以及和__getattr__的区别?

回顾__getattr__

class Foo:
def __init__(self,x):
self.x = x def __getattr__(self, item):
print('只有属性不存在,才会执行')
# return self.__dict__[item] f1 = Foo(10)
print(f1.x) # 此时不会触发__getattr__
f1.xxx # 不存在属性时,会触发__getattr__ # 打印结果
#
# 只有属性不存在,才会执行

__getattribute__用法

class Foo:
def __init__(self,x):
self.x = x def __getattribute__(self, item):
print('不管属性是否存在,都会执行')
# return self.__dict__[item] f2 = Foo(10)
print(f2.x) # 此时不会触发__getattr__
f2.xxx # 不存在属性时,会触发__getattr__ # 打印结果
# 不管属性是否存在,都会执行
# None
# 不管属性是否存在,都会执行

两者同时存在时,只会触发__getattribute__,除非__getattribute__报异常

class Foo:

    def __init__(self,x):
self.x = x def __getattr__(self, item):
print('只有属性不存在,才会执行')
# return self.__dict__[item] def __getattribute__(self, item):
print('不管属性是否存在,都会执行')
# return self.__dict__[item] f3 = Foo(10)
print(f3.x)
f3.xxxxx # 打印结果
# 不管属性是否存在,都会执行
# None
# 不管属性是否存在,都会执行

__setitem__,__getitem__,__delitem__

'''
通过在类中实现这个3个方法__setitem__,__getitem__,__delitem__
可以实现类似操作字典dict的访问方式,访问类的属性
''' class Foo:
def __init__(self,name):
self.name = name def __setitem__(self, key, value):
print('from __setitem__ key:',key,'value:',value)
self.__dict__[key] = value def __getitem__(self, item):
print('from __getitem__')
return self.__dict__[item] def __delitem__(self, key):
print('from __delitem__ key:',key)
self.__dict__.pop(key) f = Foo('alex') print(f['name'])
f['age'] = 123
print(f['age']) del f['age'] # 打印结果
# from __getitem__
# alex
# from __setitem__ key: age value: 123
# from __getitem__
#
# from __delitem__ key: age

注意:这里的__setitem__、__getitem__、__delitem__只能通过字典的访问方式访问属性,如果通过“对象.属性”的方式则不会触发

总结

反射可以总结为一下几点:

  1.以字符串的形式去某个模块找东西

  2.以字符串的形式去某个模块中判断东西是否存在

  3.以字符串的形式去某个模块设置东西

  4.以字符串的形式去某个模块删除东西

用一句话说明反射,"以字符串的形式去某个模块操作成员"

项目练习

一. 基于反射机制的模拟Web框架路由系统:

使用场景:目前大多数Web页面都有切换标签的功能,切换标签时你会发现,不同标签对应的url是不一样的,那么现在我们就模拟通过输入不同的url,切换到不同的标签,项目的目录结构如下  

不使用反射机制实现切换标签的代码如下:

from lib import account

url = input('请输入xxxx/函数名格式的url:')
if url.endswith('login'):
ret = account.login()
print(ret)
elif url.endswith('logout'):
ret = account.logout()
print(ret)
elif url.endswith('register'):
ret = account.register()
print(ret)
else:
print('')

这种情况下,我们不使用反射,功能是可以完成,弊端就是如果页面中有好多标签,那我们就需要各种判断,调用各种函数,这肯定不是作为屌丝程序员的我们想要的,那么下面是时候展示真正的技能了

使用反射机制优化第一版

# 优化第一版
# 要求输入的url格式为:xxxx/函数名
# 通过getatter()函数解决了判断是否为各种函数的问题
url = input('请输入xxxx/函数格式的url:')
func_name = url.split('/')[-1]
if hasattr(account,func_name):
target_func = getattr(account,func_name)
ret = target_func()
print(ret)
else:
print('')

此时你会发现,哎呦!可以啊!离我们理想目标进了一大步啊,似乎从解放前回到现实了,通过hasattr()先判断模块是否存在这个成员,存在则通过getattr()获取成员,并执行,不存在则报404错误,但是这样似乎还是没有达到我们的要求,因为还需要import lib.account,其次实际的使用场景不是这样的,因为肯定是每个标签对应一个模块,不会写在一个模块中,那么问题来了,难道我还需要各种import xxx?下面我们通过另外一种方法解决这个问题

# 优化第二版
# 要求输入的url格式为:模块名/函数名
# 通过__import__('xxx')解决了导入模块的问题
# 通过getattr()解决了函数判断问题
# 通过hasatter()解决了函数是否存在的判断问题
url = input('请输入模块/函数名格式的url:')
module_name,module_func = url.split('/')
moudle = __import__('lib.' + module_name,fromlist=True) if hasattr(moudle,module_func):
target_func = getattr(moudle,module_name)
ret = target_func()
print(ret)
else:
print('')

我们在优化第一版的基础上加了__import__('xxx')的方法导入模块,这样只要值输入url时指定格式模块名/函数名,就可以很好的解决上面说的问题了

注意:因为lib模块中包含多个模块,所以在使用__import__('xxx',fromlist=True)别忘记加后面的参数

二. 二次加工标准类型(包装)

包装:Python为大家提供了标准的数据模型,以及丰富的内置方法,其实很多情况下我们需要基于标准数据模型来定制我们自己的数据模型,新增/修改方法,这就用到了我们刚学的继承/派生知识(其他标准类型,可以通过下面的方式进行二次加工)

1.基于继承实现二次加工

class List(list):

    def __init__(self,item):
super().__init__(item)def append(self, object):
if not isinstance(object,int):
raise TypeError('Must be int')
super().append(object) @property
def mid(self):
# 新增自己的属性
index = len(self) // 2
return self[index] li = List([1,2,3,4],tag=False)
print(li)
li.append(5)
print(li)
# print(li.append('12345')) # 报错 必须为int类型
print(li.mid) # 其他继承list方法仍然可以使用
li.insert(0,123)
print(li)

2.list的clear()方法加权限限制

class List(list):

    def __init__(self,item,tag=False):
super().__init__(item)
self.tag = tag def append(self, object):
if not isinstance(object,int):
raise TypeError('Must be int')
super().append(object) def clear(self):
if not self.tag:
raise PermissionError
super().clear() @property
def mid(self):
# 新增自己的属性
index = len(self) // 2
return self[index] li = List([1,2,3,4],tag=False)
print(li)
li.append(5)
print(li)
# print(li.append('12345')) # 报错 必须为int类型
# li.clear() # 报错 没有权限
print(li.mid) # 其他继承list方法仍然可以使用
li.insert(0,123)
print(li)

3.授权示范一

说明:自定义一个类似open的文件操作对象,你会发现FileHandle没有自定义seek()、read()等方法,为什么调用的时候,可以通过对象调用到,这一切都归功于__getattr__,聪明的你已经发现,在FileHandle类底部有一个__getattr__方法,之前我们讲解过,__getattr__触发的条件是,使用对象/类.属性的方式调用,并且属性不存在的时候会触发,而且我们返回的是通过反射getattr(self.file_obj,item)的方法,由于self.file_obj实质就是open()的文件句柄,所以实质就是在调用文件句柄的seek()等函数

import time
class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'):
self.file_obj = open(filename,mode,encoding=encoding) def write(self,line):
t = time.strftime('%Y-%m-%d %T')
self.file_obj.write('%s %s' % (t,line)) # 只有使用.调用属性,并且属性不存在会触发 这里是关键点
def __getattr__(self, item):
print(self.file_obj,item)
return getattr(self.file_obj,item) f1 = FileHandle('a.txt','w+')
print(f1) f1.write('你好啊!')
f1.seek(0)
print(f1.read())
f1.close()

4.授权示范二(在示范一的基础上加上对'b'模式的支持)

# 授权示范二:支持b模式
class FileHandle:
def __init__(self,filename,mode='r',encoding='utf-8'):
if 'b' in mode:
self.file_obj = open(filename,mode)
else:
self.file_obj = open(filename,mode,encoding=encoding)
self.filename = filename
self.mode = mode
self.encoding = encoding
def write(self,line):
if 'b' in self.mode:
if not isinstance(line,bytes):
raise TypeError('must be bytes')
self.file_obj.write(line) def __getattr__(self, item):
return getattr(self.file_obj,item) def __str__(self):
if 'b' in self.mode:
res = '<io.BufferedReader name=%s>' % self.filename
else:
res = '<io.TxtIOWrapper name=%s encoding=%s>' % (self.filename,self.encoding)
return res f2 = FileHandle('b.txt','wb')
# f2.write('你好啊!') # 报错 must be bytes
f2.write('XM美女,你好啊!'.encode('utf-8'))
print(f2)
f2.close()

5.基于上面的授权模式,优化List

class List:
def __init__(self,seq):
self.seq = seq def append(self,object):
if not isinstance(object,int):
raise TypeError('must be int')
self.seq.append(object) @property
def mid(self):
'''
新增自己的方法
:return:
'''
index = len(self.seq) // 2
return self.seq[index] def __getattr__(self, item):
return getattr(self.seq,item) def __str__(self):
return str(self.seq) li = List([1,2,3,4])
print(li)
li.append(5)
print(li)
# li.append('12345') # 报错 must be int print(li.mid) #基于授权,获取insert方法
li.insert(0,1234567)
print(li)

20180308-Python内置方法的更多相关文章

  1. Python内置方法详解

    1. 字符串内置方法详解 为何要有字符串?相对于元组.列表等,对于唯一类型的定义,字符串具有最简单的形式. 字符串往往以变量接收,变量名. 可以查看所有的字符串的内置方法,如: 1> count ...

  2. 匿名函数 python内置方法(max/min/filter/map/sorted/reduce)面向过程编程

    目录 函数进阶三 1. 匿名函数 1. 什么是匿名函数 2. 匿名函数的语法 3. 能和匿名函数联用的一些方法 2. python解释器内置方法 3. 异常处理 面向过程编程 函数进阶三 1. 匿名函 ...

  3. python 内置方法、数据序列化

    abc(*args, **kwargs) 取绝对值 def add(a,b,f): return f(a)+f(b) res = add(3,-6,abs) print(res) all(*args, ...

  4. 基于python内置方法进行代码混淆

    0x00 动态加载模块 在python脚本中,直接使用import os.import subprocess或from os import system这种方法很容易被规则检测,即使使用其它执行命令的 ...

  5. python内置方法

    1. 简介 本指南归纳于我的几个月的博客,主题是 魔法方法 . 什么是魔法方法呢?它们在面向对象的Python的处处皆是.它们是一些可以让你对类添加"魔法"的特殊方法. 它们经常是 ...

  6. Python内置方法的时间复杂度(转)

    原文:http://www.orangecube.net/python-time-complexity 本文翻译自Python Wiki本文基于GPL v2协议,转载请保留此协议. 本页面涵盖了Pyt ...

  7. Python内置方法的时间复杂度

    转载自:http://www.orangecube.NET/Python-time-complexity 本页面涵盖了Python中若干方法的时间复杂度(或者叫"大欧"," ...

  8. Python 内置方法

    1. abs() 取绝对值函数 #!/usr/bin/env python # _*_ coding: UTF-8 _*_ # Author:taoke i = 100 print(abs(i)) i ...

  9. Python内置方法/函数

    abs() 返回数字的绝对值. abs(x) all() 用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False. 元素除了是 0.空. ...

  10. python内置方法大全

    数学运算 abs:求数值的绝对值 >>> abs(-2) 2 divmod:返回两个数值的商和余数 >>> divmod(5,2) (2, 1) >> ...

随机推荐

  1. robotframework关键字

    *** Settings ***Library Selenium2Library *** Keywords ***Checkbox应该不被选择 [Arguments] ${locator} Check ...

  2. 从Excel粘到Word的图片只有下面一半

    把图片粘贴到WORD上为什么只显示最底下一部分? 出现此故障的原因,有可能是设置为固定值的文档行距小于图形的高度,从而导致插入的图形只显示出了一部分.所以要调整图片的段落格式中的行间距. 解决方法 选 ...

  3. frugally-deep: Header-only library for using Keras models in C++

    // Convenience wrapper around predict for models with // single tensor outputs of shape (1, 1, 1), / ...

  4. MySql不区分大小写。

    解决方案: 1:给相关字段添加上让其区分大小写. alter table 表名 modify column 字段名 varchar(100) binary character set utf8

  5. python 全栈开发,Day54(关于DOM操作的相关案例,JS中的面向对象,定时器,BOM,client、offset、scroll系列)

    04-jQuery的属性操作 jquery的属性操作模块分为四个部分:html属性操作,dom属性操作,类样式操作和值操作 html属性操作:是对html文档中的属性进行读取,设置和移除操作.比如at ...

  6. python 全栈开发,Day9(函数的初始,返回值,传参,三元运算)

    一.函数的初始 比如python没有len()方法,如果求字符串的长度 使用for循环 s = 'asdfadsf' count = 0 for i in s: count += 1 print(co ...

  7. php substr_count()函数 语法

    php substr_count()函数 语法 作用:统计一个字符串,在另一个字符串中出现次数大理石量具 语法:substr_count(string,substring,start,length) ...

  8. 实现网页qq在线咨询功能

    在自己的网页中实现qq在线咨询,给图片或链接添加地址为:tencent://message/?uin=你的qq号码!就可以了. <a href="tencent://message/? ...

  9. Python_005(字典无极坑)

    一.字典(dict) 1.字典的定义格式:dic{key1:value1,key2,value2} :这里面key是唯一的,保存的时候,根据key计算一个内存地址,然后将key-value保存在这个地 ...

  10. 学习日记4、datagrid多行删除

    1.前台展现单选框datagrid代码 $('#List').datagrid({ url: '@Url.Action("GetList")', width: $(window). ...