Named Parameters in Method Calls: Python Si, Ruby No

"Hello My Name Is" sticker In an earlier article, Default and Named Parameters in C# 4.0 / Sith Lord in Training, I wrote about how C# 4.0 – that’s the version coming out with the next release of Visual Studio, known as Visual Studio 2010 – is going to provide support for named parameters.

In that article, I also incorrectly stated that Ruby supported named parameters. Luckily, Jörg W Mittag spotted my mistake an corrected me in a comment. I’ve since corrected the article and thought I’d show you how I got it wrong in the first place.

Ruby and My Named Parameter Goof

I had a vague recollection of Ruby accepting named parameters. I figured I’d be empirical and fired up irb – the Ruby REPL shell – and put together a quick little method to see if the recollection was correct:

# Ruby 1.8.6
def test_named_params(first, second)
    puts "#{first}\n#{second}"

Once put together, I made some test calls to the method:

# irb session (Ruby 1.8.6) irb(main):> test_named_params("alpha", "beta") alpha beta

=> nil irb(main):> test_named_params(first = "alpha", second = "beta") alpha beta

=> nil

Seeing that the interpreter didn’t choke on that named parameter call, I thought to myself “Vague recollection confirmed, Ruby supports named parameters!” and wrote the blog article.

Had my brain actually been firing on all cylinders, I would’ve given the method a proper test by providing the named parameters out of the order in which they appear in the method signature. Here’s what I would’ve seen:

# irb session (Ruby 1.8.6)
irb(main):> test_named_params(second = "alpha", first = "beta")

=> nil

Uh-oh. If named parameters worked, the first output line would be “beta” and the second would be “alpha”. Clearly something’s wrong with my recollection.

Let’s try some non-existent named parameters – say, ones involving current entertainemtn news headlines — just to see what happens:

# irb session (Ruby 1.8.6)
irb(main):> test_named_params(lindsay_lohan_dui = "alpha",
jim_cramer_smackdown = "beta")



=> nil

Even with nonsensical named parameters, the method is still accepting the values in order. Why is that?

Just about everything in Ruby has a return value (which can be anything, including nil). You can see for yourself in irb – here’s a quick do-nothing method definition:

irb(main)> def doNothing
irb(main)> end
=> nil

As you can see. defining a method returns a value of nil.

As Jorg pointed out, Ruby assignment statements return a value: the value used in the assigment. Once again, for proof, I’ll use an example from an irb session. In the example below, assigning the string "alpha" to the variable first also returns the string "alpha":

# irb session (Ruby 1.8.6)
irb(main):> first = "alpha"
=> "alpha"

In the call to test_named_params, the Ruby interpreter was interpreting my “named parameters” as assignment statements. first = "alpha" evaluates to plain old "alpha", but so does second = "alpha" (and for that matter, so does lindsay_lohan_dui = "alpha"). Each assignment statement in my parameter list was evaluated, and then those values were passed to method in positional order.

Python Supports Named Parameters

After getting the comment from Jorg and correcting my article, I wondered why I thought Ruby supported named parameters. Then it hit me – it’s Python.

So I fired up the Python REPL and put together this quick little method:

# Python 3.0
def test_named_params(first, second):
    print("%s\n%s" % (first, second))

And this time, I decided to be a little more thorough in my testing:

# Python 3.0 REPL
>>> test_named_params("alpha", "beta")

>>> test_named_params(first = "alpha", second = "beta")

>>> test_named_params(second = "alpha", first = "beta")

And some additional searching on the web confirmed that yes, Python method calling does in fact support named parameters.

So in conclusion, when it comes to named parameters, it’s Python si, Ruby no…and C# pronto.

13 replies on “Named Parameters in Method Calls: Python Si, Ruby No”

Who actually uses named parameters? If you have enough arguments that you can’t remember what goes where then you’re doing it wrong. Named parameters are a mitigation for poor design. I’d be surprised to see any use of named parameters in Django’s source.

I’m a big fan of named parameters, because it simply makes the intention of the calling code clearer – it’s easier for the _maintainer_, because you don’t have to go around looking up function signatures.

You may also want to add in here Objective-C – iPhone programming – and Smalltalk, as if I remember correctly more or less all parameters are named. Javascript by convention uses passing in a dictionary to achieve a similar result. URL parameters to APIs are effectively RESTful named parameters.

You can use a dictionary to pass in named parameters in Python:

d = { “second” : “alpha”, “first” : “beta” }

Also see two blog posts, the anything goes pattern for specialized catchalls, and my bitching at MS for not using named parameters!

How would you make a _practical_ programming API to this without named parameters. Hmmm. How does Ruby do it? (really)

Django does in fact use named parameters – I just happen to have a copy of the source code with me here on vacation however did that happen …

$ find . -name “*.py” | xargs grep Paginator
./contrib/admin/views/ django.core.paginator import Paginator, InvalidPage
./contrib/admin/views/ paginator = Paginator(self.query_set, self.list_per_page)
./contrib/comments/views/ django.core.paginator import Paginator, InvalidPage
./contrib/comments/views/ paginator = Paginator(qs, 100)
./contrib/sitemaps/ self._paginator = paginator.Paginator(self.items(), self.limit)
./core/ Paginator(object):
./views/generic/ django.core.paginator import Paginator, InvalidPage
./views/generic/ paginator = Paginator(queryset, paginate_by, allow_empty_first_page=allow_empty)

Some thoughts on named parameters:

  • I suspect that they’re included in Python because one of its core philosophies is “Favour the explicit over the implicit.” Named parameters make the meaning of each parameter very explicit.
  • They’re useful in cases where different cultures use different ordering, such as dates. There’s no wondering if the argument order is month-day-year or day-month-year.
  • With methods that have a large number of optional arguments, they’re pretty handy. I’ve seen method calls that have had to be written like someMethod(5, "fudge", , , , , True, , , "x") when it would’ve been nicer to just have someMethod(iterations = 5, flavor = "fudge", dontWhizOnElectricFence = True, discountCode = "x")
  • The Ruby way (and especially the Rails way) of using named parameters is to accept a hash as an argument. From the caller’s point of view, there isn’t that much difference between myMethod(flavor = "fudge", sprinkles = False) and myMethod :flavor => "fudge", :sprinkles = false.

There is an important difference between multipart message selectors in Smalltalk (and of course also its derivatives Self, Newspeak and Objective-C) and keyword arguments: as Joey demonstrated, order of keyword arguments doesn’t matter – that’s more or less the point of keyword arguments. In Smalltalk, however, there are no keyword arguments. Instead, the method name is broken into pieces, and the arguments are written in between the pieces. So, in Python, both test_named_params(first = "alpha", second = "beta") and test_named_params(second = "beta", first = "alpha") send the same message (test_named_params) and thus execute the same method. In Smalltalk, self testNamedParamsFirst: "alpha" second: "beta". and self testNamedParamsSecond: "beta" first: "alpha". send two different messages: the first one sends testNamedParamsFirst:second: and the second one sends testNamedParamsSecond:first:, thus invoking two different methods!

So, to regurgitate the infamous geometric shapes example: in Python, you would have a class Rectangle, with a constructor that takes two keyword parameters, width and height and it wouldn’t matter in which order you pass the arguments. In Smalltalk, instead of the constructor, you would have a factory method called width:height: and trying to pass arguments the other way round would result in a No Method Exception, unless you also define a height:width: method.

By no means am I a Ruby guy, but in the non-sensical named parameters example there were some extra line breaks. I suspect it’s just formatting throw up. Or is that the actual output? Looks hinky if it’s the actual output.

Named Parameters are used Heavily in Smalltalk, Sometimes you have a very complicated interface that is best dealt with in one shot instead of dragging it out.

In OCaml if you have named parameters you can keep currying the function til you’re done filling out the parameters.

In perl named parameters are just
sub { my %params = @_; … } and everything is in the params hash (really common place in perl)

Surely you could just pass a hash in ruby

Another great use for named arguments is that you can use boolean parameters that are otherwise opaque and without creating ad hoc two-member enumerations—doSomething(ansychronous: false) vs. doSomething(false) vs. doSomething(DoSomething.Synchronous).

Ruby doesn’t do named parameters in this way, but it does support the idiom:

# Ruby 1.8.6
def test_named_params(params)
puts "#{params[:first]}\n#{params[:second]}"

# irb session (Ruby 1.8.6)
irb(main):004:0> test_named_params(:first => 'alpha', :second => 'beta')
=> nil
irb(main):005:0> test_named_params(:second => 'alpha', :first => 'beta')
=> nil
irb(main):006:0> test_named_params(:first => 'beta', :second => 'alpha')
=> nil

Note that Ruby converts the method parameters into a single hash. This approach is used a lot in Rails, for example to simplify the process of associating form parameters with model attributes.

Alas Blake, I was on an airplane. I’m thinking of going to that Thursday night Python think that that Leah person was talking about at DemoCamp.

Leave a Reply

Your email address will not be published. Required fields are marked *