Programmatically create function specification

后端 未结 3 742
礼貌的吻别
礼貌的吻别 2021-02-04 01:46

For my own entertainment, I was wondering how to achieve the following:

functionA = make_fun([\'paramA\', \'paramB\'])
functionB = make_fun([\'arg1\', \'arg2\',          


        
3条回答
  •  遥遥无期
    2021-02-04 02:16

    Here's another way to do it using functools.wrap, which preserves signature and docstring, at least in python 3. The trick is to create the signature and documentation in dummy functions which never get called. Here are a couple of examples.

    Basic example

    import functools
    
    def wrapper(f):
        @functools.wraps(f)
        def template(common_exposed_arg, *other_args, common_exposed_kwarg=None, **other_kwargs):
            print("\ninside template.")
            print("common_exposed_arg: ", common_exposed_arg, ", common_exposed_kwarg: ", common_exposed_kwarg)
            print("other_args: ", other_args, ",  other_kwargs: ", other_kwargs)
        return template
    
    @wrapper
    def exposed_func_1(common_exposed_arg, other_exposed_arg, common_exposed_kwarg=None):
        """exposed_func_1 docstring: this dummy function exposes the right signature"""
        print("this won't get printed")
    
    @wrapper
    def exposed_func_2(common_exposed_arg, common_exposed_kwarg=None, other_exposed_kwarg=None):
        """exposed_func_2 docstring"""
        pass
    
    exposed_func_1(10, -1, common_exposed_kwarg='one')
    exposed_func_2(20, common_exposed_kwarg='two', other_exposed_kwarg='done')
    print("\n" + exposed_func_1.__name__)
    print(exposed_func_1.__doc__)
    

    And the result is:

    >> inside template.
    >> common_exposed_arg:  10 , common_exposed_kwarg:  one
    >> other_args:  (-1,) ,  other_kwargs:  {}
    >>  
    >> inside template.
    >> common_exposed_arg:  20 , common_exposed_kwarg:  two
    >> other_args:  () ,  other_kwargs:  {'other_exposed_kwarg': 'done'}
    >>  
    >> exposed_func_1
    >> exposed_func_1 docstring: this dummy function exposes the right signature
    

    Calling inspect.signature(exposed_func_1).parameters returns the desired signature. Using inspect.getfullargspec(exposed_func_1), however, still returns the signature of template. At least if you put any arguments common to all functions you want to make in the definition of template, those will appear.

    If this is a bad idea for some reason, please let me know!

    More complicated example

    And you can get much more complicated than this, by layering in more wrappers and defining more distinct behaviors in an inner function:

    import functools
    
    def wrapper(inner_func, outer_arg, outer_kwarg=None):
        def wrapped_func(f):
            @functools.wraps(f)
            def template(common_exposed_arg, *other_args, common_exposed_kwarg=None, **other_kwargs):
                print("\nstart of template.")
                print("outer_arg: ", outer_arg, " outer_kwarg: ", outer_kwarg)
                inner_arg = outer_arg * 10 + common_exposed_arg
                inner_func(inner_arg, *other_args, common_exposed_kwarg=common_exposed_kwarg, **other_kwargs)
                print("template done")
            return template
        return wrapped_func
    
    # Build two examples.
    def inner_fcn_1(hidden_arg, exposed_arg, common_exposed_kwarg=None):
        print("inner_fcn, hidden_arg: ", hidden_arg, ", exposed_arg: ", exposed_arg, ", common_exposed_kwarg: ", common_exposed_kwarg)
    
    def inner_fcn_2(hidden_arg, common_exposed_kwarg=None, other_exposed_kwarg=None):
        print("inner_fcn_2, hidden_arg: ", hidden_arg, ", common_exposed_kwarg: ", common_exposed_kwarg, ", other_exposed_kwarg: ", other_exposed_kwarg)
    
    @wrapper(inner_fcn_1, 1)
    def exposed_function_1(common_exposed_arg, other_exposed_arg, common_exposed_kwarg=None):
        """exposed_function_1 docstring: this dummy function exposes the right signature """
        print("this won't get printed")
    
    @wrapper(inner_fcn_2, 2, outer_kwarg="outer")
    def exposed_function_2(common_exposed_arg, common_exposed_kwarg=None, other_exposed_kwarg=None):
        """ exposed_2 doc """
        pass
    

    It's a bit verbose, but the point is that there is a lot of flexibility in where the dynamic inputs from you (the programmer) come in when using this to create functions, and so with where the exposed inputs (from user of the function) get used.

提交回复
热议问题