How to replace part of a function call with variable in Python? - python

I have a dictionary with some function expressions as values. Each of the values are very similar, except the part in the middle. In the following example, only earn_yld, free_cash_flow_yield and eps_growth are different in the long formula.
factor_bql = {
"ltm_earnings_yield": bq.func.dropna(bq.data.earn_yld(as_of_date=bq.func.RANGE(params['start'],params['end']))),
"ltm_fcf_yield": bq.func.dropna(bq.data.free_cash_flow_yield(as_of_date=bq.func.RANGE(params['start'],params['end']))),
'ltm_eps_growth': bq.func.dropna(bq.data.eps_growth(as_of_date=bq.func.RANGE(params['start'],params['end'])))
}
Is there any way to write a function or variable to simplify the values of the dictionary to something like
def simple_formula(xyz):
... ...
factor_bql = {
"ltm_earnings_yield": simple_formula('earn_yld'),
"ltm_fcf_yield": simple_formula('free_cash_flow_yield'),
'ltm_eps_growth': simple_formula('eps_growth')
}

I'd do this in following way:
def simple_formula(fn):
return bq.func.dropna(fn(as_of_date=bq.func.RANGE(params['start'],params['end'])))
factor_bql = {
"ltm_earnings_yield": simple_formula(bq.data.earn_yld),
"ltm_fcf_yield": simple_formula(bq.data.free_cash_flow_yield),
'ltm_eps_growth': simple_formula(bq.data.eps_growth)
}
So, functions themselves (not their names) are parameters of simple_formula.

You can use the globals function to call a function in the current module by the string representation of its name.
def func1(bar):
return "func1" + str(bar)
def func2(bar):
return "func2" + str(bar)
def simple_formula(func_name):
return globals()[func_name](bar="baz")
factor_bql = {
"key1": simple_formula("func1"),
"key2": simple_formula("func2"),
}
print(factor_bql["key2"]) # prints "func2baz"

Assuming bq.data is some object:
def simple_formula(xyz):
method = getattr(bq.data, xyx) # get a method by its name
return bq.func.dropna(method(as_of_date=bq.func.RANGE(params['start'],params['end'])))

Related

Reducing a function using dictionary

I have two functions that are very similar:
def hier_group(self):
if self.sku:
return {f"{self.hierarchic}": f"${self.hierarchic}", "id": "$id", "ix": "$ix"}
else:
return {f"{self.hierarchic}": f"${self.hierarchic}", "ix": "$ix"}
def hier_group_merge(self):
if self.sku:
return {f"{self.hierarchic}": f"${self.hierarchic}", "id": "$id"}
else:
return {f"{self.hierarchic}": f"${self.hierarchic}"}
I am trying to reduce into 1 function that has only one if/else.
The only difference in both functions is "ix": "$ix".
What I am trying to do is the following:
def hier_group(self, ix=True):
if self.sku:
return {f"{self.hierarchic}": f"${self.hierarchic}", "id": "$id" f'{',"ix": "$ix"' if ix == True else ""}'}
else:
return {f"{self.hierarchic}": f"${self.hierarchic}" f'{',"ix": "$ix"' if ix == True else ""}'}
But it's getting trick to return , "ix": "$ix".
Build a base dictionary, then add keys as appropriate.
def hier_group(self, ix=True):
d = { f'{self.hierarchic}': f'${self.hierarchic}' }
if self.sku:
d['id'] = '$id'
if ix:
d['ix'] = '$ix'
return d
However, there are many who believe using two functions, rather than having one function behave like two different functions based on a Boolean argument, is preferable.
def hier_group(self):
d = { f'{self.hierarchic}': f'${self.hierarchic}' }
if self.sku:
d['id'] = '$id'
return d
def hier_group_with_ix(self):
d = self.hier_group()
d.update('ix': '$ix')
return d
You might also use a private method that takes an arbitrary list of attribute names.
# No longer needs self, so make it a static method
#staticmethod
def _build_group(attributes):
return {f'{x}: f'${x} for x in attributes}
def build_group(self, ix=True):
attributes = [self.hierarchic]
if ix:
attributes.append('ix')
if self.sku:
attributes.append('id')
return self._build_group(attributes)
You will probably ask: why is using a Boolean attribute here OK? My justification is that you aren't really altering the control flow
of build_group with such an argument; you are using it to
build up a list of explicit arguments for the private method. (The dataclass decorator in the standard library takes a similar approach: a number of Boolean-valued arguments to indicate whether various methods should be generated automatically.)
You can avoid repeating common parts:
def hier_group(self, ix=True):
out = {f"{self.hierarchic}": f"${self.hierarchic}"}
if self.sku:
out["id"] = "$id"
if ix:
out["ix"] = "$ix"

How to call functions and variables from a dictionary or a json file

I am trying to print a string, call functions and variables in a line.
such as [Hello! %(job), %(name)s, (function_name)]->[Hello! student, John, This is the function.]
json01.json
{
"test" : "Hello! %(job), %(name)s, (function_name)"
}
test01.py
import json
a = 'test'
name = 'John'
job = 'student'
def function_name(message):
print(message)
with open('json01.json') as json_file:
json_dict = json.load(json_file)
if a in json_dict:
print(json_dict[a] %locals())
#This works if there's only variables in the value
#but I don't know how to call functions when the value is not only function's name but also variables..
Is there any simple way to print whatever they are in the value?
or is there another way to do this work?
Sorry for the poor explanation and Thank you!
You can define your own function replacer using regular expressions. I defined an example syntax here as: Hello, !(function_name) where function_name is the name of the function that is called.
Using regular expressions we find all occurences of a function call and try to
evaluate them one by one. If successfull, we replace the function's name with the return value.
import re
def runfunctions(string):
# find all functions defined with our syntax
funcs = re.findall(r'!\((.*?)\)', string)
result = string
# iterate through found functions
for func in funcs:
try:
# try to evaluate with globals()[func]() and replace function call
# with return value
result = re.sub(r'!\(' + func + r'\)', globals()[func](), result)
except (KeyError, TypeError) as e:
# if func is not callable or does not exist catch error
print("Error while evaluating functions in string:", e)
# return final result
return result
Note: I used globals instead of locals as otherwise the function is not found.
You can use it like this:
if a in json_dict:
replaced_vars = json_dict[a] % locals()
replaced_funcs = runfunctions(replaced_vars)
print(replaced_funcs)

Python: Use returned values from a function to another function

With the simple function I am able to get data stored in response variable. With a print statement I can get the data showing correctly.
ec2_client = boto3.client("ec2")
def instance_list(instance_name):
response = ec2_client.describe_instances(
Filters=[
{
'Name': 'tag:Name',
'Values': [ instance_name ]
}
]
)['Reservations']
return response
#print(response)
if __name__ == '__main__':
my_instance_list = instance_list("example-*")
However while trying to import the value of response from the above function to another function, getting error as NameError: name 'response' is not defined
def my_list():
list = instance_list(response)
print(list)
Looks like something unidentified.
you need to pass the varable to the next function, for example
my_list(instance_list())
the basic idea is to use return value of one function to another function
Your function should be this:
def my_list():
# List is equal to the response value of instance list.
list = instance_list("example-*")
print(list)
Easy example
def add_five_to_number(number):
number += 5
return number
def print_number(number):
higher_number = add_five_to_number(number)
print(higher_number)
test_number = 3
print_number(test_number)
# Returns 8

Is it possible to do "extended" time monitoring in Python using a decorator?

I have a simple set of functions that use each other. For example:
def func(x)
y = func_1(x)
z = func_2(y)
return z
def func_1(x):
return x + 1
def func_2(x)
a = func_a(x)
b = func_b(y)
return b
As you can see, the func is the "root" function that uses func_1 and func_2 and func_2, in its turn, uses func_a and func_b. When I call func I get z as the result.
Now I would like to "modify" or "extend" my functions with a decorator such that in the end (as a result of func) I get not only z but also an object that shows me how much it took to execute this function as well as what functions have been used by the function and how long it took to execute these "sub-functions" as well as what "sub-sub-functions" have been used by what "sub-functions" and how long does it took to execute them. To make it simpler I give an example of what I expect as an "additional" result:
{
'fname' : 'func',
'etime' : 12.000,
'subs' : [
{
'fname' : 'func_1',
'etime' : 2.000,
'subs' : []
},
{
'fname' : 'func_2',
'etime' : 10,
'subs' : [
{
'fname' : 'func_a',
'etime' : 6,
'subs' : []
},
{
'fname' : 'func_b',
'etime' : 4
'subs' : []
}
]
}
]
}
In the above example "fname" means name of the function, "etime" means execution time (how long did it took to execute this function), and "subs" is a list of sub-function that were used by the considered function. For each sub-function we have the same keys ("fname", "etime", "subs"). So, it is a "recursive" structure. If a function did not use any function then "subs" maps to an empty list.
I have started with the following decorator:
def decorate(func):
def wrapper(*args, **kw):
d = {}
d['fname'] = func.__name__
t0 = time.time()
out = func(*args, **kw)
d['etime'] = time.time() - t0
d['subs'] = ?
?.append(d)
return wrapper
But then I stack with the further implementation. I cannot find a solution and am not even sure that it is possible.
The idea is that I use a decorator to extend the number of arguments passed to each function. Each function gets an empty list containing all sub-functions used so far and append itself to this list.
You would be better using a real profiler as suggested.
Still, it can be done with a decorator class. You'll be able to keep track of the subs list with a stack shared between all the decorator's instances.
class profile(object):
#class variable used as a stack of subs list
stack = [[]]
def __init__(self, f):
self.f = f
def __call__(self, *args, **kw):
func = dict(fname = self.f.__name__)
#append the current function in the latest pushed subs list
profile.stack[-1].append(func)
#push a new subs list in the stack
profile.stack.append([])
#execution time of the actual call
t0 = time.time()
out = self.f(*args, **kw)
func['etime'] = time.time() - t0
#pull the subs list from the stack
func['subs'] = profile.stack.pop()
return out
#classmethod
def show(cls):
import json #useful to prettify the ouput
for func in cls.stack[0]:
print json.dumps(func, sort_keys=True, indent=4)
You'll have to decorate all the functions you want to appear in the profile with #profile.
Note that in a real world situation, you might want to handle exceptions when a decorated function fails.
Ouput:
profile.show() shows the list of all the called 'root' functions profiled with all their inners' calls.
{
"etime": 4.5,
"fname": "func",
"subs": [
{
"etime": 1.0,
"fname": "func_1",
"subs": []
},
{
"etime": 3.5,
"fname": "func_2",
"subs": [
{
"etime": 1.5,
"fname": "func_a",
"subs": []
},
{
"etime": 2.0,
"fname": "func_b",
"subs": []
}
]
}
]
}

Don't quote some strings with using json.dumps

I'm working on a Django wrapper for jqGrid (yes, another one, the existing ones don't fit my needs). In my wrapper I'm generating the Javascript code that initializes the grid. This code looks like this:
$('#my-grid').jqGrid({
"option1": 12,
"option2": "option",
"eventHandler": handlerFunction
});
Since I'm generating this code in Python, I've created a dictionary like so:
options = {"option1": 12, "option2": "option", "eventHandler": "handlerFunction"}
I then use json.dumps like so:
js_code = "${'#my-grid').jqGrid(%s);" % json.dumps(options)
The problem is that json.dumps puts quotes around "handlerFunction", which is not what I want. I want handlerFunction to be unquoted, so that it is evaluated as a function in JavaScript, and not as a string.
How can I tell json.dumps not to quote some of the strings?
I was hoping a custom JsonEncoder would do the trick, but no - objects returned from encoders pass through the normal encoding sequence, so strings are quoted.
So I had to do something else:
First I defined the following function:
def function(name):
return '##' + name + '##'
Then I created a JSON encoding function, instead of json.dumps:
def my_dumps(obj, *args, **kwargs):
s = json.dumps(obj, *args, **kwargs)
s = s.replace('"##', '')
s = s.replace('##"', '')
return s
Now I can create my to-be-jsoned dictionary like this: {'event': function(handler)} and it will be encoded properly.
Not pretty, but it works.
That won't work. The solution is not to mix logic in Python and JavaScript. Here is one way: move all your JavaScript to template and pass only data to it like this:
def some_view(...):
grid_options = {
"option1": 12,
"option2": "option",
}
return render(request, {'grid_options': json.dumps(grid_options)})
In view:
var gridOptions = {{ grid_options }};
$('#my-grid').jqGrid($.extend(gridOptions, {
"eventHandler": handlerFunction
});
json.dumps can not provide such function, neither does Python, because it's not a valid json string. You should try to unquote it in JS.
In addition to #zmbq's answer: my_dumps wraps the keys for you.
key_wrap_seq = '##'
def wrap_key(name):
return key_wrap_seq + name + key_wrap_seq
def wrap_obj_keys(obj):
if type(obj) is dict:
return {wrap_key(k): wrap_obj_keys(obj[k]) for k in obj}
elif type(obj) is list:
return [wrap_obj_keys(x) for x in obj]
else:
return obj
def my_dumps(obj, *args, **kwargs):
obj_wrapped = wrap_obj_keys(obj)
s = json.dumps(obj_wrapped, *args, **kwargs)
s = s.replace('"'+key_wrap_seq, '')
s = s.replace(key_wrap_seq+'"', '')
return s
Result:
>>> obj = {"name": "john", "age": 22}
>>> my_dumps(obj)
... '{name: "john", age: 22}'

Categories