Re: [Tutor] Defining variable arguments in a function in python

2018-12-31 Thread boB Stepp
On Mon, Dec 31, 2018 at 10:36 AM David Rock  wrote:
>
> > On Dec 30, 2018, at 18:39, Alan Gauld via Tutor  wrote:
> >
> > On 30/12/2018 22:25, Avi Gross wrote:
> >
> >> I admit I have not studied the charter for the group.
> >
> > As moderator I feel the need to step in here because the
> > charter is extremely apropos to that function and some
> > clarification may be helpful.
>
> I would like to add an observation…
>
> The core complaint appears to be the length of the postings.  To my eye, 
> Avi’s style of posting is extremely verbose, which is not necessarily a bad 
> thing; but perhaps taking some time to distill the thoughts to make a 
> concise, on topic, point would be helpful in this case.  When discussions 
> appear to ramble at length in odd tangents, the helpfulness to the beginner 
> is diluted and the original point of the discussion is lost.

While I guess I am not considered here a rank beginner anymore, I
still know I have a LOT to learn.  In general I *try* to read all
posts on both the Tutor list and the main Python list.  However, I
must echo David's point -- I find Avi's posts way too long and
rambling, and while I am sure there are many useful nuggets to
explore, I find myself more and more often just doing a quick scan for
anything that just *pops out* that interests me, and, if not, hit
delete.

To Avi:  I know I would enjoy your posts much more if you would:  (1)
Get to the point quickly and stay on topic while addressing that
point.  Or, (2)  When you have a variety of disparate topics you would
like to discuss, break your one long post into a series of separate
emails, each with an appropriate subject line, and stick to each
post's topic implied by the subject line you choose.

I know I am often quite guilty on rambling on, so I know how hard it
is to do this.

I say all of this, Avi, not to be critical, but to hopefully enhance
everyone's opportunity to process and learn from your thoughts.

In any event, I hope all you Pythonistas have both a HAPPY AND BLESSED
NEW YEAR!!!

-- 
boB
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-31 Thread David Rock

> On Dec 30, 2018, at 18:39, Alan Gauld via Tutor  wrote:
> 
> On 30/12/2018 22:25, Avi Gross wrote:
> 
>> I admit I have not studied the charter for the group.
> 
> As moderator I feel the need to step in here because the
> charter is extremely apropos to that function and some
> clarification may be helpful.

I would like to add an observation…

The core complaint appears to be the length of the postings.  To my eye, Avi’s 
style of posting is extremely verbose, which is not necessarily a bad thing; 
but perhaps taking some time to distill the thoughts to make a concise, on 
topic, point would be helpful in this case.  When discussions appear to ramble 
at length in odd tangents, the helpfulness to the beginner is diluted and the 
original point of the discussion is lost.


— 
David Rock
da...@graniteweb.com




___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Steven D'Aprano
On Sun, Dec 30, 2018 at 11:07:19AM -0500, Avi Gross wrote:
> Steve,
> 
> I had the same thoughts and many more when I played with these ideas 
> last night.

Pity that one of those thoughts wasn't "I shouldn't suggest a bad 
solution on a mailing list populated by beginners who won't recognise 
how bad it is". That would have saved both you and me a lot of time.

> I thought I stated clearly that this was an EXPLORATION 

And I explored the problems with the design.

[...]
> Consider a NORMAL function with no external gimmicks. Say it 
> accepts an argument (positional or by name does not matter) and 
> processes it by checking they argument type, the specific values 
> against a range expected, and so on. It then unilaterally makes 
> changes. If it expects a single value of type integer, ang gets a 
> floating point number, it may round or truncate it back to an integer.
[...]

Sure. But in general, such a design applies a known transformation of 
input argument to value actually used:

- any number -> ignore any fraction part;
- any string -> ignore leading and trailing whitespace;
- any string -> treat as case-insensitive;
- list of values -> sorted list of values;

which is not the same replacing out-of-band values with some default 
value. In each of the above examples, there is a known, direct 
connection between the caller's argument, and the value used:

- the argument, ignoring any fraction part;
- the argument, ignoring leading and trailing whitespace;
- the argument, ignoring case differences;
- the argument, ignoring the original order of items;

rather than:

- throw away the argument, and use an unrelated default value.

The closest analogue would be clamping values to a given size, so 
that out-of-band values are truncated to the outermost value:

clamp(999, min=1, max=10)
=> returns 10

but even that should be used with care. And as I said, it is acceptable 
to use an dedicated sentinel value like None or Ellipsis.

But using otherwise valid-looking, but out of range, values as a trigger 
for the default should be used with care, if at all.

Some designs are better (more likely to be useful, less likely to lead 
to bugs and surprises) than others.


> If your choice is in some sense wrong, or just not convenient, it 
> replaces it with another choice. 

That would very likely be a poor or dangerous design. If your argument 
is "wrong", that's an error, and errors should not in general pass 
silently.


[...]
> Again, discussing a different scenario. Would you agree that kind of 
> design does happen and sometimes is seen as a smart thing but also as 
> full of pitfalls even sometimes for the wary?

Sure, it does happen, often badly. 


> In that spirit, any design like the ones I played with is equally 
> scary. Even worse, I have a QUESTION. Let me remind readers of the 
> initial idea behind this. You have a function in which you want to 
> communicate with the python ruler that it have a positional argument 
> that will be switched to a default. But the current rules are that the 
> only way to make something optional is by making it a keyword 
> parameter and declare the default right there.

No, that's not the current rules. Positional values can have default 
values too. You just can't skip positional values: there's no way of 
saying "I'm not supplying argument 1, but I am supplying argument 2" 
using only positional arguments:

function(a, b)  # provide argument 1 and 2

function(a) # only provide argument 1

function(b) still argument 1, not argument 2

[...]
> The problem is if you want a non-key word to be used as a default and 
> then also want to gather up additional positional arguments and so on. 
> There is no way designed to do that and possibly there should not be.

I'm open to suggestions for good designs that allow more flexible 
calling conventions, but as Python exists now, you can't skip over 
positional arguments except at the end.

> So what I am trying to understand is this. When someone types in a 
> function invocation and it is evaluated, when does the programmer get 
> a chance to intervene?

Aside from compile-time errors, any exception can be caught and 
processed *by the caller*:

try:
function(a, b)
except TypeError as e:
...

Python also allows you to customize the way exceptions are printed:

https://docs.python.org/3/library/sys.html#sys.excepthook

But the callee -- in this case, "function()" -- doesn't get a chance to 
customize the TypeError generated by the interpreter when you pass the 
wrong number of arguments, or an invalid keyword argument.

(Aside: it is unfortunate that TypeError gets used for both actual type 
errors, and mismatched arguments/parameter errors.)

[...]
> Is there a way to create a function and set it up so you have some 
> control over the error message?

Not easily.

You could, I suppose, take over parameter parsing yourself. Write your 
function like this:

def 

Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Alan Gauld via Tutor
On 30/12/2018 22:25, Avi Gross wrote:

> I admit I have not studied the charter for the group.

As moderator I feel the need to step in here because the
charter is extremely apropos to that function and some
clarification may be helpful.

Mark is correct in that the group is focused on the
core language and standard library as documented on
python.org.

That is, it does not cover third party modules
like SciPy or Pillow or frameworks like wxPython or
Django etc, regardless of how popular these may be.

That having been said, we do normally allow questions
on these modules to go through because there is a fair
amount of in-house expertise and so a fair chance
of providing an answer without protracted discussion.

The group also has a secondary mission included in
the statement Mark quoted, which is to help beginners
"learn computer programming" and that aspect of our
charter encompasses the occasional non-python led
topic on the nature of programming and the wider areas
of software engineering - although we prefer topics to
be illustrated using Python. Occasionally, language
comparisons are valid in highlighting areas where
Python is stronger or weaker relative to other tools.

The final valid area for discussion is the meta topic
of what (and how) the group should discuss. But that
subject raises its head very rarely these days.

> The standard library is not something I consider as 
> many distributions automatically add libraries ...

While that is true, the term 'standard library' is
generally understood to be the one distributed from
and documented with the official language. In our
case that means the python.org web site.

Distributions like Anaconda, Entropy, Maya, and
RaspberryPi etc all exist for specific audiences and
add modules suited to those tasks. But they also
have their own support communities better qualified
to deal with their issues.

Sometimes the best answer is a simple referral to
one of those community fora.

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Avi Gross
Mark,

I will be happy to make a somewhat less brief reply to a reasonable enough 
question.

I admit I have not studied the charter for the group. The standard library is 
not something I consider as many distributions automatically add libraries like 
np and pandas. I do accept your point. I still think it reasonable to MENTION 
the existence of some functionality that the person might use as some people 
just want to solve a problem, but clearly detailed examples and tutorials are 
way beyond that scope. I have unfortunately had people who got a private reply 
tell me to put it on the forum where clearly many are not interested.

On to the main point about recent posts that you replied to. Someone asked a 
question on how to do something using python.

Some responses boiled down to you can't do that.

One suggested something similar  in another way by asking what could be done in 
a context to make it happen, implying there was no answer.

I took the bait and in a very limited way suggested a context. Some may think 
the answer is not much of an answer or not useful and they probably have a 
point.

What I call an ACADEMIC exercise will vary. It may simply mean that I do not 
perceive any personal need to do things that way BUT since you asked, here is 
something ...

The language at any particular time and in a particular implementation allows 
you to do some things that are not necessarily a good idea. It is fair to 
suggest people avoid doing that but unfair to say it is WRONG as in it should 
not work. A dumb example is features that are deprecated but still exist. The 
goal is to switch, not keep using it till the last minute. But for a program 
you will run today then toss, it works!

I have to approach my role here carefully and appreciate that some choices will 
not be met well by some, including of course you. I can ignore some requests, 
and mainly do. There are others who can reply, often better. But when something 
catches my interest, it more often is not about an aspect of very basic python. 
I can reply and suggest the person not bother doing that, or perhaps not that 
way, or perhaps not use python. That might indeed teach them some things. Or, I 
can give a measured reply helping them see what can legally be done and maybe 
point out some tradeoffs including why it may not be the suggested way.

Last point. I promise! We often debate efficiency here. In retrospect, some 
people just want to solve a problem so all they may want is to find their error 
and let them do it as planned. Others ask if there is a more efficient way. 
Their question invites another approach. Unfortunately, that is when I often 
enter into an academic discussion. Maybe I should go back into teaching and 
bore people elsewhere.  I am sure that would make you happy for as long as 5 
minutes.


-Original Message-
From: Tutor  On Behalf Of Mark 
Lawrence
Sent: Sunday, December 30, 2018 4:35 PM
To: tutor@python.org
Subject: Re: [Tutor] Defining variable arguments in a function in python

On 30/12/2018 17:26, Avi Gross wrote:
> Replying to Steve's points. Again, it was not a serious design and 
> said so but was an ACADEMIC exploration of what could be done. I fully 
> agree with Steve that it is probably not a great idea to do this but 
> note the original request might not have been a great one in the first place.
> 

The usual massive snip, but what has an academic exploration got to do with 
learning Python on the tutor mailing list?  From 
https://mail.python.org/mailman/listinfo/tutor "This list is for folks who want 
to ask questions regarding how to learn computer programming with the Python 
language and its standard library. "  Also note the latter, nothing in there 
about pandas dataframes.

Now will you please go away.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Mark Lawrence

On 30/12/2018 17:26, Avi Gross wrote:

Replying to Steve's points. Again, it was not a serious design and said so
but was an ACADEMIC exploration of what could be done. I fully agree with
Steve that it is probably not a great idea to do this but note the original
request might not have been a great one in the first place.



The usual massive snip, but what has an academic exploration got to do 
with learning Python on the tutor mailing list?  From 
https://mail.python.org/mailman/listinfo/tutor "This list is for folks 
who want to ask questions regarding how to learn computer programming 
with the Python language and its standard library. "  Also note the 
latter, nothing in there about pandas dataframes.


Now will you please go away.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Avi Gross
Replying to Steve's points. Again, it was not a serious design and said so
but was an ACADEMIC exploration of what could be done. I fully agree with
Steve that it is probably not a great idea to do this but note the original
request might not have been a great one in the first place.

There are people who make a family of related functions and tell users to
pick the right one.

def add_two(x, y): return x+y
def add_three(x, y, z): return add_two(x,y)+z

You get the idea, I hope. So you can create one function in which an
argument is required and a sister function in which it is not allowed and a
default is built in. The user picks which function to call.

Again, NOT suggesting that is a great solution, just exploring
possibilities. There are often many ideas you can brainstorm before
rejecting most or all of them.

-Original Message-
From: Tutor  On Behalf Of
Steven D'Aprano
Sent: Sunday, December 30, 2018 5:39 AM
To: tutor@python.org
Subject: Re: [Tutor] Defining variable arguments in a function in python.

So everything Steve says as commentary and even criticism is indeed true. It
is a bug magnet and so on. So is just about any code I have seen, albeit not
as bad. A function with 40 positional arguments very often gets called with
arguments out of order for example. Some languages allow a row of empty
commas so you can specify the third and then the eight and then the
sixteenth and skip the others. 

Languages that use named arguments may be better but some get cute and will
match any argument that starts right. I mean you can call them with
layout="portrait" but also lay="landscape" or even l="..." and who knows
what they do if your allowed arguments include layers=True".

That is not a defense. It is an acknowledgement that many things we do have
drawbacks as well as advantages. On the whole, I doubt I would use the
design I offered except as an academic display of what not to do even if the
language allows it.

Having said that, I do defend the placeholder construct in places where it
is needed as a serious way to make a point.

Consider a request to do something frequent where the default should be
something like ALL, or ALL UP TO HERE, or ALL FROM HERE. Think of how you
take slices out of a list or other data structure including
multi-dimensional structures.

Here is an assortment of examples with many more possible. This is not a
function call exactly but the sub-language used allows many variations with
the use of what seems to be a series of up to three arguments with a colon
between them. But sometimes the absence of an argument makes it look like
the colon defaulted to something.

>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[ : 3]
[0, 1, 2]
>>> a[ 3: ]
[3, 4, 5, 6, 7, 8, 9]
>>> a[3:9]
[3, 4, 5, 6, 7, 8]
>>> a[3:9:2]
[3, 5, 7]
>>> a[9:3:-1]
[9, 8, 7, 6, 5, 4]
>>> a[-1]
9
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

So the DEFAULT for anything before the colon is a loose concept. It is
anything before whatever the next argument states. This shows that the
default works in the extreme case to be nothing:

>>> a[:0]
[]

The third optional argument is defaulted to be "1".

But underneath it all this is what is termed syntactic sugar. Each and every
such pattern is translated into a slice call:

>>> slice(1,9,4)
slice(1, 9, 4)
>>> a[slice(1,9,4)]
[1, 5]
>>> a[slice(None,9,4)]
[0, 4, 8]

Did you see the value None is used to mean use the darn DEFAULT?

It can be used for any of the parts or even all.

>>> a[slice(None,None,None)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

So here is an example of a dangerous function we all use regularly, albeit
usually indirectly, that uses my odd design principle. 

I can drown you with more examples, but since I don't hate anyone here, just
one more. Without details, the Ellipsis seems to have been partially created
to be used in the numpy module as a way to deal with indexing arrays with
multiple indices. It is not easy to say I want the first and the last an
skip the middle ones. Enough said. 

Steven is quite reasonably more concerned when the placeholder is invisibly
changed than when it is obviously just a placeholder. As he says you can
just look up the default in documentation and use it. I could argue that a
pythonic way to do things is to make it so if the default later changes, you
are not stuck. Then again, maybe you don't want it to change invisibly.  A
compromise would be to have the user explicitly ask for whatever is the
default as in soup="Du Jour" gets you whatever the soup of the day is, and
may even get you a salad if they ran out.

Explicit is often better. But you can have too much of a good thing. People
often create a wrapper to a function with too many required or optional
arguments. An example is a function used to read data in from a file. 

Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Avi Gross
Steve,

I had the same thoughts and many more when I played with these ideas last 
night. I thought I stated clearly that this was an EXPLORATION and not a 
serious solution. So if that is accepted, we can certainly discuss merits 
without giving demerits as well as the many flaws and dangers lurking in any 
such design.


To begin with, anyone trying any variants of a design like this needs to be 
sure it is well documented. I have seen functions that do something vaguely 
similar and often with unexpected results.

Consider a NORMAL function with no external gimmicks. Say it accepts an 
argument (positional or by name does not matter) and processes it by checking 
they argument type, the specific values against a range expected, and so on. It 
then unilaterally makes changes. If it expects a single value of type integer, 
ang gets a floating point number, it may round or truncate it back to an 
integer. If it gets a complex number, it drops the imaginary part. If it gets a 
list or tuple or iterator of any sort, it just takes the first entry and 
coerces it into an integer. If it sees a matrix or other challenging object, it 
flattens it then ... If it sees a character string, it tries to convert 
something like "one" to an integer. If your choice is in some sense wrong, or 
just not convenient, it replaces it with another choice. For example, it may 
move your integer to the nearest prime integer. It may reject your suggestion 
to make a window on your screen a trillion pixels wide and drop it to 1024. 

Sometimes it prints a warning about changes it has made. Sometimes it won't run 
but print out a message telling you what value it might have accepted.

Again, discussing a different scenario. Would you agree that kind of design 
does happen and sometimes is seen as a smart thing but also as full of pitfalls 
even sometimes for the wary?

In that spirit, any design like the ones I played with is equally scary. Even 
worse, I have a QUESTION. Let me remind readers of the initial idea behind 
this. You have a function in which you want to communicate with the python 
ruler that it have a positional argument that will be switched to a default. 
But the current rules are that the only way to make something optional is by 
making it a keyword parameter and declare the default right there. If the 
function template is:

def hello(a=5)

then you can do any of the following with reasonable results:

hello()
hello(7)
hello(a=7)

The default is only applied in the first version.

The problem is if you want a non-key word to be used as a default and then also 
want to gather up additional positional arguments and so on. There is no way 
designed to do that and possibly there should not be.

So what I am trying to understand is this. When someone types in a function 
invocation and it is evaluated, when does the programmer get a chance to 
intervene? Something in what I will call the interpreter seems to look at what 
the programmer typed just now and sees:
hello()
hello(1)
hello(1,2)

and so on. It looks up the object that encapsulates the function hello is 
pointing to. It examines the many parts hidden within and determines what the 
designer asked for. If it sees that the actual call does not match, it geerally 
produces an error message like this:

>>> def hello(n): print(n)

>>> hello()
Traceback (most recent call last):
  File "", line 1, in 
hello()
TypeError: hello() missing 1 required positional argument: 'n'

That is a reasonable thing to do. But in my idiotic design, I would like the 
user to be told a tad more. I want them told that if they want the default, use 
an n consisting of an ellipsis as in
hello(...)

to get the default.

Is there a way to create a function and set it up so you have some control over 
the error message? Without that, this kind of function is even more dangerous.

This message is long enough. I will reply to Steven's specific points in a bit. 
Still on vacation 


-Original Message-
From: Tutor  On Behalf Of Steven 
D'Aprano
Sent: Sunday, December 30, 2018 5:39 AM
To: tutor@python.org
Subject: Re: [Tutor] Defining variable arguments in a function in python

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 

Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Karthik Bhat
Thank you all for the quick response!

On Sun, Dec 30, 2018 at 10:39 AM Avi Gross  wrote:

> I have my usual off the wall answer.
>
> OK, seriously. Not exactly an answer but perhaps an experiment.
>
> The question was how to have a non-named first argument to a function with
> some form of default.
>
> As was pointed out, this does not fit well with being able to have python
> gather all positional arguments after it as well as all keyword arguments.
>
> But bear with me. Say I want to have a way to signal that I want a default
> for the first argument?
>
> An empty comma fails but try this:
>
> def hello(a, *n, **m) :
> if a == None: a=5
> print(a)
> print(*n)
> print(**m)
>
> The above says "a" is required. It can be followed by any number of
> positional args gathered into "n" and any number of keyword args gathered
> into "m"
>
> But what if you define a sentinel to watch for such as None, in the above?
>
> If the first and only arg is None, it switches to the default of 5.
>
> >>> hello(None)
> 5
>
> Add a few more args and it properly takes it.
>
> >>> hello(1,2,3)
> 1
> 2 3
>
> Switch the first to None:
>
> >>> hello(None,2,3)
> 5
> 2 3
>
> The keywords don't work for print but no biggie.
>
> But is this only for None? What I say any negative arg is replaced by 5?
>
> def hello(a, *n, **m) :
> if a < 0: a=5
> print(a)
> print(*n)
>
> Seems to work fine:
>
> >>> hello(-666, 2, 3, 4)
> 5
> 2 3 4
>
> And I wonder if we can use the darn ellipsis for something useful?
>
> def hello(a, *n, **m) :
> if a == ... : a=5
> print(a)
> print(*n)
>
> >>> hello(1,2,3)
> 1
> 2 3
> >>> hello(...,2,3)
> 5
> 2 3
> >>> hello(...,2,...)
> 5
> 2 Ellipsis
>
> OK, all kidding aside, is this helpful? I mean if you want a function where
> you MUST give at least one arg and specify the first arg can be some odd
> choice (as above) and then be replaced by  a default perhaps it would be
> tolerable to use None or an Ellipsis.
>
> 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
> >>> hello("infinity and beyond",2,3)
> Traceback (most recent call last):
>   File "", line 1, in 
> hello("infinity and beyond",2,3)
>   File "", line 2, in hello
> if not (1 <= a <= 10) : a=5
> TypeError: '<=' not supported between instances of 'int' and 'str'
>
> As expected, it may take a bit more code such as checking if you got an int
> but the idea may be solid enough. It is NOT the same as having a default
> from the command line but it may satisfy some need.
>
> Other than that, I fully agree that the current python spec cannot support
> anything like this in the function definition.
>
> Side note: To spare others, I sent Steven alone a deeper reply about ways
> to
> select random rows from a pandas DataFrame. I am still learning how pandas
> works and doubt many others here have any immediate needs.
>
>
>
>
>
>
>
>
>
> -Original Message-
> From: Tutor  On Behalf Of
> Steven D'Aprano
> Sent: Saturday, December 29, 2018 6:02 AM
> To: tutor@python.org
> Subject: Re: [Tutor] Defining variable arguments in a function in python
>
> On Sat, Dec 29, 2018 at 11:42:16AM +0530, Karthik Bhat wrote:
> > Hello,
> >
> > I have the following piece of code. In this, I wanted to make
> > use of the optional parameter given to 'a', i.e- '5', and not '1'
> >
> > def fun_varargs(a=5, *numbers, **dict):
> [...]
> >
> > fun_varargs(1,2,3,4,5,6,7,8,9,10,Jack=111,John=222,Jimmy=333)
> >
> > How do I make the tuple 'number' contain the first element to be 1 and
> not
> 2?
>
>
> You can't. Python allocates positional arguments like "a" first, and only
> then collects whatever is left over in *numbers. How else would you expect
> it to work? Suppose you called:
>
> fun_varargs(1, 2, 3)
>
> wanting a to get the value 1, and numbers to get the values (2, 3). And
> then
> immediately after that you call
>
> fun_varargs(1, 2, 3)
>
> wanting a to g

Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Peter Otten
Avi Gross wrote:

> To spare others, 

Thank you for that.

> I sent Steven alone

'Tis well deserved ;)


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-30 Thread Steven D'Aprano
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


Re: [Tutor] Defining variable arguments in a function in python

2018-12-29 Thread Avi Gross
I have my usual off the wall answer.

OK, seriously. Not exactly an answer but perhaps an experiment.

The question was how to have a non-named first argument to a function with
some form of default. 

As was pointed out, this does not fit well with being able to have python
gather all positional arguments after it as well as all keyword arguments.

But bear with me. Say I want to have a way to signal that I want a default
for the first argument?

An empty comma fails but try this:

def hello(a, *n, **m) :
if a == None: a=5
print(a)
print(*n)
print(**m)

The above says "a" is required. It can be followed by any number of
positional args gathered into "n" and any number of keyword args gathered
into "m"

But what if you define a sentinel to watch for such as None, in the above?

If the first and only arg is None, it switches to the default of 5.

>>> hello(None)
5

Add a few more args and it properly takes it.

>>> hello(1,2,3)
1
2 3

Switch the first to None:

>>> hello(None,2,3)
5
2 3

The keywords don't work for print but no biggie.

But is this only for None? What I say any negative arg is replaced by 5?

def hello(a, *n, **m) :
if a < 0: a=5
print(a)
print(*n)

Seems to work fine:

>>> hello(-666, 2, 3, 4)
5
2 3 4

And I wonder if we can use the darn ellipsis for something useful?

def hello(a, *n, **m) :
if a == ... : a=5
print(a)
print(*n)

>>> hello(1,2,3)
1
2 3
>>> hello(...,2,3)
5
2 3
>>> hello(...,2,...)
5
2 Ellipsis

OK, all kidding aside, is this helpful? I mean if you want a function where
you MUST give at least one arg and specify the first arg can be some odd
choice (as above) and then be replaced by  a default perhaps it would be
tolerable to use None or an Ellipsis.

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
>>> hello("infinity and beyond",2,3)
Traceback (most recent call last):
  File "", line 1, in 
hello("infinity and beyond",2,3)
  File "", line 2, in hello
if not (1 <= a <= 10) : a=5
TypeError: '<=' not supported between instances of 'int' and 'str'

As expected, it may take a bit more code such as checking if you got an int
but the idea may be solid enough. It is NOT the same as having a default
from the command line but it may satisfy some need.

Other than that, I fully agree that the current python spec cannot support
anything like this in the function definition.

Side note: To spare others, I sent Steven alone a deeper reply about ways to
select random rows from a pandas DataFrame. I am still learning how pandas
works and doubt many others here have any immediate needs.









-----Original Message-
From: Tutor  On Behalf Of
Steven D'Aprano
Sent: Saturday, December 29, 2018 6:02 AM
To: tutor@python.org
Subject: Re: [Tutor] Defining variable arguments in a function in python

On Sat, Dec 29, 2018 at 11:42:16AM +0530, Karthik Bhat wrote:
> Hello,
> 
> I have the following piece of code. In this, I wanted to make 
> use of the optional parameter given to 'a', i.e- '5', and not '1'
> 
> def fun_varargs(a=5, *numbers, **dict):
[...]
> 
> fun_varargs(1,2,3,4,5,6,7,8,9,10,Jack=111,John=222,Jimmy=333)
> 
> How do I make the tuple 'number' contain the first element to be 1 and not
2?


You can't. Python allocates positional arguments like "a" first, and only
then collects whatever is left over in *numbers. How else would you expect
it to work? Suppose you called:

fun_varargs(1, 2, 3)

wanting a to get the value 1, and numbers to get the values (2, 3). And then
immediately after that you call 

fun_varargs(1, 2, 3)

wanting a to get the default value 5 and numbers to get the values (1, 2,
3). How is the interpreter supposed to guess which one you wanted?

If you can think of a way to resolve the question of when to give "a" 
the default value, then we can help you program it yourself:


def func(*args, **kwargs):
if condition:
# When?
a = args[0]
numbers = args[1:]
else:
a = 5  # Default.
numbers = args
...

But writing that test "condition" is the hard part.




--
Steve
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-29 Thread Peter Otten
Karthik Bhat wrote:

> Hello,
> 
> I have the following piece of code. In this, I wanted to make use
> of the optional parameter given to 'a', i.e- '5', and not '1'
> 
> def fun_varargs(a=5, *numbers, **dict):
> print("Value of a is",a)
> 
> for i in numbers:
> print("Value of i is",i)
> 
> for i, j in dict.items():
> print("The value of i and j are:",i,j)
> 
> fun_varargs(1,2,3,4,5,6,7,8,9,10,Jack=111,John=222,Jimmy=333)
> 
> How do I make the tuple 'number'  contain the first element to be 1 and
> not 2?

One option is to change the function signature to

def fun_varargs(*numbers, a=5, **dict):
...

which turns `a` into a keyword-only argument.

>>> fun_varargs(1, 2, 3, foo="bar"):
Value of a is 5
Value of i is 1
Value of i is 2
Value of i is 3
The value of i and j are: foo bar

To override the default you have to specify a value like this:

>>> fun_varargs(1, 2, 3, foo="bar", a=42)
Value of a is 42
Value of i is 1
Value of i is 2
Value of i is 3
The value of i and j are: foo bar


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-29 Thread Steven D'Aprano
On Sat, Dec 29, 2018 at 11:42:16AM +0530, Karthik Bhat wrote:
> Hello,
> 
> I have the following piece of code. In this, I wanted to make use
> of the optional parameter given to 'a', i.e- '5', and not '1'
> 
> def fun_varargs(a=5, *numbers, **dict):
[...]
> 
> fun_varargs(1,2,3,4,5,6,7,8,9,10,Jack=111,John=222,Jimmy=333)
> 
> How do I make the tuple 'number' contain the first element to be 1 and not 2?


You can't. Python allocates positional arguments like "a" first, and 
only then collects whatever is left over in *numbers. How else would you 
expect it to work? Suppose you called:

fun_varargs(1, 2, 3)

wanting a to get the value 1, and numbers to get the values (2, 3). And 
then immediately after that you call 

fun_varargs(1, 2, 3)

wanting a to get the default value 5 and numbers to get the values 
(1, 2, 3). How is the interpreter supposed to guess which one you 
wanted?

If you can think of a way to resolve the question of when to give "a" 
the default value, then we can help you program it yourself:


def func(*args, **kwargs):
if condition:
# When?
a = args[0]
numbers = args[1:]
else:
a = 5  # Default.
numbers = args
...

But writing that test "condition" is the hard part.




-- 
Steve
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Defining variable arguments in a function in python

2018-12-29 Thread Alan Gauld via Tutor
On 29/12/2018 06:12, Karthik Bhat wrote:

> def fun_varargs(a=5, *numbers, **dict):
> print("Value of a is",a)
> 
> for i in numbers:
> print("Value of i is",i)
> 
> for i, j in dict.items():
> print("The value of i and j are:",i,j)
> 
> fun_varargs(1,2,3,4,5,6,7,8,9,10,Jack=111,John=222,Jimmy=333)
> 
> How do I make the tuple 'number'  contain the first element to be 1 and not
> 2?

You need to provide a value for a.

The default 5 will only be used if the function is called
without *any* arguments. Otherwise it will always take
the first argument value. So, if you want a to be 5 and
then provide a tuple etc you must explicitly pass a 5 in:

fun_varargs(5, 1,2,3,4,5,6,7,8,9,10, Jack=111,John=222,Jimmy=333)


HTH
-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


[Tutor] Defining variable arguments in a function in python

2018-12-29 Thread Karthik Bhat
Hello,

I have the following piece of code. In this, I wanted to make use
of the optional parameter given to 'a', i.e- '5', and not '1'

def fun_varargs(a=5, *numbers, **dict):
print("Value of a is",a)

for i in numbers:
print("Value of i is",i)

for i, j in dict.items():
print("The value of i and j are:",i,j)

fun_varargs(1,2,3,4,5,6,7,8,9,10,Jack=111,John=222,Jimmy=333)

How do I make the tuple 'number'  contain the first element to be 1 and not
2?


-- 
Regards,
Karthik A Bhat
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor