getattr in python

Keywords: Python Attribute Windows rsync

Catalog

getattr details

Preface

These two days, we are optimizing Tencent's cloud migration platform. SmartMS When using the middleware (go2cloud_api). Some of the interfaces involved multiple server system types and the judgment of migration type. As a result, an interface tends to run on 70-80 lines. Modifying an interface, debugging, and changing parameters will take several minutes to unravel the logic in the middle. Adding the same interface, different system types (mainly windows, will take several minutes to unravel.Linux) involves different stacks of technologies, and there are two different code styles of people writing on the same interface. Ultimately, the code structure, functions, and classes are messy. Just during the epidemic, people in Hubei (want to cry without tears), have plenty of time, and want to do a code refactoring. First, look at the original code.

The above code is just part of the code for windows. Because it also involves checking the common parameters of interfaces, different migration system types call different interfaces of agent s. The same interface uses a lot of if...else.

First of all, the interface itself is not complex. Core technologies are not covered by this article. If you are interested in researching code or trying out the Tencent Cloud Migration Platform (SmartMS), you can leave a message. Free experience.

1. Eliminate if...else

Because there's too much if involved in the code...Others. Originally intended to use strategy mode + factory mode ( Eliminate if...else However, since the code design pattern was not taken into account at the beginning of the project, changes can now take a lot of work. You can only think of other options.

  • When you redeveloped jumpserver before, you studied the core code. You found that a lot of getattrs were used in the code, where getattrs were mainly used in function calls. For example, let me intercept some of the code below

    def run_command(self, func_name, args):
            """ Attempts to run the given command.
    
                If the command does not execute, or there are any problems
                validating the given GET vars, an error message is set.
    
                func: the name of the function to run (e.g. __open)
                command_variables: a list of 'name':True/False tuples specifying
                which GET variables must be present or empty for this command.
            """
            if not self.check_command_args(args):
                self.response['error'] = 'Invalid arguments'
                print("++++++++++++++++++++++++++++++++ not valid")
                return
    
            func = getattr(self, '_' + self.__class__.__name__ + func_name, None)
            if not callable(func):
                self.response['error'] = 'Command failed'
                return
    
            try:
                func()
            except Exception as e:
                self.response['error'] = '%s' % e
                logger.error("Error occur ------------------------------")
                logger.exception(e)
                logger.error("Error end ------------------------------")

    Getattr is a built-in function in python. In python's official documentation, getattr() is interpreted as:

    getattr(object, name[, default])

    Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object's attributes, the result is the value of that attribute. For example, getattr(x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.

    The primary function of getattr() is to implement the reflection mechanism.That is, you can get a method instance through a string.This is what python calls introspection.

    • You can put a method that a class might call into a configuration file and load it dynamically when needed.
  • There are hasattr,setattr,delattr related to getattr in python, so let's go into more detail about their usage through the following examples.

2. Reflection mechanism

  1. hasattr(object´╝îname)

    • bool determines if an object has a name attribute, such as:

      foo = test()
      
      hasattr(foo,'setName') #Determines whether setName exists, and returns True if it exists.

2.getattr(object,name,default)

  • Returns the value of name (method address) if the name attribute (method) exists, otherwise returns the default value.

    # foo.py
    
    def name():
        return "hello"
    
    # main.py
    import foo
    
    getattr(foo,'name',"default value") # The name attribute exists, so its value is returned
    # "hello"
    
    getattr(foo,'test',None)
    # None
  • The way the function is called here.

3.setattr(object,name,default)

  • Set a new property for the class

    class Foo(object):
        def __init__(self,sex):
            self.sex = sex
    
    foo = Foo('man')      # instantiate  
    setattr(Foo,'age',18) # Set a new property
    
    foo.age
    # 18
  • Changing the value of the original class attribute

    class Foo(object):
        def __init__(self,sex):
            self.sex = sex
    
    foo = Foo('man')           # instantiate  
    setattr(Foo,'sex','woman') # Set a new property
    
    foo.sex
    # woman

4.delattr(object,'name')

  • Delete Class Properties

    delattr(foo,'sex')  #Delete attribute sex, original value is `woman`
    
    getattr(foo,'sex','not find')
    #'not find''

3. getattr Details

This article focuses on the use of getattr

1. Functions

  • demo.py

    #!/usr/bin/env python
    # ~*~ coding: utf-8 ~*~
    
    import time
    
    def hello(a, b):
        print('hello')
        return a, b
    
    def test_sleep(a, b):
        print('test_sleep')
        time.sleep(3)
        return a, b
  • main.py

    #!/usr/bin/env python
    # ~*~ coding: utf-8 ~*~
    
    import multiprocessing
    import demo
    
    def run(func, *args):
        print(getattr(demo, func)(*args))
    
    
    if __name__ == "__main__":
        run('hello', "a", "b")
        pool = multiprocessing.Pool(processes=4)
    
        for i in range(10):
            pool.apply_async(run, ('hello', 'a', 'b'))
            pool.apply_async(run, ('test_sleep', 'a', 'b'))
        pool.close()
        pool.join()
    
        print("END ")

Define a string and execute it with getattr, which is somewhat like python's celery use of globals.Pass the name of a function, of course a string, and call this method with a multiprocessing multiprocess.

2. Use in Classes

This method optimizes the code provided at the beginning of the article as follows

Class Code

class SrcAgentClient(CommAgentClient):
    ...
    @property
    async def system_disk_sync_status(self, action="system_migration_heartbeat"):
        ''' Get rsync server service status.
        :param action: a string,match the desAgent api:rsync_status_check
        :return:
        '''
        url = self._api_base_url + action
        if await self._agent_status:
            response = await self.client.fetch(url, method="GET")
            data = json.loads(response.body.decode())
            return data
        else:
            return self.response

    @property
    async def data_disk_sync_status(self, action="data_migration_heartbeat"):
        ''' Get rsync server service status.
        :param action: a string,match the desAgent api:rsync_data_status_check
        :return:
        '''
        url = self._api_base_url + action
        if await self._agent_status:
            response = await self.client.fetch(url, method="GET")
            data = json.loads(response.body.decode())
            return data
        else:
            return self.response

The simplified code uses getattr to determine the incoming disk_type parameter, map the corresponding attributes in the map, and optimize the code.

summary

The getattr built-in attribute plus map simplifies if...else logic code. This code does not take into account the performance of the code. Take time to compare.

Posted by hhheng on Fri, 13 Mar 2020 09:52:31 -0700