1

Implementing some Neural Network with tensorflow, I've faced a method which parameters have took my attention. I'm talking about tf.nn.sigmoid_cross_entropy_with_logits (Documentation here).

The first parameter it receives as first parameter _sentinel=None which, according to the documentation:

_sentinel: Used to prevent positional parameters. Internal, do not use.

I understand that by having this parameter, next ones have to be named instead of positional is this one don't have to be used, but my question is. In which cases does prevent positional parameters have some benefit? What is their main goal to use this? Because I could also run

tf.nn.sigmoid_cross_entropy_with_logits(None, my_labels, my_logits)

being all arguments positional. Anyway, I want to clarify that my question is not focused in TensorFlow, it's just the example that I have found.

Charles Duffy
  • 257,635
  • 38
  • 339
  • 400
josepdecid
  • 1,638
  • 13
  • 24
  • 7
    Presumably the folks building the API don't want to guarantee that parameters' names/positions will be stable across API versions (if a user *can* pass arguments positionally, then an API's authors can't reorder those positions without it being a non-backwards-compatible change that forces a new major version number when following [semver](https://semver.org/)). Throwing an error when someone passes positional parameters is a way to accomplish that, even if this isn't a robust mechanism for doing so as is possible. – Charles Duffy Feb 20 '19 at 22:20
  • See https://www.python.org/dev/peps/pep-3102/ – jonrsharpe Feb 20 '19 at 22:21
  • @CharlesDuffy: More likely, they don't want people relying on parameters' *positions*. After all, with positional arguments heavily discouraged this way, people have to rely on parameter names even more. – user2357112 Feb 20 '19 at 22:21
  • 1
    @user2357112, that's what I thought I was saying. – Charles Duffy Feb 20 '19 at 22:22
  • 1
    @JosepJoestar, ...so, when you do something like add an explicit `None`, you're going out of the way to defeat the API authors' clear intent, so it's your fault and not theirs if a minor version breaks your code's compatibility with their API. Think of it as a human-communication thing, not a technical issue. – Charles Duffy Feb 20 '19 at 22:24
  • I was not saying that this was an issue, just wondering why to prevent this behaviours. Thanks anyway! – josepdecid Feb 20 '19 at 22:34
  • I didn't interpret you as "saying that this was an issue". (Also not sure which value of "this" you're referring to). My above comments are focused only on answering the "why" question you asked. – Charles Duffy Feb 20 '19 at 23:03

3 Answers3

4

Positional parameters couple the caller and receiver on the order of the parameters. It makes refactoring the order of the reciver's parameters more difficult.

For example, if I have

def foo(a, b, c):
    do_stuff(a,b,c)

and I decide, for reasons, perhaps I want to make a partial function or whatever, that it would be better to have

def foo(b, a, c):
   do_stuff(a,b,c)

But now I have callers in the wild and it would be very rude to change my contract, so I'm stuck.

Sandi Metz in Practical Object-Oriented Design in Ruby also addresses this. (I know this is python, but oop is oop)

When the code [is changed to use keyword arguments], it lost its dependency on argument order but it gained a dependency on the names of the keys in the [keyword arguments]. This change is healthy. The new dependency is more stable than the old, and thus this code faces less risk of being forced to change. Additionally, and perhaps unexpectedly, the [keywords] provides one new, secondary benefit: The key names in the hash furnish explicit documentation about the arguments. This is a byproduct of using a hash but the fact that it is unintentional makes it no less useful. Future maintainers of this code will be grateful for the information.

Keyword arguments are also nice if you have a lot of parameters. Order is easy to get wrong. It may also make a nicer API in the opinion of the authors.

PEP-3102 also addresses this, but I find the rationale unsatisfying from the perspective of "why would I choose to design something like this"

The current Python function-calling paradigm allows arguments to be specified either by position or by keyword. An argument can be filled in either explicitly by name, or implicitly by position.

There are often cases where it is desirable for a function to take a variable number of arguments. The Python language supports this using the 'varargs' syntax (*name), which specifies that any 'left over' arguments be passed into the varargs parameter as a tuple.

One limitation on this is that currently, all of the regular argument slots must be filled before the vararg slot can be.

This is not always desirable. One can easily envision a function which takes a variable number of arguments, but also takes one or more 'options' in the form of keyword arguments. Currently, the only way to do this is to define both a varargs argument, and a 'keywords' argument (**kwargs), and then manually extract the desired keywords from the dictionary.

munk
  • 11,550
  • 7
  • 49
  • 70
0

First, a caveat that I can't know the intention of the person who wrote that. However, I can offer reason why “prevent positional parameters” might be desirable.

It's often important that a parameter be keyword-only, that is, it must be used only by name. The parameter is not conceptually an input to the function's purpose; it's more a modifier (change the behaviour in this way), or an external resource (here is the log file to emit your messages to), etc.

For that reason, Python 3 now allows you to define, in the signature of the function, specific parameters as keyword-only parameters. The change is documented in PEP 3102 Keyword-only arguments along with rationale.

bignose
  • 27,414
  • 13
  • 72
  • 104
0

What is the use for keyword only parameters:

  1. For some function, it is impossible to do otherwise (ex: print(a, b, end=''))
  2. It prevents you from making silly mistakes, consider the following example:
# if it wasn't made with kw-only parameters, this would return 3
>>> sorted(3, 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sorted expected 1 arguments, got 2
>>> sorted((1,2), reverse=True)
[2, 1]
  1. It allows you to change things later:
# if
def sorted(iterable, reverse=False)
# becomes
def sorted(iterable, key=None, reverse=False)
# you can guarantee backwards compatibility
Benoît P
  • 2,744
  • 12
  • 30