Ext.: http://www.jb51.net/article/87479.htm
This article mainly introduces the reflection in Python, and the simple application of this mechanism, familiar with Java programmer, must often deal with class.forname. In many frameworks (the Spring,eclipse plugin mechanism) relies on the reflection capabilities of Java, and in Python, it also has a strong reflective ability, this article will do a brief introduction
A friend who is familiar with programming languages should know the mechanism of "reflection". Python, as a dynamic language, certainly does not lack this important function. However, there are few detailed or profound analytical papers on the web. The following is an example of a web route that illustrates the use and core nature of the Python reflection mechanism.
First, preface
?
12345 |
def f1(): print ( "f1是这个函数的名字!" ) s = "f1" print ( "%s是个字符串" % s) |
In the above code, we must distinguish between two concepts, F1 and "F1". The former is the function name of function F1, the latter is just a string called "F1", the two are different things. We can call the function F1 in F1 (), but we can't call the function in the way "F1" (). To be blunt, you can't call a function that looks the same by a string!
Second, the Web instance
Consider a scenario in which, depending on the URL entered by the user, different functions are invoked to implement different operations, that is, the functionality of a URL router, which is one of the core components in the Web framework. Here is an example of a lite version:
First, there is a Commons module, which has several functions for displaying different pages, the code is as follows:
?
12345678 |
def login(): print ( "这是一个登陆页面!" ) def logout(): print ( "这是一个退出页面!" ) def home(): print ( "这是网站主页面!" ) |
Second, there is a visit module, as a program entry, accept user input, display the corresponding page, the code is as follows: (This code is a relatively elementary writing)
?
123456789101112131415 |
import commons
def run():
inp
= input
(
"请输入您想访问页面的url: "
).strip()
if inp
=
= "login"
:
commons.login()
elif inp
=
= "logout"
:
commons.logout()
elif inp
=
= "home"
:
commons.home()
else
:
print
(
"404"
)
if __name__
=
= ‘__main__‘
:
run()
|
We run visit.py, enter: Home, the page results are as follows:
?
12 |
请输入您想访问页面的url: home 这是网站主页面! |
This implements a simple Web routing function, which executes different functions according to different URLs, and obtains different pages.
However, let's consider a question, if there are hundreds or thousands of functions in the Commons module (which is very normal)? Do you write hundreds or thousands of elif in the visit module? Obviously this is impossible! So how is it broken?
Third, reflection mechanism
Looking closely at the code in visit, we will find the URL string entered by the user and the function name of the corresponding call as if! It would be nice if I could call the function directly with this string. However, as we have said before, strings cannot be used to invoke functions. To solve this problem, Python provides us with a powerful built-in function: getattr! We will modify the previous visit, the code is as follows:
?
123456789 |
import commons def run (): INP = input ( "Please enter the URL of the page you want to access:" func = getattr (COMMONS,INP) Code class= "Py Plain" >func () if __name__ = = ' __main__ ' : run () |
First explain the use of the GetAttr function: it receives 2 parameters, the front is an object or module, followed by a string, note! It's a string!
example, the user input is stored in the INP, the INP is a string, the GetAttr function allows the program to commons the module, looking for a member called INP (is called, not equal), the process is equivalent to the process of turning a string into a function name. The resulting result is then assigned to the Func variable, which in effect points to a function in the Commons. Finally, by calling the Func function, the call to the Commons function is implemented. This is completely a dynamic access process, all do not write dead, all according to user input to change.
Execute the above code, and the result is the same as the beginning.
This is the reflection of Python, its core essence is actually to use the form of a String object (module) operation (Find/Get/delete/Add) members, a string-based event-driven!
This passage is not necessarily accurate, but it is probably the meaning.
Iv. further improvement
The above code also has a small flaw, that is, if the user input an illegal URL, such as JPG, because there is no same name in the Commons function, will certainly produce a running error, as follows:
?
1234567 |
请输入您想访问页面的url: jpg Traceback (most recent call last): File "F:/Python/pycharm/s13/reflect/visit.py", line 16, in <module> run() File "F:/Python/pycharm/s13/reflect/visit.py", line 11, in run func = getattr(commons,inp) AttributeError: module ‘commons‘ has no attribute ‘jpg‘ |
What about that? In fact, Python is very comprehensive, and it also provides a hasattr
built-in function called to determine if there is a member in the Commons. Let's modify the code:
?
123456789101112 |
import commons
def run():
inp
= input
(
"请输入您想访问页面的url: "
).strip()
if hasattr
(commons,inp):
func
= getattr
(commons,inp)
func()
else
:
print
(
"404"
)
if __name__
=
= ‘__main__‘
:
run()
|
Through hasattr
the judgment, you can prevent illegal input errors and position them uniformly to the error page.
In fact, friends who have studied Python's built-in functions should note that there are also delattr
setattr
two built-in functions. They are literally well understood.
Python's four important built-in functions: getattr
, hasattr
, delattr
and setattr
more fully implement a string-based reflection mechanism. They all operate on the in-memory module and do not modify the source files.
Five, dynamic import module
The above example is implemented under a specific directory structure, that is, the Commons and visit modules are in the same directory, and all the page handlers are within the Commons module. Such as:
In a real-life environment, however, page-handling functions are often categorized into different modules in different directories, such as:
Are we going to write a bunch of import statements in the Visit module, importing the account, manage, Commons modules one after the other? What if there are 1000 of these modules?
We just finished analyzing the string-based reflection, implementing dynamic function call function, we can not help but want to be able to dynamically import modules? This is totally possible!
Python provides a special way :__import__(字符串参数)。
through which we can implement a similar reflection function. The __import__ () method dynamically imports a module of the same name according to the parameters.
Let's revise the code for the visit module above.
?
123456789101112 |
def run():
inp
= input
(
"请输入您想访问页面的url: "
).strip()
modules, func
= inp.split(
"/"
)
obj
= __import__
(modules)
if hasattr
(obj, func):
func
= getattr
(obj, func)
func()
else
:
print
(
"404"
) if __name__
=
= ‘__main__‘
:
run()
|
Run it:
?
1234 |
请输入您想访问页面的url: commons/home 这是网站主页面! 请输入您想访问页面的url: account/find 这是一个查找功能页面! |
Let's examine the code above:
First, we do not define any line of import statements;
Second, the user's input INP is required to be similar to the "Commons/home" format, in fact, the web framework to simulate the URL address, the left side of the slash bar to the module name, the right side of the module in the member name.
The modules,func = inp.split("/")
user input is then processed, allowing us to obtain 2 strings, which are stored in the modules and Func variables respectively.
Next, the most critical is obj = __import__(modules)
this line, which allows the program to import a module with the same name as the string saved by the modules variable, and assigns it to the obj variable.
In the final call, the GetAttr to call the Func member in the Modules module has the same meaning as before.
Summary: Through the __import__
function, we implement the dynamic module import based on string.
Similarly, there is a small flaw here!
If our directory structure is like this:
Then in the Visit module call statement, you have to modify it, and we want to do this, of course:
?
123456789101112 |
def run():
inp
= input
(
"请输入您想访问页面的url: "
).strip()
modules, func
= inp.split(
"/"
)
obj
= __import__
(
"lib." + modules)
#注意字符串的拼接
if hasattr
(obj, func):
func
= getattr
(obj, func)
func()
else
:
print
(
"404"
)
if __name__
=
= ‘__main__‘
:
run()
|
Changing to such a place seems :obj = __import__("lib." + modules),
to have no problem, import lib.commons
similar to the traditional approach, but there are actually errors when running.
?
1234 |
请输入您想访问页面的url: commons/home 404 请输入您想访问页面的url: account/find 404 |
Why is it? Because of the module import path for lib.xxx.xxx.xxx, __import__ defaults to importing only the directory to the left of the first dot, which is "Lib". We can do a test, create a new file within the visit sibling directory, the code is as follows:
?
12 |
obj = __import__ ( "lib.commons" ) print (obj) |
Execution Result:
?
1 |
<module ‘lib‘ (namespace)> |
How can we solve this problem? Plus fromlist = True parameter!
?
123456789101112 |
def run():
inp
= input
(
"请输入您想访问页面的url: "
).strip()
modules, func
= inp.split(
"/"
)
obj
= __import__
(
"lib." + modules, fromlist
=
True
)
# 注意fromlist参数
if hasattr
(obj, func):
func
= getattr
(obj, func)
func()
else
:
print
(
"404"
)
if __name__
=
= ‘__main__‘
:
run()
|
At this point, the problem of dynamic import module is basically solved, only the last one, that is, in case the user input the wrong module name? For example, the user input somemodules/find
, because there is actually no somemodules
such module, will inevitably error! Does that have a function like the hasattr built-in function above? The answer is NO! In this case, you can only deal with exception handling.
Six, the final thinking
Maybe someone would ask if Python has two built-in functions and not? exec
eval
They are also able to execute strings. Like what:
?
Results:
?
Why not just use them? Why do you have to use it so hard getattr
__import__
?
In fact, in the above example, the core theme is how to use strings to drive different events, such as import modules, call functions, and so on, these are Python's reflection mechanism, is a programming method, the embodiment of design patterns, condensed high cohesion, loosely coupled programming ideas, can not simply use the execution string to replace. Of course, exec and Eval also have its stage, which is often used in web frameworks.
A brief talk on the reflection mechanism of Python