On Sun, Dec 30, 2018 at 12:07:20AM -0500, Avi Gross wrote: [...] > Or on a more practical level, say a function wants an input from 1 to 10. > The if statement above can be something like: > > >>> def hello(a, *n, **m) : > if not (1 <= a <= 10) : a=5 > print(a) > print(*n) > > > >>> hello(1,2,3) > 1 > 2 3 > >>> hello(21,2,3) > 5 > 2 3 > >>> hello(-5,2,3) > 5 > 2 3
This design is an example of "an attractive nuisance", possibly even a "bug magnet". At first glance, when used for mickey-mouse toy examples like this, it seems quite reasonable: hello(999, 1, 2) # I want the default value instead of 999 but thinking about it a bit more deeply, and you will recognise some problems with it. First problem: How do you know what value to pass if you want the default? Is 999 out of range? How about 11? 10? Who knows? If you have to look up the docs to know what counts as out of range, you might as well read the docs to find out what the default it, and just pass that: hello(5, 1, 2) # I want the default value 5 but that kind of defeats the purpose of a default. The whole point of a default is that you shouldn't need to pass *anything at all*, not even a placeholder. (If you need a placeholder, then you probably need to change your function parameters.) But at least with sentinels like None, or Ellipsis, it is *obvious* that the value is probably a placeholder. With a placeholder like 11 or 999, it isn't. They look like ordinary values. Second problem: Most of the time, we don't pass literal values to toy functions. We do something like this example: for number, widget_ID, delivery_date in active_orders: submit_order(number, widget_ID, delivery_date) Can you see the bug? Of course you can't. There's no obvious bug. But little do you know, one of the orders was accidentally entered with an out-of-range value, let's say -1, and instead of getting an nice error message telling you that there's a problem that you need to fix, the submit_order() function silently replaces the erroneous value with the default. The bug here is that submit_order() function exceeds its authority. The name tells us that it submits orders, but it also silently decides to change invalid orders to valid orders using some default value. But this fact isn't obvious from either the name or the code. You only learn this fact by digging into the source code, or reading the documentation, and let's be honest, nobody wants to do either of those unless you really have to. So when faced with an invalid order, instead of getting an error that you can fix, or even silently skipping the bad order, the submit_order() function silently changes it to a valid-looking but WRONG order that you probably didn't want. And that costs real money. The risk of this sort of bug comes directly from the design of the function. While I suppose I must acknowledge that (hypothetically) there could be use-cases for this sort of design, I maintain that in general this design is a bug magnet: responsibility for changing out-of-range values to in-range values belongs with the caller, not the called function. The caller may delegate that responsibility to another: for number, widget_ID, delivery_date in active_orders: number = validate_or_replace(number) submit_order(number, widget_ID, delivery_date) which is fine because it is explicit and right there in plain sight. This then allows us to make the submit_order() far more resiliant: if it is passed an invalid order, it can either fail fast, giving an obvious error, or at least skip the invalid order and notify the responsible people. -- Steven _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor