I have a GUI which allows the user to run any function from a specific *.py file. I'd like some of the functions to run in a different manner than each other. In order to do that, I was trying to attach attributes to the function (Simple things like which inputs it needs). However, I found the only way to get those attributes was to run the code first.
Is there a way to get these attributes without running the code, or perhaps a more pythonic way to approach this task in general?
Very rudimentary example of my code:
FileA.py
def Beta(x):
Beta.input_stype = "Float"
y = x + 0.5
return y
def Gamma(x):
Gamma.input_stype = "String"
y = x + "_blah_blah_blah"
return y
def Delta(x):
Delta.input_stype = "String"
y = x.index('WhereIsIt')
return y
FileB.py
import FileA
import inspect
z = inspect.getmembers(Fiddle2, inspect.isfunction)
#### User selects the code value here ####
x = user_selection
executable = z[x][1] # Pulls the executable code
if executable.input_stype == "Float" :
y = executable(45)
elif executable.input_stype == "String" :
y = executable('Testing_the_WhereIsIt_stuff')
-
Possible duplicate of Type checking of arguments PythonNatecat– Natecat2016年05月16日 20:10:38 +00:00Commented May 16, 2016 at 20:10
4 Answers 4
Don't assign the attributes inside the function body:
def Beta(x):
y = x + 0.5
return y
Beta.input_stype = "Float"
While you're at it, you may want to use the actual float or str types instead of the strings "Float" and "String". If you're on Python 3, you may also want to use function annotations:
def Beta(x: float):
y = x + 0.5
return y
1 Comment
You may also be able to make the code look a little cleaner, and keep the information closer to the function definition where people are more likely to look when reading your code, by using a decorator.
def input_stype(typ):
def deco(f):
f.input_stype = typ
return f
return deco
@input_stype('Float')
def Beta(x):
...
@input_stype('String')
def Gamma(x):
...
2 Comments
You can set the attribute right after the function definition:
def Beta(x):
y = x + 0.5
return y
Beta.input_stype = "Float"
Comments
Another idea I wanted to suggest:
def validate_input_type(typ):
from functools import wraps
def decorator(f):
f.input_type = typ
@wraps(f)
def wrapper(arg):
try:
assert isinstance(arg, typ)
except AssertionError:
raise TypeError('{} is not of type {}'.format(arg, typ))
return f(arg)
return wrapper
return decorator
To be used like this:
@validate_input_type(float)
def foo(x):
pass
@validate_input_type(str)
def bar(x):
pass
This creates validation of the type of the arg at runtime AND sets input_type on the function for introspection.
Tests:
foo(1) -> TypeError
bar(1) -> TypeError
foo(1.0) -> Ok
bar('aaa') -> Ok
foo.input_type -> float
bar.input_type -> str
1 Comment
functools.singledispatch. You would still need to have a wrapper like yours to do everything within a single decorator though. And it comes with the additional limitation that you can only have a single argument, which seems like a weird limitation. Frankly, I'm kind of surprised the feature made it into python at all.