Dynamically add methods to a class in Python 3.0

前端 未结 3 487
遥遥无期
遥遥无期 2021-02-06 01:10

I\'m trying to write a Database Abstraction Layer in Python which lets you construct SQL statments using chained function calls such as:

results = db.search(\"bo         


        
3条回答
  •  佛祖请我去吃肉
    2021-02-06 01:38

    You can simply add the search functions (methods) after the class is created:

    class Search:  # The class does not include the search methods, at first
        def __init__(self):
            self.conditions = {}
    
    def make_set_condition(option):  # Factory function that generates a "condition setter" for "option"
        def set_cond(self, value):
            self.conditions[option] = value
            return self
        return set_cond
    
    for option in ('price', 'name'):  # The class is extended with additional condition setters
        setattr(Search, option, make_set_condition(option))
    
    Search().name("Nice name").price('$3').conditions  # Example
    {'price': '$3', 'name': 'Nice name'}
    

    PS: This class has an __init__() method that does not have the family parameter (the condition setters are dynamically added at runtime, but are added to the class, not to each instance separately). If Search objects with different condition setters need to be created, then the following variation on the above method works (the __init__() method has a family parameter):

    import types
    
    class Search:  # The class does not include the search methods, at first
    
        def __init__(self, family):
            self.conditions = {}
            for option in family:  # The class is extended with additional condition setters
                # The new 'option' attributes must be methods, not regular functions:
                setattr(self, option, types.MethodType(make_set_condition(option), self))
    
    def make_set_condition(option):  # Factory function that generates a "condition setter" for "option"
        def set_cond(self, value):
            self.conditions[option] = value
            return self
        return set_cond
    
    >>> o0 = Search(('price', 'name'))  # Example
    >>> o0.name("Nice name").price('$3').conditions
    {'price': '$3', 'name': 'Nice name'}
    >>> dir(o0)  # Each Search object has its own condition setters (here: name and price)
    ['__doc__', '__init__', '__module__', 'conditions', 'name', 'price']
    
    >>> o1 = Search(('director', 'style'))
    >>> o1.director("Louis L").conditions  # New method name
    {'director': 'Louis L'}
    >>> dir(o1)  # Each Search object has its own condition setters (here: director and style)
    ['__doc__', '__init__', '__module__', 'conditions', 'director', 'style']
    

    Reference: http://docs.python.org/howto/descriptor.html#functions-and-methods


    If you really need search methods that know about the name of the attribute they are stored in, you can simply set it in make_set_condition() with

           set_cond.__name__ = option  # Sets the function name
    

    (just before the return set_cond). Before doing this, method Search.name has the following name:

    >>> Search.price
    
    

    after setting its __name__ attribute, you get a different name:

    >>> Search.price
    
    

    Setting the method name this way makes possible error messages involving the method easier to understand.

提交回复
热议问题