SoFunction
Updated on 2024-10-30

python reflection mechanism built-in functions and scene construction details

Advantages of Reflection

Its core essence is actually string-based event-driven, via theString formTo manipulate an object's properties or methods

A concept is presented as a way to understand what its advantages are so that we can know why we should use reflection.

Scene construction

Development of 1 website, consisting of two files, one for the specific execution of operationsOne is the entry file.

Requirements: need to be set in the entry file to allow the user to enter the url, according to the user to enter the url to perform the corresponding operation

# 
def login():
    print("It's a landing page!")
def logout():
    print("It's an exit page!")
def home():
    print("This is the main website page!")
# 
import commons
def run():
    inp = input("Please enter the url of the page you want to visit: ").strip()
    if inp == 'login':
        ()
    elif inp == 'logout':
        ()
    elif inp == 'index':
        ()
    else:
        print('404')
if __name__ == '__main__':
    run()

(of a computer) runrunAfter the method, the results are as follows:

Please enter the url of the page you want to visit: login
This is a landing page!

Q: The above uses if judgment to execute the specified function based on each url request, if theThere are 100 operations in it, and it would be inappropriate to use if judgments anymore Answer: Using the python reflection mechanism, theThe contents of the document remain unchanged and the document is modified to read as follows

import commons
def run():
    inp = input("Please enter the url of the page you want to visit: ").strip()
    if hasattr(commons, inp):
        getattr(commons, inp)()
    else:
        print("404")
if __name__ == '__main__':
    run()

Using these lines of code, it is possible to cope withCalls to any number of page functions in the file!

Built-in Functions in Reflection

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 .
    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

getattr()The first argument to the function needs to be an object, and in the example above, I imported the customizedcommonsmodule, commons is an object; the second argument specifies the name of a method in the preceding object.

getattr(x, 'y')Equivalent to executing the. If the second parameter is a method that does not exist in the previous object, the function will throw an exception and exit. So at this point, for the sake of program robustness, we need to determine whether the method exists in the object first, so we use thehasattr()function (math.)

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

hasattr()The function returns whether the object has a property with the specified name, which simply means that it checks whether a method with the same name as the second parameter can be found in the object with the first parameter. The explanation of the source code also says that the implementation of the function is actually a call to thegetattr()function, except that it catches the exception. So with this function, we can go ahead and determine if the object has this method in it, and if it does, we can use thegetattr()to get the method.

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 `` = v''
    """
    pass

setattr()function is used to reassign a method in the specified object (assigning the new function body/method body to the specified object name) takes effect only in the memory of this program run.setattr(x, 'y', v)equivalence = v

delattr

def delattr(x, y): # real signature unknown; restored from __doc__
    """
    Deletes the named attribute from the given object.
    delattr(x, 'y') is equivalent to ``del ''
    """
    pass

Deletes the specified method in the specified object. Special note: the method is only deleted in the memory of the running program, and does not affect the contents of the file.

__import__ module reflection

Continuing with the example of the website above, now a backend file can no longer meet my needs, this time I need to divide the backend file according to the function, now I have added a newThis user class file also needs to be imported to the home page for calling.

However, for the website example above, I've written it to death to only be able to specify thecommonsmodule's methods are called arbitrarily, and there is now a newusermodule, so at this point I have to use if judgment again?

A: No, use the functions that come with Python__import__

Since the import of modules also requires the use of Python reflection features, the module name has to be added to the url as well, so now the url request becomes something likecommons/visitforms

# 
def add_user():
    print('Add User')
def del_user():
    print('Delete user')
# 
def login():
    print("It's a landing page!")
def logout():
    print("It's an exit page!")
def home():
    print("This is the main website page!")
# 
def run():
    inp = input("Please enter the url of the page you want to visit: ").strip()
    # modules for the imported modules, func for the methods inside the imported modules.
    modules, func = ('/')
    obj_module = __import__(modules)
    if hasattr(obj_module, func):
        getattr(obj_module, func)()
    else:
        print("404")
if __name__ == '__main__':
    run()

final executionrunfunction with the following results:

Please enter the url of the page you want to visit: user/add_user
Add User
Please enter the url of the page you want to visit: user/del_user
Delete User

We can appreciate it now.__import__What it does now is to import strings as modules.

But what if the structure of my site becomes the following

|- 
|- 
|- 
|- lib
    |- __init__.py
    |- 

Now I want to be invisitCalls in the pagelibtake overconnectdbIs it ok to call the methods in the module, or in the same way as before?

# 
def conn():
    print("Connected to mysql.")
# 
def run():
    inp = input("Please enter the url of the page you want to visit: ").strip()
    # modules for the imported modules, func for the methods inside the imported modules.
    modules, func = ('/')
    obj_module = __import__('lib.' + modules)
    if hasattr(obj_module, func):
        getattr(obj_module, func)()
    else:
        print("404")
if __name__ == '__main__':
    run()

(of a computer) runruncommand with the following results:

Please enter the url of the page you want to access: connectdb/conn
404

The result shows that it can't be found, and in order to test calling modules under lib, I ditched support for all sibling directory modules, so we need to look at the__import__source code (computing)

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__('', ...)
    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

__import__function has afromlistparameter, the source code explains that if a module is imported in a package, this parameter if empty, then return this package object, if this parameter is not empty, then return the module object specified below the package, so we are above the return of the package object, so it will return 404 results, now modify the following:

# 
def run():
    inp = input("Please enter the url of the page you want to visit: ").strip()
    # modules for the imported modules, func for the methods inside the imported modules.
    modules, func = ('/')
    # Only added fromlist=True
    obj_module = __import__('lib.' + modules, fromlist=True)
    if hasattr(obj_module, func):
        getattr(obj_module, func)()
    else:
        print("404")
if __name__ == '__main__':
    run()

Run the run method with the following results:

Please enter the url of the page you want to access: connectdb/conn
Connected mysql

It worked, but I wrote it to death.lib prefixIt's the equivalent of abandoning thecommonscap (a poem)usertwo imported functions, so the above code is not perfect, the demand is complex, or need to do a little judgment on the request url

def getf(module, func):
    """
    Withdrawal of the public portion
    """
    if hasattr(module, func):
        func = getattr(module, func)
        func()
    else:
        print('404')
def run():
    inp = input("Please enter the url of the page you want to visit: ").strip()
    if len(('/')) == 2:
        # modules for the imported modules, func for the methods inside the imported modules.
        modules, func = ('/')
        obj_module = __import__(modules)
        getf(obj_module, func)
    elif len(("/")) == 3:
        p, module, func = ('/')
        obj_module = __import__(p + '.' + module, fromlist=True)
        getf(obj_module, func)
if __name__ == '__main__':
    run()

Run the run function with the following results:

Please enter the url of the page you want to access: lib/connectdb/conn
Connected mysql
Please enter the url of the page you want to visit: user/add_user
Add User

Of course you can continue to optimize the code, now only judged with 1 slash and 2 slashes, if the directory level more it, this is not considered for the time being, this time is to understand python's reflection mechanism

The above is python reflection mechanism built-in functions and scene construction details, more information about python reflection mechanism built-in functions please pay attention to my other related articles!