Module functions with default arguments and namespaces? - python

I want to collect a few generally useful function into a module my_module. These functions must have default arguments that are variables in the workspace. When I move these functions out from the main code to the module, and then import them into the main code, then I get an error since these default arguments cannot be found.
y = 1;
def f(x, y=y):
sum = x+y
return sum
f(1)
And when f instead is imported from my_module we have
y = 1
from my_module import f
f(1)
How can I adjust this later code to work as the first one?

my_module.py:
y = 1
def f(x, y = None):
if y is None:
y = globals()['y']
sum = x+y
return sum
test.py
import my_module
my_module.y = 2
f = my_module.f
print(f(1))

Related

How do I figure out what the named arguments to a function are in python

I am writing a function that is going to take, as one of it's arguments, a dictionary of functions to apply. All of these functions will have at least one argument in common, but some will have others. Without losing the flexibility of letting the user specify arbitrary functions (conditional on having their arguments in scope), how can I introspect a function to see what its arguments are?
Here's a dummy example:
def f1(x, y):
return x+y
def f2(x):
return x**2
fundict = dict(f1 = f1,
f2 = f2)
y = 3 # this will always be defined in the same scope as fundict
for i in list(fundict.keys()):
if <the args to the function have y in them>:
fundict[i](x, y)
else:
fundict[i](x)
Even better would be something that programmatically looks at the definition of the function and feeds the array of arguments to it, conditional on them being in-scope.
I'd also appreciate good general suggestions for different ways to go about solving this problem, that might not involve introspecting a function.
You can use inspect.getfullargspec
import inspect
def f1(x, y):
return x+y
def f2(x):
return x**2
fundict = dict(f1 = f1,
f2 = f2)
y = 3 # this will always be defined in the same scope as fundict
x = 1
for i in list(fundict.keys()):
if 'y' in inspect.getfullargspec(fundict[i]).args:
print(fundict[i](x, y))
else:
print(fundict[i](x))
This gives:
4
1
You can use inspect.getfullargspec.
Example:
>>> for k, fun in fundict.items():
... print(fun.__name__, inspect.getfullargspec(fun)[0])
...
f1 ['x', 'y']
f2 ['x']

Return Function description as string?

Is it possible to output the content of a user-defined function as a string (not the enumeration, but just the function call):
Function:
def sum(x,y):
return x+y
Function content as a string:
"sum(), return x+y"
The inspect function might have worked but it seems to be for just python 2.5 and below?
The inspect module works just fine for retrieving source code, this is not limited to older Python versions.
Provided the source is available (e.g. the function is not defined in C code or the interactive interpreter, or was imported from a module for which only the .pyc bytecode cache is available), then you can use:
import inspect
import re
import textwrap
def function_description(f):
# remove the `def` statement.
source = inspect.getsource(f).partition(':')[-1]
first, _, rest = source.partition('\n')
if not first.strip(): # only whitespace left, so not a one-liner
source = rest
return "{}(), {}".format(
f.__name__,
textwrap.dedent(source))
Demo:
>>> print open('demo.py').read() # show source code
def sum(x, y):
return x + y
def mean(x, y): return sum(x, y) / 2
def factorial(x):
product = 1
for i in xrange(1, x + 1):
product *= i
return product
>>> from demo import sum, mean, factorial
>>> print function_description(sum)
sum(), return x + y
>>> print function_description(mean)
mean(), return sum(x, y) / 2
>>> print function_description(factorial)
factorial(), product = 1
for i in xrange(1, x + 1):
product *= i
return product

How to define a mathematical function in SymPy?

I've been trying this now for hours. I think I don't understand a basic concept, that's why I couldn't answer this question to myself so far.
What I'm trying is to implement a simple mathematical function, like this:
f(x) = x**2 + 1
After that I want to derive that function.
I've defined the symbol and function with:
x = sympy.Symbol('x')
f = sympy.Function('f')(x)
Now I'm struggling with defining the equation to this function f(x). Something like f.exp("x**2 + 1") is not working.
I also wonder how I could get a print out to the console of this function after it's finally defined.
sympy.Function is for undefined functions. Like if f = Function('f') then f(x) remains unevaluated in expressions.
If you want an actual function (like if you do f(1) it evaluates x**2 + 1 at x=1, you can use a Python function
def f(x):
return x**2 + 1
Then f(Symbol('x')) will give a symbolic x**2 + 1 and f(1) will give 2.
Or you can assign the expression to a variable
f = x**2 + 1
and use that. If you want to substitute x for a value, use subs, like
f.subs(x, 1)
Here's your solution:
>>> import sympy
>>> x = sympy.symbols('x')
>>> f = x**2 + 1
>>> sympy.diff(f, x)
2*x
Another possibility (isympy command prompt):
>>> type(x)
<class 'sympy.core.symbol.Symbol'>
>>> f = Lambda(x, x**2)
>>> f
2
x ↦ x
>>> f(3)
9
Calculating the derivative works like that:
>>> g = Lambda(x, diff(f(x), x))
>>> g
x ↦ 2x
>>> g(3)
6
Have a look to:
Sympy how to define variables for functions, integrals and polynomials
You can define it according to ways:
a python function with def as describe above
a python expression g=x**2 + 1
I recommended :
first, define a symbolic variable
x = sympy.symbols('x')
second, define a symbolic function
f = sympy.Function('f')(x)
define a formula
f = x**x+1
if you have so many variable can use this function
def symbols_builder(arg):
globals()[arg]=sp.symbols(str(arg))
if you have so many functions can use this function
def func_build(name, *args):
globals()[name]=sp.Function(str(name))(args)

Using math in loaded module in Python 3

in my main python file I just load the math module and an own module (called funcs.py). Finally, I run a function from the just loaded module.
import math
from funcs import *
RetentionTime(1,2,3,4)
The funcs.py file looks like this:
def RetentionTime(a, b, c, d):
"calculation of retention time"
RT = (11.2 * a) / (b * c * math.degrees( math.atan( d / 100 ) ) )
return RT
This leads to the following Nameerror:
NameError: name 'math' is not defined
In the python shell I can use commands like math.atan(...) without a problem. What am I doing wrong?
Thanks.
# test.py
y = 5
def f(x):
print(x+y)
Here f will bind the object named y from the innermost scope, which in this case is the module scope of test.py. Had it been as follows,
y = 5
def g():
y = 10
def f(x):
print(x+y)
return f
Here the y bound in f will be object 10. In your case RetentionTime is compiled in its own module scope and has no access to the scope of the caller. Hence add import math to the same module as RetentionTime.

How to deepcopy a function object

I create a function which returns an value specified by an variable. Like
y = 1.
def f(x):
return y
I need this function as an function object to create another object
dist = dist.distribution(f, other_variables)
this works fine. But if i want to create several different distribution objects (with different functions f in the sense that y changes) Like
dist = dist.distribution(f, other_variables)
y = 2.
dist2 = dist.distribution(f, other_variables)
Then all distribution objects only return the last specified value y. I.e.
dist.f()(1.)
>>>> 2.
dist2.f()(1.)
>>>> 2.
Instead of the expected
dist.f()(1.)
>>>> 12.
dist2.f()(1.)
>>>> 2.
The problem clearly is, that the function f accesses the variable only when it is called and not one time initially.
Is there a way around it?
What I want at the end is:
A function with one variable only (x, although this doesnt do anything in this case, it is needed in others), which returns the value of y of the moment, when the distribution is created. So in principle I want that at the initialisation of the distribution, the given function is deepcopyed, in the sense, that it is no longer influenced by any change of variables.
Is this possible?
Don't use globals for this. There is no need to 'deepcopy' the function either; the y global is not part of the function state at all.
Use a function factory that provides a scoped value instead, or use a functools.partial() to provide a default argument to your function.
Function factory:
def produce_f(y):
def f(x):
return y
return f
dist = dist.distribution(produce_f(1.), other_variables)
Now y is a scoped value for f, produce_f() returns a new f every time it is called, and y is stored as a cell variable for f.
Demo:
>>> f1 = produce_f(12.)
>>> f2 = produce_f(42.)
>>> f1('foo')
12.0
>>> f2('foo')
42.0
Using functools.partial():
from functools import partial
def f(y, x):
return y
dist = dist.distribution(partial(f, 1.), other_variables)
Here partial(f, 1.) produces a new callable that will call f(1., ...) whenever called, appending any extra arguments passed in.
Demo:
>>> f1 = partial(f, 12.)
>>> f2 = partial(f, 42.)
>>> f1('foo')
12.0
>>> f2('foo')
42.0

Categories