2

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')
Mike Müller
  • 82,630
  • 20
  • 166
  • 161
Helos35
  • 121
  • 1
  • 13
  • Possible duplicate of [Type checking of arguments Python](http://stackoverflow.com/questions/734368/type-checking-of-arguments-python) – Natecat May 16 '16 at 20:10

4 Answers4

5

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
user2357112
  • 260,549
  • 28
  • 431
  • 505
4

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):
    ...
Brendan Abel
  • 35,343
  • 14
  • 88
  • 118
  • oh, I just noticed your answer.... I was working on my code and hadn't refreshed the screen – DevLounge May 16 '16 at 21:02
  • This works well, as long as I move the def input_stype(type) function to another py file. I'm pulling all functions in the given file, and this adds another function to it. Thanks for answer – Helos35 May 17 '16 at 12:51
3

You can set the attribute right after the function definition:

def Beta(x):     
    y = x + 0.5
    return y

Beta.input_stype = "Float"
Mike Müller
  • 82,630
  • 20
  • 166
  • 161
1

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
DevLounge
  • 8,313
  • 3
  • 31
  • 44
  • 1
    Newer versions of python (>=3.4) have a similar feature built-in using [`functools.singledispatch`](https://docs.python.org/3.4/library/functools.html#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. – Brendan Abel May 16 '16 at 21:13