72

Before jumping into python, I had started with some Objective-C / Cocoa books. As I recall, most functions required keyword arguments to be explicitly stated. Until recently I forgot all about this, and just used positional arguments in Python. But lately, I've ran into a few bugs which resulted from improper positions - sneaky little things they were.

Got me thinking - generally speaking, unless there is a circumstance that specifically requires non-keyword arguments - is there any good reason NOT to use keyword arguments? Is it considered bad style to always use them, even for simple functions?

I feel like as most of my 50-line programs have been scaling to 500 or more lines regularly, if I just get accustomed to always using keyword arguments, the code will be more easily readable and maintainable as it grows. Any reason this might not be so?

UPDATE:

The general impression I am getting is that its a style preference, with many good arguments that they should generally not be used for very simple arguments, but are otherwise consistent with good style. Before accepting I just want to clarify though - is there any specific non-style problems that arise from this method - for instance, significant performance hits?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
chris
  • 2,404
  • 3
  • 27
  • 33
  • 3
    One place I've seen advice to always use keyword arguments is with `__init__`. See http://fuhm.net/super-harmful/. That's not as general as you're asking here, but it's an argument in favor of what you're suggesting. – Joe White Aug 12 '11 at 14:45
  • I would say you ask why Python goes this way because he is coming from Objective-C to Python. If you were going the reversed way, you would ask yous why Objective-C require us to use keyword arguments everywhere! (Not that it is an bad or irrational choice, though; it is just a different design. However, the full language is impacted by it and so I would suggest you to do not try to copy such design to a different language) – brandizzi Aug 12 '11 at 15:17

11 Answers11

68

There isn't any reason not to use keyword arguments apart from the clarity and readability of the code. The choice of whether to use keywords should be based on whether the keyword adds additional useful information when reading the code or not.

I follow the following general rule:

  1. If it is hard to infer the function (name) of the argument from the function name – pass it by keyword (e.g. I wouldn't want to have text.splitlines(True) in my code).
  2. If it is hard to infer the order of the arguments, for example if you have too many arguments, or when you have independent optional arguments – pass it by keyword (e.g. funkyplot(x, y, None, None, None, None, None, None, 'red') doesn't look particularly nice).
  3. Never pass the first few arguments by keyword if the purpose of the argument is obvious. You see, sin(2*pi) is better than sin(value=2*pi), the same is true for plot(x, y, z).

In most cases, stable mandatory arguments would be positional, and optional arguments would be keyword.

There's also a possible difference in performance, because in every implementation the keyword arguments would be slightly slower, but considering this would be generally a premature optimisation and the results from it wouldn't be significant, I don't think it's crucial for the decision.

UPDATE: Non-stylistical concerns

Keyword arguments can do everything that positional arguments can, and if you're defining a new API there are no technical disadvantages apart from possible performance issues. However, you might have little issues if you're combining your code with existing elements.

Consider the following:

  • If you make your function take keyword arguments, that becomes part of your interface. You can't replace your function with another that has a similar signature but a different keyword for the same argument.
  • You might want to use a decorator or another utility on your function that assumes that your function takes a positional argument. Unbound methods are an example of such utility because they always pass the first argument as positional after reading it as positional, so cls.method(self=cls_instance) doesn't work even if there is an argument self in the definition.

None of these would be a real issue if you design your API well and document the use of keyword arguments, especially if you're not designing something that should be interchangeable with something that already exists.

Rosh Oxymoron
  • 20,355
  • 6
  • 41
  • 43
  • 12
    Perfect answer. I'd add that the Python way is similar to the way programs command line options are passed: required ones should be a few and positional, optional ones should be passed as an option (which is in some sense a keyword). – brandizzi Aug 12 '11 at 15:13
  • Can keyword arguments really result in any kind of significant performance hit? I suppose it's one or two extra tokens to be parsed, but any deeper reasons than that? – Gravity Aug 25 '11 at 06:44
  • 1
    @Rosh: Stricly speaking "Keyword arguments can do everything that positional arguments can" statement is not true. For example, tuple unpacking doesn't work for keyword arguments, while `def f((x, y)): …` is a valid definition. Sure, it's neither widely used nor recommended style. – Denis Otkidach Dec 01 '11 at 08:59
19

If your consideration is to improve readability of function calls, why not simply declare functions as normal, e.g.

def test(x, y):
    print "x:", x
    print "y:", y

And simply call functions by declaring the names explicitly, like so:

test(y=4, x=1)

Which obviously gives you the output:

x: 1
y: 4

or this exercise would be pointless.

This avoids having arguments be optional and needing default values (unless you want them to be, in which case just go ahead with the keyword arguments! :) and gives you all the versatility and improved readability of named arguments that are not limited by order.

bdeniker
  • 995
  • 1
  • 9
  • 21
  • perfect answer for what george is asking. make args act as kwargs. on [Salty Crane Blog](http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/) he shows how to even mix kwargs and args in function calls – j_syk Aug 12 '11 at 15:44
  • Yes! This gives most of the benefits that the post was asking about, without all the drawbacks that changing the function arguments spec would create. – wim Apr 09 '14 at 17:25
10

Well, there are a few reasons why I would not do that.

If all your arguments are keyword arguments, it increases noise in the code and it might remove clarity about which arguments are required and which ones are optionnal.

Also, if I have to use your code, I might want to kill you !! (Just kidding), but having to type the name of all the parameters everytime... not so fun.

Martin
  • 5,954
  • 5
  • 30
  • 46
  • 3
    I've seen some Smalltalk code (which Obj-C is based on). When it's done well, it reads very naturally. Instead of seeing a list of parameters, you see a sentence. You have to think differently about how you name parameters, but someone from an Objective-C background would probably already have that mindset. – Joe White Aug 12 '11 at 14:53
  • 1
    I can see the Noise argument. For whether they were optional or not - can you give an example? As I'm thinking of it - you'd have to understand how the function works regardless to know if an argument is optional or not - wouldn't having the arguments named (if named well) speed up comprehension in that case? – chris Aug 12 '11 at 14:54
  • @Joe White it is true that this way of writing params feels good in SmallTalk/Objective-C but it is because the languages are designed as a whole to use it and developers of such languages take a lot of care of doing it well in the "right way". Note, for example, that ObjC params are both keyword-based and positional so the sentence appearance is forced. The problem is that the "right way" in Python is different and follow the patterns of Objective-C would result in strange code. – brandizzi Aug 12 '11 at 15:24
  • @brandizzi Fair point. If the app will be written and maintained by people familiar with Objective-C, it could work well, but it wouldn't be Pythonic. – Joe White Aug 12 '11 at 16:01
8

Just to offer a different argument, I think there are some cases in which named parameters might improve readability. For example, imagine a function that creates a user in your system:

create_user("George", "Martin", "g.m@example.com", "payments@example.com", "1", "Radius Circle")

From that definition, it is not at all clear what these values might mean, even though they are all required, however with named parameters it is always obvious:

create_user(
    first_name="George",
    last_name="Martin",
    contact_email="g.m@example.com",
    billing_email="payments@example.com",
    street_number="1",
    street_name="Radius Circle")
Brent Newey
  • 4,479
  • 3
  • 29
  • 33
  • This is what I liked about learning Cocoa/Obc. - seeing a function for the first time wasn't so mystifying - you could usually get a pretty good idea of what it did, which I thought was great. I'm just not experienced enough to know if large project code can be parsimonious enough to not have the constant weigh-down of typed arguments not be a hassle in the long-run. – chris Aug 12 '11 at 15:01
  • 3
    I think it depends on the function. In the case of something like `def validate_email(email)`, it is probably obvious when you look at an instance of the function what it is doing. – Brent Newey Aug 12 '11 at 15:03
  • 1
    Indeed, keyword arguments are really useful when passing literals to a function. However, if the arguments are variables with clear enough names, it becomes very noisy. Consider: `create_user(first_name=first_name, last_name=last_name, contact_email=contact_email, ...)`. – André Caron Aug 20 '11 at 20:47
  • This is a great argument, code becomes hard to read very quickly when the meaning of the passed parameters is unclear. For example, passing boolean values to functions is a bad idea in general, e.g.: `do_some_stuff(True)`. What does `True` really accomplishes here? This hurts the readability of code in the most literal sense, because now you'd have to lookup the definition of said function to find out. Python's named parameters provides a nice way to improve the readability of code, just make sure not to overextends its use, there is a turning point where it achieves the opposite. – 303 Oct 24 '21 at 00:04
5

I remember reading a very good explanation of "options" in UNIX programs: "Options are meant to be optional, a program should be able to run without any options at all".

The same principle could be applied to keyword arguments in Python. These kind of arguments should allow a user to "customize" the function call, but a function should be able to be called without any implicit keyword-value argument pairs at all.

Zaur Nasibov
  • 22,280
  • 12
  • 56
  • 83
  • So... why did Python 3 add a way to have required-keyword arguments? – agf Aug 13 '11 at 12:58
  • 5
    `def some_func(x, y, *, method, error='strict')` -- `method` and `error` can *only* be specified by keyword, and `method` *must* be specified as it has no default value. – Ethan Furman Sep 30 '11 at 23:16
4

When Python's built-in compile() and __import__() functions gain keyword argument support, the same argument was made in favor of clarity. There appears to be no significant performance hit, if any.

Now, if you make your functions only accept keyword arguments (as opposed to passing the positional parameters using keywords when calling them, which is allowed), then yes, it'd be annoying.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
michel-slm
  • 9,438
  • 3
  • 32
  • 31
4

Sometimes, things should be simple because they are simple.

If you always enforce you to use keyword arguments on every function call, soon your code will be unreadable.

Diego Queiroz
  • 3,198
  • 1
  • 24
  • 36
3

I don't see the purpose of using keyword arguments when the meaning of the arguments is obvious

Xavier Combelle
  • 10,968
  • 5
  • 28
  • 52
2

A mistake I often do is that I forget that positional arguments have to be specified before any keyword arguments, when calling a function. If testing is a function, then:

testing(arg = 20, 56)

gives a SyntaxError message; something like:

SyntaxError: non-keyword arg after keyword arg

It is easy to fix of course, it's just annoying. So in the case of few - lines programs as the ones you mention, I would probably just go with positional arguments after giving nice, descriptive names to the parameters of the function. I don't know if what I mention is that big of a problem though.

Dora
  • 374
  • 3
  • 8
2

One downside I could see is that you'd have to think of a sensible default value for everything, and in many cases there might not be any sensible default value (including None). Then you would feel obliged to write a whole lot of error handling code for the cases where a kwarg that logically should be a positional arg was left unspecified.

Imagine writing stuff like this every time..

def logarithm(x=None):
    if x is None:
        raise TypeError("You can't do log(None), sorry!")
wim
  • 338,267
  • 99
  • 616
  • 750
  • why do you need to check for the value of x when an invalid value will raise ValueError automatically? raising "BadArgsException" is superbly redundant in this case. – cowbert May 16 '18 at 22:42
  • @cowbert `ValueError` will not me raised "automatically". Anyway, the point is that there is no sensible default value to use for `x` - sometimes a positional arg is just better. – wim May 16 '18 at 23:35
  • Whether the signature uses a positional or a kwarg in the example you posted makes no difference since using a positional you could easily have assigned x to None earlier and called `logarithm(x)` and you would still have to handle NoneType.I don't understand why one would feel 'obliged to write a whole lot of error handling code', with default values since there are only 2 code paths: either return a real value or raise an exception (or some inner operator/function will do it). By default you'd raise an exception, no extra code needed... – cowbert May 17 '18 at 00:15
  • But using keyword means the argument is *optional*. So, there is a difference! If user has passed None explicitly, that's their own fault for passing a bad argument. – wim May 17 '18 at 00:49
  • ‘But using keyword means the argument is optional.’ [No](https://stackoverflow.com/a/57819001/2326961). – Géry Ogam Oct 05 '22 at 21:54
  • @Maggyero This answer is over 10 years old, I would have been on Python 2.7 at the time :) – wim Oct 06 '22 at 01:14
2

Keyword args are good when you have long parameter lists with no well defined order (that you can't easily come up with a clear scheme to remember); however there are many situations where using them is overkill or makes the program less clear.

First, sometimes is much easier to remember the order of keywords than the names of keyword arguments, and specifying the names of arguments could make it less clear. Take randint from scipy.random with the following docstring:

randint(low, high=None, size=None)    
Return random integers x such that low <= x < high.
If high is None, then 0 <= x < low.

When wanting to generate a random int from [0,10) its clearer to write randint(10) than randint(low=10) in my view. If you need to generate an array with 100 numbers in [0,10) you can probably remember the argument order and write randint(0, 10, 100). However, you may not remember the variable names (e.g., is the first parameter low, lower, start, min, minimum) and once you have to look up the parameter names, you might as well not use them (as you just looked up the proper order).

Also consider variadic functions (ones with variable number of parameters that are anonymous themselves). E.g., you may want to write something like:

def square_sum(*params):
    sq_sum = 0
    for p in params:
        sq_sum += p*p
    return sq_sum

that can be applied a bunch of bare parameters (square_sum(1,2,3,4,5) # gives 55 ). Sure you could have written the function to take an named keyword iterable def square_sum(params): and called it like square_sum([1,2,3,4,5]) but that may be less intuitive, especially when there's no potential confusion about the argument name or its contents.

dr jimbob
  • 17,259
  • 7
  • 59
  • 81
  • Interesting remark about spatial memory (positional parameters) versus linguistic memory (keyword parameters). – Géry Ogam Oct 05 '22 at 21:50