Built-in functions and keyword arguments

  • Thread starter Armando Serrano Lombillo
  • Start date
D

Duncan Booth

Armando Serrano Lombillo said:
Why does Python give an error when I try to do this:
Traceback (most recent call last):
File "<pyshell#40>", line 1, in <module>
len(object=[1,2])
TypeError: len() takes no keyword arguments

but not when I use a "normal" function:
return len(object)
my_len(object=[1,2])
2
At the C level there are several options for how you define a function
callable from Python. The most general form is
METH_VARARGS|METH_KEYWORDS which accepts both a tuple of arguments and a
dictionary of keyword arguments. These then have to be parsed to find
the actual arguments.

Many of the builtin functions use only METH_VARARGS which means they
don't support keyword arguments. "len" is even simpler and uses an
option METH_O which means it gets a single object as an argument. This
keeps the code very simple and may also make a slight difference to
performance.

I don't know if the reason that most builtin functions don't accept
keywords is just historical (someone would have to go through a lot of
code and add keyword argument names) or if there really is any
measurable performance difference to not using the METH_KEYWORDS option.
Being able to write less C code may be the main factor.
 
B

Bruno Desthuilliers

Armando Serrano Lombillo a écrit :
Why does Python give an error when I try to do this:
Traceback (most recent call last):
File "<pyshell#40>", line 1, in <module>
len(object=[1,2])
TypeError: len() takes no keyword arguments

but not when I use a "normal" function:
return len(object)
my_len(object=[1,2])
2

In the second case, the name of the argument *is* 'object'. Which is not
the case for the builtin len (which, fwiw, has type
'builtin_function_or_method', not 'function', so inspect.getargspec
couldn't tell me more).

<ot>
While we're at it, you should avoid using builtin's names for
identifiers - here, using 'object' as the arg name shadows the builtin
'object' class).
</ot>
 
S

Steven D'Aprano

Why does Python give an error when I try to do this:
Traceback (most recent call last):
File "<pyshell#40>", line 1, in <module>
len(object=[1,2])
TypeError: len() takes no keyword arguments

but not when I use a "normal" function:
return len(object)
my_len(object=[1,2])
2

Because len() takes no keyword arguments, just like it says, but my_len()
is written so it DOES take a keyword argument.

When you call a function foo(object=[1,2]) you are instructing Python to
bind the value [1,2] to the keyword argument "object". But if the
function doesn't have a keyword argument named "object" then it will fail
immediately. Since len() doesn't have ANY keyword arguments, naturally it
doesn't have one called "object".

You can see the same effect here:
.... return len(arg[0])
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: my_len2() got an unexpected keyword argument 'object'


Apart from the error message being slightly different, many (most? all?)
of the built in functions are like my_len2().

You may be thinking that keyword arguments are the equivalent of this:

object=[1,2]
len(object)

That is not the case. They are not at all equivalent.
 
D

Duncan Booth

In the second case, the name of the argument *is* 'object'. Which is not
the case for the builtin len (which, fwiw, has type
'builtin_function_or_method', not 'function', so inspect.getargspec
couldn't tell me more).

<ot>
While we're at it, you should avoid using builtin's names for
identifiers - here, using 'object' as the arg name shadows the builtin
'object' class).
</ot>

I think you are being a little bit unfair here: help(len) says:

len(...)
len(object) -> integer

Return the number of items of a sequence or mapping.

which implies that the argument to len has the name 'object' (although in
fact it doesn't have a name). The OP was simply asking about the difference
in calling conventions, not proposing to write code using 'object' as the
argument name.
 
T

Tim Chase

I think you are being a little bit unfair here: help(len) says:

len(...)
len(object) -> integer

Return the number of items of a sequence or mapping.

which implies that the argument to len has the name 'object' (although in
fact it doesn't have a name).

And to confound matters even further for the uninitiated,

http://docs.python.org/lib/built-in-funcs.html#l2h-45

says that it's "len(s)" instead (but "len(s=[])" doesn't work either)

-tkc
 
A

Armando Serrano Lombillo

Why does Python give an error when I try to do this:
len(object=[1,2])
Traceback (most recent call last):
File "<pyshell#40>", line 1, in <module>
len(object=[1,2])
TypeError: len() takes no keyword arguments
but not when I use a "normal" function:
def my_len(object): return len(object)
my_len(object=[1,2])
2

At the C level there are several options for how you define a function
callable from Python. The most general form is
METH_VARARGS|METH_KEYWORDS which accepts both a tuple of arguments and a
dictionary of keyword arguments. These then have to be parsed to find
the actual arguments.

Many of the builtin functions use only METH_VARARGS which means they
don't support keyword arguments. "len" is even simpler and uses an
option METH_O which means it gets a single object as an argument. This
keeps the code very simple and may also make a slight difference to
performance.

I don't know if the reason that most builtin functions don't accept
keywords is just historical (someone would have to go through a lot of
code and add keyword argument names) or if there really is any
measurable performance difference to not using the METH_KEYWORDS option.
Being able to write less C code may be the main factor.

Ok. I was suspecting something like this. Performance issues aside, I
think it would be a good idea if built-in functions behaved exactly
the same as normal functions.

BTW, I came into this problem when trying to use functools.partial:

import functools
getattrF = functools.partial(getattr, default=False)

which I think should have worked if getattr behaved as normal
functions do.

In the end I did:

def getattrF(object, name):
return getattr(object, name, False)

Any better idea?
 
A

Armando Serrano Lombillo

Armando Serrano Lombillo a écrit :
Why does Python give an error when I try to do this:
len(object=[1,2])
Traceback (most recent call last):
File "<pyshell#40>", line 1, in <module>
len(object=[1,2])
TypeError: len() takes no keyword arguments
but not when I use a "normal" function:
def my_len(object): return len(object)
my_len(object=[1,2])
2

In the second case, the name of the argument *is* 'object'. Which is not
the case for the builtin len (which, fwiw, has type
'builtin_function_or_method', not 'function', so inspect.getargspec
couldn't tell me more).

so that's the point, built-in functions don't behave as normal
functions.
<ot>
While we're at it, you should avoid using builtin's names for
identifiers - here, using 'object' as the arg name shadows the builtin
'object' class).
</ot>

As Duncan points out, I was using the name object because it's what
you get when you type help(len) (or in the calltips, or in). Anyway, I
don't think there's any harm in overriding a builtin name in such a
small scope (the object=[1,2] won't shadow the built-in object outside
of the function).
 
M

Marc 'BlackJack' Rintsch

Ok. I was suspecting something like this. Performance issues aside, I
think it would be a good idea if built-in functions behaved exactly
the same as normal functions.

As Steven D'Aprano showed they behave like normal functions. Even pure
Python functions can have arguments without names:

def spam(*args):
pass

Ciao,
Marc 'BlackJack' Rintsch
 
H

Hendrik van Rooyen

Tim Chase said:
I think you are being a little bit unfair here: help(len) says:

len(...)
len(object) -> integer

Return the number of items of a sequence or mapping.

which implies that the argument to len has the name 'object' (although in
fact it doesn't have a name).

And to confound matters even further for the uninitiated,

http://docs.python.org/lib/built-in-funcs.html#l2h-45

says that it's "len(s)" instead (but "len(s=[])" doesn't work either)

Looks like a gotcha to me - its the difference between a keyword
(master = 42) and an assignment (s='I am a string')

You just can't do that - how is the parser supposed to know that
the second one is an assignment and not a keyword?

len([]) should work, though.

- Hendrik
 
B

Bruno Desthuilliers

Armando Serrano Lombillo a écrit :
Armando Serrano Lombillo a écrit :
Why does Python give an error when I try to do this:
len(object=[1,2])
Traceback (most recent call last):
File "<pyshell#40>", line 1, in <module>
len(object=[1,2])
TypeError: len() takes no keyword arguments
but not when I use a "normal" function:
def my_len(object):
return len(object)
my_len(object=[1,2])
2
In the second case, the name of the argument *is* 'object'. Which is not
the case for the builtin len (which, fwiw, has type
'builtin_function_or_method', not 'function', so inspect.getargspec
couldn't tell me more).

so that's the point, built-in functions don't behave as normal
functions.
<ot>
While we're at it, you should avoid using builtin's names for
identifiers - here, using 'object' as the arg name shadows the builtin
'object' class).
</ot>

As Duncan points out, I was using the name object because it's what
you get when you type help(len)

That what I thought. But anyway, you're not the only person reading this
newsgroup, and there are a couple gotchas that are worth pointing out
for those who don't know yet.
(or in the calltips, or in). Anyway, I
don't think there's any harm in overriding a builtin name in such a
small scope

In this specific case, possibly - because there are few chances you need
to get at the builtin object type here. Still, it's better to avoid
shadowing builtin names IMHO - if only for readability.
 
J

J. Clifford Dyer

I think you are being a little bit unfair here: help(len) says:

len(...)
len(object) -> integer

Return the number of items of a sequence or mapping.

which implies that the argument to len has the name 'object' (although in
fact it doesn't have a name). The OP was simply asking about the difference
in calling conventions, not proposing to write code using 'object' as the
argument name.

Hmm.... To my mind, that just implies that the *type* of the expected input is an object. Just like the "-> integer" tells you that the type of the output is an integer. If the documentation read "len(s=object) -> integer", then I would expect a keyword argument s typed as an object.

Now that might also be misleading, as not all objects have a length (integers, for example raise a TypeError). But the documentation has to say something, which doesn't imply that every argument has to be a keyword.

Cheers,
Cliff
 
D

Duncan Booth

J. Clifford Dyer said:
Hmm.... To my mind, that just implies that the *type* of the expected
input is an object. Just like the "-> integer" tells you that the
type of the output is an integer. If the documentation read
"len(s=object) -> integer", then I would expect a keyword argument s
typed as an object.

How do you interpret:
Help on built-in function __import__ in module __builtin__:

__import__(...)
__import__(name, globals={}, locals={}, fromlist=[], level=-1) ->
module
....Help on class int in module __builtin__:

class int(object)
| int(x[, base]) -> integer
....

Can you find any case (other than a single parameter identified as
'object') where you can interpret the help string as telling you the types
of the parameters?
 
J

J. Clifford Dyer

J. Clifford Dyer said:
Hmm.... To my mind, that just implies that the *type* of the expected
input is an object. Just like the "-> integer" tells you that the
type of the output is an integer. If the documentation read
"len(s=object) -> integer", then I would expect a keyword argument s
typed as an object.

How do you interpret:
Help on built-in function __import__ in module __builtin__:

__import__(...)
__import__(name, globals={}, locals={}, fromlist=[], level=-1) ->
module
...Help on class int in module __builtin__:

class int(object)
| int(x[, base]) -> integer
...

Can you find any case (other than a single parameter identified as
'object') where you can interpret the help string as telling you the types
of the parameters?

OK, good point. Perhaps it's not so specific as the type, but certainly the use of name and x in the docstrings listed above only imply something about the character of the argument, not the name of the argument itself, which is what I was trying to get at. Help documentation for keyword arguments usually shows the argument being used as a keyword, like the example from __import__ above.

Thanks for the correction.

Cheers,
Cliff
 
D

Duncan Booth

J. Clifford Dyer said:
How do you interpret:
help(__import__)
Help on built-in function __import__ in module __builtin__:

__import__(...)
__import__(name, globals={}, locals={}, fromlist=[], level=-1) ->
module
...
help(int)
Help on class int in module __builtin__:

class int(object)
| int(x[, base]) -> integer
...

Can you find any case (other than a single parameter identified as
'object') where you can interpret the help string as telling you the
types of the parameters?

OK, good point. Perhaps it's not so specific as the type, but
certainly the use of name and x in the docstrings listed above only
imply something about the character of the argument, not the name of
the argument itself, which is what I was trying to get at. Help
documentation for keyword arguments usually shows the argument being
used as a keyword, like the example from __import__ above.

Usually, but not in the second example I gave:
50
 
G

Gabriel Genellina

En Tue, 30 Oct 2007 14:43:48 -0300, Duncan Booth
J. Clifford Dyer said:
help(int)
Help on class int in module __builtin__:

class int(object)
| int(x[, base]) -> integer
...
OK, good point. Perhaps it's not so specific as the type, but
certainly the use of name and x in the docstrings listed above only
imply something about the character of the argument, not the name of
the argument itself, which is what I was trying to get at. Help
documentation for keyword arguments usually shows the argument being
used as a keyword, like the example from __import__ above.

Usually, but not in the second example I gave:
50

So one can conclude that, unfortunately, there is no way to tell from the
docs alone whether a certain function will accept keyword arguments or
not. Too bad :(
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top