Querying Python Functions by Signature
What's the best way to document/decorate your Python functions/methods so that you're able to easily query them by signature (e.g. argument count, argument type, and return type)?
I'm playing around with a genetic-programming package, and I'd like to extend it to be able to dynamically replace a function in an expression by randomly selecting a replacement from all functions in my module with an equivalent signature.
开发者_Go百科I was thinking about rolling my own decorator, so I could do something like:
@validate(args=((int, range(1,10), (float, range(1,10)), (bool,)), returns=(int,range(-10,10))
def myfunc(a, b, c):
result = do_stuff
return result
and then be able to query similar functions by doing something like:
similar_functions = find_functions(like=myfunc)
Are there any standard tools or libraries along this line?
You can get function's argument list using inspect
. This includes default values, but of course cannot provide types, because the very definitions lack this info.
Decorators like the one you shown seem the best solution for 2.x if you need type info. But I'd probably make their syntax a bit less parenthesized:
@validate(a=(int, 1, 10), b=(float, 1, 10), c=bool, returns=(int, -10, 10))
def foo(a, b, c): ...
You may detect obvious errors if your decorator will actually inspect function's arglist and make sure that it matches decorator's arguments. It need not modify the actual function if you don't aim to add validation code.
Note that you'll need special handling for *arg
and **kwarg
: those are far harder to provide type info for, and I don't know how much f1(int, int, int)
is similar to f2(int, *ints)
, let alone f3(int, **kwargs)
.
In keeping with the "we're all adults here" mentality, why need you ensure that the specified types are actually adhered to? You know what the signature of the function is meant to be, so just tag each function with a decorator and store the tags in a dictionary.
>>> import collections
>>> tags = collections.defaultdict(set)
>>>
>>> def tag(tag):
... def decorator(func):
... tags[tag].add(func)
... func.tag = tag
... return func
... return decorator
...
>>> @tag("foo")
... def a(): pass
...
>>> @tag("foo")
... def b(): pass
...
>>> tags['foo']
{<function b at 0x0000000002BF2448>, <function a at 0x0000000002BD5A48>}
similar
is trivial:
def similar(func):
return signatures[func.sig]
dynamically replace a function in an expression by randomly selecting a replacement from all functions in my module with an equivalent signature.
Just do this.
all_functions_of_some_sig = [
myfunc, myfunc2, myotherfunc, ...
]
It's not that hard. And it's more likely to work because any filtering or matching will be done by the designer of the function.
This is probably simpler
def myfunc( a, b, c ):
return something
myfunc.args= ((int, range(1,10), (float, range(1,10)), (bool,))
myfunc.returns= (int,range(-10,10))
patterns= collections.defaultdict( list )
for f in __all__:
patterns[f.args,f.returns]= f
Is that what you want?
All you have to do then is make sure that __all__
lists all the relevant functions. Not hard to do, and often expected of a module which includes thousands of functions.
精彩评论