Deep analysis of Python reflection mechanism

Source: Internet
Author: User

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
Def f1 ():    print ("F1 is the name of this function!") ") s =" 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)

Import Commonsdef Run ():    INP = input ("Please enter the URL of the page you want to access:  "). 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:

Please enter the URL of the page you want to visit:  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 Commonsdef Run ():    INP = input ("Please enter the URL of the page you want to access:  "). Strip ()    func = GetAttr (COMMONS,INP)    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:

Please enter the URL of the page you want to visit:  Jpgtraceback (most recent):  File "f:/python/pycharm/s13/reflect/visit.py", line 16, In <module>    run ()  File "f:/python/pycharm/s13/reflect/visit.py", line one, 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 built-in function called hasattr to determine if there is a member in the Commons. Let's modify the code:

Import Commonsdef Run ():    INP = input ("Please enter the URL of the page you want to access:  "). Strip ()    if Hasattr (COMMONS,INP):        func = GetAttr (COMMONS,INP)        func ()    else:        print ("404") if __name__ = = ' __main__ ':    run ()

By Hasattr's judgment, you can prevent illegal input errors and target them uniformly to the error page.

In fact, friends who have studied Python's built-in functions should note that there are two built-in functions for delattr and SetAttr. They are literally well understood.

Python's four important built-in functions: GetAttr, Hasattr, delattr, and SetAttr implement a string-based reflection mechanism in a comprehensive way. They all operate on the in-memory module and do not modify the source files.

Five, dynamic import module

The above example is actually 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 method: __import__ (String argument). With it, 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.

def run ():    INP = input ("Please enter the URL of the page you want to access:  "). 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:

Please enter the URL of the page you want to visit:  commons/home This is the homepage of the website! Please enter the URL of the page you want to visit:  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.

Then, Modules,func = Inp.split ("/") processed the user input, allowing us to get the 2 strings that were stored in the modules and the Func variables.

Next, the most critical is the line of obj = __import__ (modules), 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 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:

def run ():    INP = input ("Please enter the URL of the page you want to access:  "). 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 (

Changed to a place like this: obj = __import__ ("lib." + modules), it seems that there is no problem, and import lib.commons the traditional method similar, but actually run with errors.

Please enter the URL of the page you want to visit:  commons/home404 Please enter the URL of the page you want to visit:  account/find404

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:

obj = __import__ ("lib.commons") print (obj)

Execution Result:

<module ' Lib ' (namespace) >

How can we solve this problem? Plus fromlist = True parameter!

def run ():    INP = input ("Please enter the URL of the page you want to access:  "). Strip ()    modules, Func = Inp.split ("/")    obj = __import__ ("Lib . "+ modules, fromlist=true)  # Note FromList parameter    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 user input somemodules/find, because actually does not exist somemodules This module, inevitably will 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 Python not have two built-in functions exec and eval? They are also able to execute strings. Like what:

EXEC ("Print (' haha ')") Result: haha

Why not just use them? Why bother using getattr,__import__ 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.

Python reflection mechanism in-depth analysis

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.