Вопрос

I am trying to make my code NOT to accept keyword arguments just like some bulitins also do not accept keyword arguments, but, I am unable to do so. Here, is my thinking according to my limited understanding:-

def somefunc(a,b):
    print a,b

somefunc(10,20)

Output:

10 20

Now, When I run the following (I know this is not expected to be keyword argument in the function definition, but, looking at the function call, it seems to be the same syntax as that of when calling a function which accepts keyword arguments):

somefunc(b=10,a=20)

Output:

20 10

I have 2 questions:-

  1. Looking at the function call somefunc(b=10,a=20) and not the function definition, this can seem to be either of a call to a function which accepts just normal arguments or a function which accepts keyword arguments. How does the interpreter differentiate between the two?
  2. Is it possible to convert any of our function in a form so that it does not accept keyword arguments just like many of the builtins do?

Why I want to do this at all? I am just checking if I can do this, so that I do not miss anything in understanding python in depth. I do knot know whether python allows that or not.

Это было полезно?

Решение

ad 1) Correct names are verified automatically by Python if they are called like somefunct(name=value, ...). I need not to remember the exact standard order of parameters and to verify it too "neurotic" by looking into the documentation every month at every usage, if I remember a function with nice descriptive names of parameters and it will be tested that they are accepted by Python. On the contrary, the correct order of used parameters can be verified only by documentation. Calling by named parameters is preferred over very long list of positional parameters. Therefore the reported behaviour is well-founded. (Short single letter parameters "a, b" don't help against mistakes of course.)

ad 2) Some well known builtin fast functions written in C with small fixed number of required parameters do not support calling with named parameters. (e.g. hasattr)

This is because they use only simple header ...(... PyObject *args) and therefore all named parameters are rejected automatically. (Python can never introspect into names of arguments in C source. :-)

Many other C functions have a header ...(... PyObject *args, PyObject *kwds) and they support exact list of names explicitely by implementing much more complicated validation PyArg_ParseTupleAndKeywords and by writing the names to docs strings.


Edit: Positional-only parameters are possible in Python 3.8 by a new function parameter syntax / to indicate that some function parameters must be specified positionally and cannot be used as keyword arguments.

def somefunc(a, b, /):
    print(a, b)

Другие советы

You can use arbitrary argument lists to do this. See http://docs.python.org/tutorial/controlflow.html#arbitrary-argument-lists

For example:

def somefunc(*args):
    print args[0], args[1]

Calling without keywords:

somefunc(10,20)

Gives:

10 20

Calling with keywords:

somefunc(a=10,b=20)

Gives an error:

TypeError: someFunc() got an unexpected keyword argument 'a'

It's unclear why you would want to do this though.

Looking at the function call somefunc(b=10,a=20) and not the function definition, this can seem to be either of a call to a function which accepts just normal arguments or a function which accepts keyword arguments. How does the interpreter differentiate between the two?

Not sure if this is quite what you're asking, but if I understand you right, the answer is "it doesn't". You can call the function with keywords no matter how the arguments are defined in the function. If you pass any arguments using keyword syntax (e.g., f(a=...)), Python binds the corresponding values to the arguments with the same names in the function definition. It doesn't matter whether a default value was defined. The two kinds of function definition don't create different "kinds" of arguments, it's just that some of the arguments might have default values and others might not. So whether you do def f(a) or def f(a=2), you can still do f(a=...). The only restriction is that you have to pass/define positional arguments first (e.g., you can't do def f(a=2, b) or f(a=2, 3)). See also this answer.

As for the second part of your question, I'm not aware of any way to stop a Python function from accepting keyword-passed values for named arguments. If you define only *args then it won't accept keyword arguments, but the positional arguments can't have separate names.

There isn't such a strong distinction in Python between keyword and positional arguments. In fact, it is as simple as:

Positional arguments are parsed in the order they are listed in the function call Keyword arguments can be added in any order, but cannot come before positional arguments.

So, if you have this:

def foo(x):
    pass

You can do:

foo(1) # positional
foo(x=1) # keyword

But you cannot do foo() because you didn't supply a default value.

Similarly, if you have this:

def foo(x,y='hello'):
    pass

foo(1) # this works fine
foo() # this is invalid, as an argument without a default isn't passed
foo(y='hello',1) # error - positional arguments come before keyword arguments

Don't forget to read up on arbitrary argument lists.

I think this is a very bad Python feature, and can lead to very unmaintainable and buggy code. Suppose you had a function:

def myurl(x, y):
    url = "{}:{}".format(x, y)
    return url

Although it might look like a good idea to enable replacing positional with keyword arguments don't ever do that. I'm surprised that there is no Python option that would prevent this function to be called with keyword arguments:

>> print myurl(x="localhost", y=8080)

If someone figures out the library would be more readable if written as:

def myurl(hostname, port):
    url = "{}:{}".format(hostname, port)
    return url

then the rest of the code that used keyword calling as myurl(x="localhost", y=8080) would fail:

TypeError: myurl() got an unexpected keyword argument 'x'

This means that your local function variable all of the sudden became a global variable! I was hoping this issue would be somehow addressed in Python 3.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top