generic function in python - calling a method with

2020-07-10 05:04发布

问题:

i'm new to python and need some help...

I'm implementing a generic search function that accepts an argument "fringe", which can be a data structure of many types.

in the search method I have the line:

 fringe.push(item, priority)

the problem is that the push method in different data structures takes different number of arguments(some require priority and some dont). is there an ellegant way to get pass that and make the "push" method take only the number of args it requires out of the argument list sent?

Thanks!

回答1:

The method to get different number of arguments and still being able of selecting the right one is the use of *args and **keyword_args parameters. From Mark Lutz's Learning Python book:

* and **, are designed to support functions that take any number of arguments. Both can appear in either the function definition or a function call, and they have related purposes in the two locations.

* and ** in function definition

If you define a function:

def f1(param1, *argparams, **kwparams): 
    print 'fixed_params -> ', param1
    print 'argparams  --> ', argparams
    print 'kwparams   ---->,', kwparams

you can call it this way:

f1('a', 'b', 'c', 'd', kw1='keyw1', kw2='keyw2')

Then you get:

fixed_params ->  a
argparams  -->  ('b', 'c', 'd')
kwparams   ---->, {'kw1': 'keyw1', 'kw2': 'keyw2'}

So that you can send/receive any number of parameters and keywords. One typical idiom to recover keyword args is as follows:

def f1(param1, **kwparams):
    my_kw1 = kwparams['kw1']
    ---- operate  with my_kw1 ------

In this way your function can be called with any number of params and it uses those it needs.
This type or arguments are frecuently used in some GUI code like wxPython class definition and subclassing as well as for function currying, decorators, etc

* and ** in function call

* and ** params in a function call are unpacked when taken by the function:

def func(a, b, c, d):
    print(a, b, c, d)

args = (2, 3)
kwargs = {'d': 4}
func(1, *args, **kwargs)

### returns ---> 1 2 3 4 

Great!



回答2:

In theory you could use inspect.getargspec(fringe) to find out what arguments the method takes. That will tell you the number of arguments you could pass, but it's very messy:

argspec = inspect.getargspec(fringe.push)
if len(argspec.args) >= 3 or argspec.varargs or argspec.keywords:
    fringe.push(item, priority)
else:
    fringe.push(item)

Much simpler just to go for it and ask forgiveness if necessary:

try:
    fringe.push(item, priority)
except TypeError:
    fringe.push(item)

Better still of course to make sure that the push() methods all have a consistent argument spec, but if you can't do that then use the try...except.



回答3:

try below code snippets,

def push(item, priority=None):
    print item,priority


args = (1,)
push(*args)

args = (1,2)
push(*args)


回答4:

Can't you just use a default argument value?

>>> def foo(a, b = 10):
...   print a, b
... 
>>> foo(1000)
1000 10
>>> foo(1000, 1000)
1000 1000
>>>

If the argument b is not provided, it defaults to 10.