A Free Trial That Lets You Build Big!
Start building with 50+ products and up to 12 months usage for Elastic Compute Service
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.
def F1 (): Print ("F1 is the name of this function!") "" F1 "Print ("%s is a string " % 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:
def login (): Print (" This is a landing page!") ")def logout (): print(" This is an exit page!) def Home (): print(" This is the homepage of the website!") ")
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)
ImportCommonsdefrun (): InP= Input ("Please enter the URL of the page you want to visit:"). Strip ()ifINP = ="Login": Commons.login ()elifINP = ="Logout": Commons.logout ()elifINP = ="Home": Commons.home ()Else: Print("404")if __name__=='__main__': Run ()
We run visit.py, enter: Home, the page results are as follows:
Please enter the page you want to visit Url:home this is the homepage of the website!
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:
Import Commons def run (): = Input (""). Strip () = getattr (COMMONS,INP) 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:
" f:/python/pycharm/s13/reflect/visit.py " in <module> "f:/python/pycharm/s13/reflect/visit.py" inch Run ='Commons'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:
ImportCommonsdefrun (): InP= Input ("Please enter the URL of the page you want to visit:"). Strip ()ifhasattr (COMMONS,INP): Func=getattr (COMMONS,INP) func ()Else: Print("404") if __name__=='__main__': Run ()
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
setattr two built-in functions. They are literally well understood.
Python's four important built-in functions:
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.
defrun (): InP= Input ("Please enter the URL of the page you want to visit:"). Strip () modules, Func= Inp.split ("/") obj=__import__(modules)ifhasattr (obj, func): Func=getattr (obj, func) func ()Else: Print("404") if __name__=='__main__': Run ()
Please enter the page you want to visit url:commons/Home This is the homepage of the website! Please enter the page you want to visit Url:account/Find this is a Find feature page!
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.
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:
defrun (): InP= Input ("Please enter the URL of the page you want to visit:"). Strip () modules, Func= Inp.split ("/") obj=__import__("lib."+ modules)#note the concatenation of strings ifhasattr (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.
Please enter the url:commons/home404 You want to access the page, enter the Url:account/find404 you want to access thepage
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:
__import__ ("lib.commons") Print (obj)
<module ' Lib ' (namespace) >
How can we solve this problem? Plus fromlist = True parameter!
defrun (): InP= Input ("Please enter the URL of the page you want to visit:"). Strip () modules, Func= Inp.split ("/") obj=__import__("lib."+ Modules, fromlist=true)#Note the fromlist parameter ifhasattr (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?
eval They are also able to execute strings. Like what:
EXEC ("Print (' haha ')")
Why not just use them? Why do you have to use it so hard
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.
ImportSysmy_moudle= sys.modules[__name__]#modules in the current PY filedefA ():Print('a')defb ():Print('b') cmd=input ('>>:'). Strip ()ifhasattr (My_moudle, cmd): Func=getattr (my_moudle, cmd) func ()
Briefly talk about the reflection mechanism of Python
Start building with 50+ products and up to 12 months usage for Elastic Compute Service