Python reflection mechanism is explained in actual application scenarios, and python scenarios
Analysis of the essence and actual application scenarios of the "reflection" mechanism in python
I. Preface
- Def s1 ():
- Print ("s1 is the name of this function! ")
- S = "s1"
- Print ("% s is a string" % s)
In the above Code, we must distinguish two concepts: f1 and f1 ". The former is the function name of function f1, and the latter is just a string called "f1". The two are different things. We can use the f1 () method to call the function f1, but we cannot use the "f1" () method to call the function. To put it bluntly, you cannot use strings to call functions with the same name!
Ii. web instances
In this scenario, different functions are called Based on the URLs entered by users to implement different operations, that is, the functions of a url router, this is one of the core components in the web framework. The following is a simplified example:
First, there is a commons module which contains several functions for displaying different pages. The Code is as follows:
- Def login ():
- Print ("this is a login page! ")
-
- Def logout ():
- Print ("this is an exit page! ")
-
- Def home ():
- Print ("this is the homepage of the website! ")
Secondly, there is a visit module, which serves as the program portal, receives user input and displays the corresponding page. The Code is as follows: (this code is a relatively preliminary writing)
- Import commons
-
- Def run ():
- Indium = input ("Enter the url of the page you want to visit:"). strip ()
- If indium = "login ":
- Commons. login ()
- Elif indium = "logout ":
- Commons. logout ()
- Elif indium = "home ":
- Commons. home ()
- Else:
- Print ("404 ")
-
- If _ name _ = '_ main __':
- Run ()
Run visit. py and enter home. The result is as follows:
- Enter the url: home
- This is the homepage of the website!
This provides a simple WEB routing function. Different functions are executed based on different URLs to obtain different pages.
However, let's consider a question. What if the commons module contains hundreds of functions (which is normal )?. Do you write hundreds of elif entries in the visit module? Obviously, this is impossible! How can this problem be solved?
Iii. reflection mechanism
After carefully observing the code in visit, we will find that the url string entered by the user and the name of the function called are like! If you can use this string to directly call the function! However, we have already mentioned that strings cannot be used to call functions. To solve this problem, python provides us with a powerful built-in function: getattr! Let's modify the previous visit. The Code is as follows:
- Import commons
-
- Def run ():
- Indium = input ("Enter the url of the page you want to visit:"). strip ()
- Func = getattr (commons, indium)
- Func ()
- If _ name _ = '_ main __':
- Run ()
First, describe how to use the getattr function: it receives two parameters, the first is an object or module, followed by a string! Is a string!
In this example, the user input is stored in the indium, And the indium is a string. The getattr function allows the program to go to the commons module and find a member called the indium (that is, not equal ), this process is equivalent to converting a string into a function name. Then, assign the obtained result to the func variable. In fact, func points to a function in commons. Finally, call the func function to call the function in commons. This is completely a process of dynamic access. Everything is not written to death, and all changes are made based on user input.
Execute the above Code and the result is the same as the first one.
This is the reflection of python. Its core essence is to use strings to perform operations (Search/retrieve/delete/Add) on objects (modules, string-based event-driven!
This paragraph is not necessarily accurate, but it probably means this.
Iv. Further Improvement
The above Code also has a flaw, that is, if the user enters an invalid url, such as jpg, because there is no function with the same name in commons, it will certainly produce a running error, as shown below:
- Enter the url to access the page: 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, indium)
- AttributeError: module 'common' has no attribute 'jpg'
What should we do? In fact, python is fully considered. It also provides a built-in function called hasattr to determine whether a member exists in commons. Let's modify the code:
- Import commons
- Def run ():
- Indium = input ("Enter the url of the page you want to visit:"). strip ()
- If hasattr (commons, indium ):
- Func = getattr (commons, indium)
- Func ()
- Else:
- Print ("404 ")
- If _ name _ = '_ main __':
- Run ()
Hasattr can be used to prevent illegal input errors and locate them on the error page.
Actually, if you have studied python built-in functions, you should note that there are two built-in functions: delattr and setattr. They have literally understood their role.
Four important built-in functions of python: getattr, hasattr, delattr, and setattr fully implement the string-based reflection mechanism. They operate on modules in the memory and do not modify the source files.
5. Dynamic import module
The above example can be implemented normally under a specific directory structure, that is, the commons and visit modules are in the same directory, and all page processing functions are in the commons module. For example:
However, in actual use environments, page processing functions are often classified and placed in different modules of different directories, that is:
Do we need to write a lot of import statements in the visit module to import the account, manage, and commons modules one by one? What if there are 1000 such modules?
Just now we have analyzed the string-based reflection and implemented the dynamic function call function. We can't help but wonder if we can dynamically import the module? This is all right!
Python provides a special method: __import _ (string parameter ). Through it, we can implement similar reflection functions. The _ import _ () method dynamically imports modules with the same name based on parameters.
Let's modify the code of the visit module above.
- Def run ():
- Indium = input ("Enter the url of the page you want to visit:"). strip ()
- Modules, func = indium. split ("/")
- Obj = _ import _ (modules)
- If hasattr (obj, func ):
- Func = getattr (obj, func)
- Func ()
- Else:
- Print ("404 ")
- If _ name _ = '_ main __':
- Run ()
Run:
- Enter the url to access the page: commons/home
- This is the homepage of the website!
- Enter the url to access the page: account/find
- This is a search function page!
Let's analyze the above Code:
First, we have not defined any line of import statements;
Secondly, the user's input in the form of "commons/home" is required, which is to simulate the url address in the web framework. The left side of the slash points to the module name, point to the member name in the module on the right.
Then, modules, func = indium. split ("/") processes user input, so that the two strings we get are saved in the modules and func variables respectively.
Next, the most critical line is the obj = _ import _ (modules) line, which allows the program to import the module with the same name as the string saved by the modules variable, and assign it to the obj variable.
In the final call, the meaning of calling the func member in the getattr de-modules module is the same as before.
Conclusion: Through the _ import _ function, we have implemented a string-based dynamic module import.
Likewise, there is a flaw here!
If our directory structure is like this:
Therefore, the visit module CALL statement must be modified. We will take it for granted:
- Def run ():
- Indium = input ("Enter the url of the page you want to visit:"). strip ()
- Modules, func = indium. split ("/")
- Obj = _ import _ ("lib." + modules) # note the concatenation of strings
- If hasattr (obj, func ):
- Func = getattr (obj, func)
- Func ()
- Else:
- Print ("404 ")
- If _ name _ = '_ main __':
- Run ()
Changed this point: obj = _ import _ ("lib. "+ modules), it seems that there is no problem, and import lib. the traditional methods of commons are similar, but errors may occur during running.
- Enter the url to access the page: commons/home
- 404
- Enter the url to access the page: account/find
- 404
Why? For modules such as lib. xxx, __import _ only imports the directory on the left of the starting dot, that is, "lib ". We can perform a test to create a file in the directory of the same level as visit. The Code is as follows:
- Obj = _ import _ ("lib. commons ")
- Print (obj)
Execution result:
- <Module 'lib' <span = "" font-size: 14px; white-space: pre; "=" "> (namespace)>
How can this problem be solved? Add the fromlist = True parameter!
- Def run ():
- Indium = input ("Enter the url of the page you want to visit:"). strip ()
- Modules, func = indium. split ("/")
- Obj = _ import _ ("lib." + modules, fromlist = True) # note the fromlist parameter.
- If hasattr (obj, func ):
- Func = getattr (obj, func)
- Func ()
- Else:
- Print ("404 ")
- If _ name _ = '_ main __':
- Run ()
At this point, the problem of the dynamic import module is basically solved, with only the last one left. What if the user inputs the wrong Module name? For example, if a user inputs somemodules/find, an error is returned because the somemodules module does not exist! Is there a function similar to the hasattr built-in function above? The answer is no! In this case, you can only handle exceptions.
6. Last thought
Someone may ask if python has two built-in functions exec and eval? They can also execute strings. For example:
- Exec ("print ('hahaha ')")
Result:
- Haha
Can't I use them directly? So hard to use getattr, _ import _ Why?
In fact, in the above example, the core topic is how to use strings to drive different events, such as import modules and call functions. These are all python reflection mechanisms, it is the embodiment of a programming method and design mode. It embodies the High Cohesion and loose coupling programming ideas and cannot be simply replaced by execution strings. Of course, exec and eval also have their stage and are often used in web frameworks.