Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically create function specification

Tags:

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

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

equivalent to

def functionA(paramA, paramB):     print(paramA)     print(paramB)  def functionB(arg1, arg2, arg3):     print(arg1)     print(arg2)     print(arg3)  

This means the following behaviour is required:

functionA(3, paramB=1)       # Works functionA(3, 2, 1)           # Fails functionB(0)                 # Fails 

The focus of the question is on the variable argspec - I comfortable creating the function body using the usual decorator techniques.

For those that are interested, I was playing around with trying to programmatically create classes like the following. Again the difficulty is in generating the __init__ method with programmatic parameters - the rest of the class appears straightforward using a decorator or maybe a metaclass.

class MyClass:     def __init__(self, paramA=None, paramB=None):         self._attr = ['paramA', 'paramB']         for a in self._attr:             self.__setattr__(a, None)      def __str__(self):         return str({k:v for (k,v) in self.__dict__.items() if k in self._attributes}) 
like image 342
Zero Avatar asked Nov 18 '14 05:11

Zero


People also ask

How to dynamically write code in Python?

Method 1: exec() ? Python's built-in exec() executes the Python code you pass as a string or executable object argument. This is called dynamic execution because, in contrast to normal static Python code, you can generate code and execute it at runtime. This way, you can run programmatically-created Python code.

How to create a dynamic function in Python?

Python Code can be dynamically imported and classes can be dynamically created at run-time. Classes can be dynamically created using the type() function in Python. The type() function is used to return the type of the object. The above syntax returns the type of object.

How do you pass a dynamic value in Python?

Using exec() method to create dynamically named variables Here we are using the exec() method for creating dynamically named variable and later assigning it some value, then finally printing its value.

How do you call a function from its name stored in a string in python?

String To Function Using The eval() Function In Python We can also use the eval() function to convert a string to a function. Here, the input string is the name of the function. In the eval() function, we will pass the name of the function and the ' () ' separated by the addition symbol ' + '.


2 Answers

You can use exec to construct the function object from a string containing Python code:

def make_fun(parameters):     exec("def f_make_fun({}): pass".format(', '.join(parameters)))     return locals()['f_make_fun'] 

Example:

>>> f = make_fun(['a', 'b']) >>> import inspect >>> print(inspect.signature(f).parameters) OrderedDict([('a', <Parameter at 0x1024297e0 'a'>), ('b', <Parameter at 0x102429948 'b'>)]) 

If you want more functionality (e.g., default argument values), it's a matter of adapting the string that contains the code and having it represent the desired function signature.

Disclaimer: as pointed out below it's important that you verify the contents of parameters and that the resulting Python code string is safe to pass to exec. You should construct parameters yourself or put restrictions in place to prevent the user from constructing a malicious value for parameters.

like image 96
Simeon Visser Avatar answered Oct 30 '22 13:10

Simeon Visser


One of the possible solutions using a class:

def make_fun(args_list):     args_list = args_list[:]      class MyFunc(object):         def __call__(self, *args, **kwargs):             if len(args) > len(args_list):                 raise ValueError('Too many arguments passed.')              # At this point all positional arguments are fine.             for arg in args_list[len(args):]:                 if arg not in kwargs:                     raise ValueError('Missing value for argument {}.'.format(arg))              # At this point, all arguments have been passed either as             # positional or keyword.             if len(args_list) - len(args) != len(kwargs):                 raise ValueError('Too many arguments passed.')              for arg in args:                 print(arg)              for arg in args_list[len(args):]:                 print(kwargs[arg])      return MyFunc()  functionA = make_fun(['paramA', 'paramB']) functionB = make_fun(['arg1', 'arg2', 'arg3'])  functionA(3, paramB=1)       # Works try:     functionA(3, 2, 1)           # Fails except ValueError as e:     print(e)  try:     functionB(0)                 # Fails except ValueError as e:     print(e)  try:     functionB(arg1=1, arg2=2, arg3=3, paramC=1)                 # Fails except ValueError as e:     print(e) 
like image 34
Maciej Gol Avatar answered Oct 30 '22 13:10

Maciej Gol