简单了解python反射机制的一些知识
作者:千锋Python唐唐君 发布时间:2022-02-05 15:01:04
反射
反射机制就是在运行时,动态的确定对象的类型,并可以通过字符串调用对象属性、方法、导入模块,是一种基于字符串的事件驱动。
解释型语言:程序不需要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次。因此效率比较低。相对于编译型语言存在的,源代码不是直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。比如Python/JavaScript / Perl /Shell等都是解释型语言。
python是一门解释型语言,因此对于反射机制支持很好。在python中支持反射机制的函数有getattr()、setattr()、delattr()、exec()、eval()、__import__,这些函数都可以执行字符串。
eval
计算指定表达式的值。它只能执行单个表达式,而不能是复杂的代码逻辑。而且不能是赋值表达式。
单个表达式:
a = "12 + 43"
b = eval(a)
print(b)
复杂表达式:
a = "print(12 + 43); print(1111)"
b = eval(a)
print(b)
# 输出:
Traceback (most recent call last):
File "xxxx.py", line 10, in <module>
b = eval(a)
File "<string>", line 1
print(12 + 43); print(1111)
^
SyntaxError: invalid syntax
赋值:
a = 1
b = eval("a = 21")
print(b)
通常我们使用eval的时候,主要是使用它的返回值,获取表达式计算出的值
exec
执行复杂表达式,返回值永远都是None
b = exec("aa = 21")
print(b) # None,exec返回值为None
print(aa) # 21,exec执行了赋值语句,并定义了aa变量
执行复杂语句:
a = '''ret = []
for i in range(10):
ret.append(i)'''
exec(a)
print(ret) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
导入模块:
# 导入模块
exec("import config")
print(config.KEYWORD)
# 动态创建类
class Base:
def __init__(self):
print("Base")
a = "Base"
exec(a+"()")
导入模块这个功能就非常厉害了,这样我们就可以动态的创建各种模块类。
eval()函数和exec()函数的区别:
eval()函数只能计算单个表达式的值,而exec()函数可以动态运行代码段。
eval()函数可以有返回值,而exec()函数返回值永远为None。
再看一下下面的例子:
class Base:
def __init__(self):
print("Base")
def test(self):
print("test")
return "Base::test"
如果我们想通过字符串来调用a对象的test方法,应该怎么做呢,如果要获取返回值,那么可以使用
b = eval("a.test()")
print(b)
输出:
test
Base::test
如果不需要获取返回值,那么可以使用exec,exec("a.test()"),输出:test
虽然我们可以使用eval和exec来执行以上代码,但是这种方式有一个缺陷,假如这个属性是不存在的,那么这种调用就会报错。那么做好的方式是什么呢?先判断属性是否存在,如果存在就调用,不存在就不调用,python为我们提供了一套方法:hasattr、getattr、setattr、delattr
hasattr
def hasattr(*args, **kwargs): # real signature unknown
"""
Return whether the object has an attribute with the given name.
This is done by calling getattr(obj, name) and catching AttributeError.
"""
pass
通过源码注释我们知道,它返回对象是否具有指定名称的属性。而且它是通过调用getattr并捕获AttributeError异常来判断的。就像上面的属性调用,我们就可以使用hasattr(a, "test")来判断,通过源码注释我们也可以思考一下,eval这种是不是也可以实现这种方法呢?
def has_attr(obj, name):
try:
eval("obj.%s()" % name)
return True
except AttributeError as e:
return False
a = Base()
if has_attr(a, "test"):
eval("a.test()")
# 输出:
Base
test
test
但是这种方式是有缺陷的,因为test输出了两次,因为我们调用了两次test(),这跟我们想要的效果不一样。如果用hasattr呢,这个函数就不会在判断的时候调用一次了。
getattr()
有了判断属性是否存在的函数,那么就得有获取属性的函数了
def getattr(object, name, default=None): # known special case of getattr
"""
getattr(object, name[, default]) -> value
Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
When a default argument is given, it is returned when the attribute doesn't
exist; without it, an exception is raised in that case.
"""
pass
从源码注释我们就能知道获取object对象的名为name的属性,想到与object.name,如果提供了default参数,那么当属性不存在的时候,就会返回默认值。同样是上面的例子:
a = Base()
if hasattr(a, "test"):
func = getattr(a, "test")
func()
# 输出:
Base
test
从例子中我们可以看出,hasattr并没有调用test函数,而且getattr获取到的是函数对象,也没有调用它,通过我们主动执行func()才执行了a.test()函数,这样相比于exec和eval就灵活了许多。
setattr
判断和获取属性有了,那么设置属性也是需要的
def setattr(x, y, v): # real signature unknown; restored from __doc__
"""
Sets the named attribute on the given object to the specified value.
setattr(x, 'y', v) is equivalent to ``x.y = v''
"""
pass
将一个特殊值设置给object对象的name属性,相当于x.y = v
class Base:
def __init__(self):
self.name = "name"
a = Base()
setattr(a, "name", "zhangsan")
print(a.name) # 改变原有属性的值
setattr(a, "age", 32)
print(getattr(a, "age")) # 新增不存在的属性,并设置值
虽然setattr(a, "age", 32)等于a.age=32,但是我们不要忘了,这是通过一个字符串来增加的属性。
判断、获取、增加都有了,当然还有删除delattr,这个我们就不详述了,接下来我们要看一个比较重要的方法。
import
在学习exec的时候,我们有一个例子,导入配置文件exec("import config"),针对这种方式python也为我们提供了更好的方法。
def __import__(name, globals=None, locals=None, fromlist=(), level=0): # real signature unknown; restored from __doc__
"""
__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module
Import a module. Because this function is meant for use by the Python
interpreter and not for general use, it is better to use
importlib.import_module() to programmatically import a module.
The globals argument is only used to determine the context;
they are not modified. The locals argument is unused. The fromlist
should be a list of names to emulate ``from name import ...'', or an
empty list to emulate ``import name''.
When importing a module from a package, note that __import__('A.B', ...)
returns package A when fromlist is empty, but its submodule B when
fromlist is not empty. The level argument is used to determine whether to
perform absolute or relative imports: 0 is absolute, while a positive number
is the number of parent directories to search relative to the current module.
"""
pass
在这里我们最需要关注的是formlist参数,先看一个简单的例子:
a = __import__("config")
print(a.KEYWORD)
config是一个py脚本-config.py,内部有一个变量KEYWORD,我们要通过其他py模块来导入这个文件,使用__import__我们就可以把它导入为一个对象,然后使用对象的方式去调用,而不是一直用exec字符串的形式去调用。上面我们说了formlist这个参数需要关注,为什么呢?我们新增了一个模块:comm。模块内有一个脚本function.py
# function.py
def comm_function():
print("test_module")
我们现在想通过动态引入的方式调用comm_function函数,那么按照上面的方式来
a = __import__("comm.function")
a.comm_function()
结果输出:
Traceback (most recent call last):
File "xxx.py", line 10, in <module>
print(a.comm_function())
AttributeError: module 'comm' has no attribute 'comm_function'
意思是comm模块没有comm_function这个属性,为什么是comm模块而不是function呢?我们可以打印一下模块的引入名称print(a.__name__),打印的结果是comm,就是说我们通过上面的方式只是引入comm,而不是function。其实通过源码注释我们就知道了,__import__(A.B),如果fromlist为空,返回的是A包,如果不为空,则返回其子包B。修改一下我们的代码:
a = __import__("comm.function", fromlist=True)
print(a.__name__)
a.comm_function()
# 输出:
comm.function
test_module
引入的模块和执行函数都正确了,符合了我们的预期要求。
总结
通过以上的函数学习,其中有常用的,也有不常用的,但是这些函数在我们进行框架设计时是必不可少的,尤其是__import__,接下来我们还会继续看框架设计中最重要的一个概念--元编程。学完了这些概念就可以设计框架了。开玩笑的,哪有那么简单。
阅读源码是一种增长知识的最快捷方式,但是前提是基础一定要打好。否则看源码是一头雾水。我们整理完这些概念后,在找几个源码库看看,学习一下里面的设计理念。
来源:https://www.cnblogs.com/cherry-tang/p/11003646.html


猜你喜欢
- 前言WebStorm是一款强大的前端开发软件,vue是当前前端热门技术栈。对于WebStorm默认的配置,它会把vue当成文本文件处理,从而
- 前言本系列文章将依据官方文档讲解Webots R2022b的使用教程(Windows10环境)。一、Webots是什么?Webots是一个开
- 引言最近两年都是在使用 react 进行项目开发,看技术博客都是针对 react 和 javaScript 高级方面的,对 vue 的知识基
- rebase在git中是一个非常有魅力的命令,使用得当会极大提高自己的工作效率;相反,如果乱用,会给团队中其他人带来麻烦。它的作用简要概括为
- 1. File->Setting 打开如下所示的对话框,可以在Editor->General->Font下设置
- mysql-5.6.14-win32为免安装解压缩版,安装版(http://dev.mysql.com/downloads/installe
- 模式特点:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。我们来看一下下面这样的程序结构:c
- 前言:我目前使用的服务器为centos6.x 系统自带的python的版本为2.6.x,但是目前无论是学习还是使用python,python
- 大家是否经常遇到在关闭网页的时候,会看到一个确定是否离开当前页面的提示框?想一些在线测试系统、信息录入系统等就经常会有这一些提示,避免用户有
- 摘要:在学习与科研中,经常会遇到一些数学运算问题,使用计算机完成运算具有速度快和准确性高的优势。Python的Numpy包具有强大的科学运算
- PDOStatement::getColumnMetaPDOStatement::getColumnMeta — 返回结果集中一列的元数据(
- 在 Python 整型对象所存储的位置是不同的, 有一些是一直存储在某个存储里面, 而其它的, 则在使用时开辟出空间.说这句话的理由, 可以
- 今天一个域名查询系统出现故障,该系统是用的ASP调用XMLHTTP获取whois库的数据,具体错误如下: msxml3.dll 错
- 发现问题当我用pip安装好opencv-pyton后,我激动得在python项目中导入cv2就像这样:import cv2 as cvbut
- 接上篇python smtplib模块自动收发邮件功能(一) ,用python smtplib模块实现了发送邮件程序了,那么接下来我们需要现
- 先介绍一下jsSmarty Project:简要说明:利用JavaScript?在客户端完成SmartyPhp的工作,加速模板开发。主要是利
- 一 介绍 在笔者支持业务过程中,经常遇到开发咨询replace into 的使用场景以及注意事项,这里做个总结。从功能原理,性能
- virtualenv是一个创建隔绝的Python环境的工具。virtualenv创建一个包含所有必要的可执行文件的文件夹,用来使用Pytho
- PyCharm 2020.1版安装破解教程,亲测好用 一、安装第一步下载版本一定得是:2020.1其他版本下载地址:https://www.
- 当使用vue做登录的时候,我们会把拿到的部分用户信息存在vuex+cookie中,我们知道,vuex的数据是会随着浏览器刷新而丢失的,此时我