Matching function parameters in Python

Suppose I have a function called generator that returns a 4-tuple with randomly selected values ​​in certain predefined ranges. Let's say that the tuple has the form (age, sex, location, marital_status) :

 age is in range(5, 85) sex is a member of the set {"m", "f"} location is a member of the set of all the cities in California marital_status is a member of {"married", "single", "separated"} 

On the other hand, let's say I defined 20 different functions with the following definitions:

 def p1 (age, sex, location, marital_status) def p2 (age, sex, location, marital_status) . . 

where p1 should receive parameters with values ​​of the following form:

 `age` must be in the range 20 to 45 `sex` must be male `location` could be any city in Southern California `marital_status` could be either single or married 

and imagine a different set of values ​​for p2 up to p20 .

What is a pragmatic way to determine which set of generated values ​​corresponds to which function?

In this case, all definitions where exactly the same, but I can provide examples where there may be slight differences in definitions, for example p18 may be def p1 (age, location) with specific restrictions on the range of possibilities for age and location .

PS Patterns are not necessarily mutually exclusive, that is, a set of generated values ​​may also correspond to more than one function.

+7
function python parameter-passing pattern-matching
source share
3 answers

Like the pythonic path in Python 3.X (but not 2.X), you can attach annotation information (arbitrary user data about the arguments and results of functions) to a function object. Here you can use this function in the decorator to wrap your function to check the range of your arguments.

For example, you can use the following range test function:

 def rangetest(func): def onCall(**kargs): argchecks = func.__annotations__ if all(val in range(*argchecks.get(arg)) for arg,val in kargs.items()): return func(**kargs) else : print ("invalid arg range") return onCall @rangetest def func(a:(1, 5), b:(4,7), c:(0, 10)): print(a + b + c) 

Demo:

 func(a=2, b=6, c=8) 16 func(a=2, b=6, c=8) invalid arg range 

There is something here. Firstly, since the annotation information is in the dictionary (python returns it as a dictionary), and the dictionaries do not have a specific order, you need to use the keyword arguments in your function to be able to get the relative range in the annotation information dictionary.

Also here I just used a numerical range, but you can use some custom ranges, such as a list of words, like what you show in your question. But inside all you need to check its type, then depending on its type use the correct operation

 all(kwargs.get(arg) in range(*arg_range) if is instance (arg_range,tuple) else kwargs.get(arg) in arg_range for arg,arg_range in argchecks.items()) 
+1
source share
 # define test t1 for function p1 def t1(params): return params["age"] in range(5, 85) \ and params["sex"] in ["m", "f"] \ and cityof(params["location"], "California") \ and params["marital_status"] in ["married", "single", "separated"] # and similarly for other p* functions # define functions def p1(params): ... def p2(params): ... # and so on # bind tests and functions RULES = { (t1, p1), (t2, p2), ... } # have the right functions called def go(params): for rule in RULES: if rule[0](params): rule[1](params) # test it go({"age": 6, "sex": "m", "location": "somewhere", "marital_status": "single"}) 

Just a few comments:

  • Tests can be included in the functions themselves.
  • Monitoring functions should also cope with missing parameters.
  • It is also possible to directly use functional parameters.

There are actually several options, but the basic principle is the same:
Find the appropriate function and call it.

+1
source share

If you want to add formatted document lines to your functions (instead of checking the types of each argument), you can do the following:

 # This function has a foratted doc string. # :argument: Truth condition to be evaluated # If any condition is False, function won't be called def party_list(name, age, sex): """ :name:"{}" != "Dad" :age:17< {} <25 :sex:True """ print("You're invited to my party, {}!".format(name)) # We will make some sample data keys = ["name", "age", "sex"] values = [["John", 24, "Male"], ["Sarah", 25, "Female"], ["Pikachu", 2, "Pokemon"], ["Pizza guy", 18, "Male"]] # Turn it in to a dictionary for key, value in enumerate(values): values[key] = {t:p for t, p in zip(keys, value)} # These conditions can be dynamically made for each function, # because we have access to the doc string from the outside conditions = list(filter(lambda c: ':' in c, party_list.__doc__.split('\n'))) for friend in values: for case in conditions: tag, code = case.split(':')[1:] if not eval(code.format(friend[tag])): break else: party_list(friend["name"], friend["age"], friend["sex"]) 
+1
source share

All Articles