From robertvstepp at gmail.com Wed Jan 1 13:30:13 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Wed, 1 Jan 2020 12:30:13 -0600 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? Message-ID: In a November thread I started, "How to type annotate complex dictionary containing lambdas?" (https://mail.python.org/pipermail/tutor/2019-November/115726.html), I justifiably received multiple critiques on what an awful dictionary I had created. Eventually I responded that the program that that dictionary was from (and other dictionaries like it) came from using what originally was a very simple script which over time became rather complex and gnarly from using it to try out different things I was learning about over time. At the time I started the aforementioned thread my learning topic of the day was type annotations, and, of course, I used my go to "simple" (Hah!) script to play around with that concept. So perhaps in line with D. L. Neil's "Friday Finking" series on the main Python list, I would like to start a discussion on how to properly transform a simple Python script into a battle-hardened program suitable for release to the general public. Below is my best reconstruction of what I probably originally wrote once upon a time: A simple throwaway script> ============================================================================ #!/usr/bin/env python3 """Calculate the number of pages per day needed to read a book by a given date.""" from datetime import date COUNT_TODAY = 1 num_pages_in_book = int(input("How many pages are there in your book? ")) num_pages_read = int(input("How many pages have you read? ")) today_date = date.today() goal_date = date.fromisoformat( input("What is your date to finish the book (yyyy-mm-dd)? ") ) days_to_goal = (goal_date - today_date).days pages_per_day = (num_pages_in_book - num_pages_read) / (days_to_goal + COUNT_TODAY) print( "\nYou must read", pages_per_day, "pages each day (starting today) to reach your goal.", ) ============================================================================ As this script is I believe it to be easy to read and understand by an outside reader. For my original purposes it quickly gave me what I wanted out of it. But if I wanted to "give it away" to the general public there are some things that bother me: 1) There is no error checking. If someone deliberately or mistakenly enters incorrect input the program will terminate with a ValueError exception. 2) There are no checks for logic errors. For instance, a person could enter a goal date that is prior to today or enter a date in the future that falls well beyond any human's possible lifespan (with current technology). 3) There is no formatting of output. It is possible to generate float output with many decimal places, something most users would not want to see. And does it make any sense to have non-integral numbers of pages anyway? 4) There are duplicated patterns of input in the code as is. Surely that duplication could be removed? 5) A minor, but bothersome quibble: If the reader need only read one page per day, the program would display an annoying "... 1.0 pages ...", which is grammatically incorrect. 6) There are no tests, which makes it more difficult to grow/maintain the program in the future. 7) The user must restart the program each time he/she wishes to try out different goal dates. 8) [Optional] There are no type annotations. So I am curious as to how you pros would approach the refactoring process. I think that such a demonstration would surely be educational for myself, and hopefully others. But I do realize that everyone does their thing voluntarily and my thoughts might not align with yours. So I am *hoping* a few experts go along with this, but, regardless, in typical boB fashion I will plow away and post my future efforts to transform version1.py into something more robust. Then those that wish to can critique version2.py leading ultimately to version-who-knows-what.py ... Anyway, it seems like a useful New Year's exercise that might actually be helpful. HAPPY NEW YEAR TO YOU AND YOURS!!! -- boB From gogonegro at gmail.com Wed Jan 1 11:20:08 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Wed, 1 Jan 2020 17:20:08 +0100 Subject: [Tutor] Setting thresholds in a compact way Message-ID: Dear friends, First of all let me wish a great 2020 for all of you kind souls and seekers of knowledge :) I am trying to setup a varying time.sleep value (in house) depending on a priorly determined threshold. If threshold <= .9 AND > .8 then sleep should be 6 hours if threshold <= 0.8 AND > .4 then sleep should be 4 hours if threshold <= .4 AND > .1 then sleep should be 3 hours if threshold <= .1 then sleep should be set to 1 hour Instead of a complex/lengthy series of (nested?) if/ands based on the above, I was wondering if I could declare a dictionary such as: sleep_thresholds_hours = {.9:6, .8:4, .4:3, .1:1} And then use some sort of single statement to set the sleep_time to the the proper value. Not even quite sure that using floats (albeit single decimal) as dictionary keys will be ok. Getting old and in 2020 even older and my brain fogged by too much food :) Thanks in advance From Richard at Damon-Family.org Wed Jan 1 14:18:53 2020 From: Richard at Damon-Family.org (Richard Damon) Date: Wed, 1 Jan 2020 14:18:53 -0500 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: On 1/1/20 11:20 AM, Robert Alexander wrote: > Dear friends, > First of all let me wish a great 2020 for all of you kind souls and seekers > of knowledge :) > > I am trying to setup a varying time.sleep value (in house) depending on a > priorly determined threshold. > > If threshold <= .9 AND > .8 then sleep should be 6 hours > if threshold <= 0.8 AND > .4 then sleep should be 4 hours > if threshold <= .4 AND > .1 then sleep should be 3 hours > if threshold <= .1 then sleep should be set to 1 hour > > Instead of a complex/lengthy series of (nested?) if/ands based on the > above, I was wondering if I could declare a dictionary such as: > > sleep_thresholds_hours = {.9:6, .8:4, .4:3, .1:1} > > And then use some sort of single statement to set the sleep_time to the the > proper value. > > Not even quite sure that using floats (albeit single decimal) as dictionary > keys will be ok. > > Getting old and in 2020 even older and my brain fogged by too much food :) > > Thanks in advance The straight forward way to state that (since you ranges are all consecutive) would be if threshold <= 0.1: sleep(1 hour) elif threshold <= 0.4: sleep (3 hour) elif threshold <= 0.8: sleep(4 hours) elif threshold <= 0.9: sleep(6 hours) else: # what you want to do otherwise. You can also simplify your if statements (and get the right syntax with if 0.8 < threshold <= 0.9: sleep 6 hours if 0.4 < threshold <= 0.8: sleep 4 hours if 0.1 < threshold <= 0.4: sleep 3 hours if threshold <= 0.1: sleep 1 hour The dictionary is an interesting idea, but I don't know of a built in way do the sort of lookup you are suggesting. Dictionaries are designed to optimize looking up exact values quickly, not find the next value. -- Richard Damon From robertvstepp at gmail.com Wed Jan 1 14:21:28 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Wed, 1 Jan 2020 13:21:28 -0600 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: Greetings! On Wed, Jan 1, 2020 at 12:47 PM Robert Alexander wrote: > I am trying to setup a varying time.sleep value (in house) depending on a > priorly determined threshold. > > If threshold <= .9 AND > .8 then sleep should be 6 hours > if threshold <= 0.8 AND > .4 then sleep should be 4 hours > if threshold <= .4 AND > .1 then sleep should be 3 hours > if threshold <= .1 then sleep should be set to 1 hour Python allows you to write a somewhat simpler if condition instead of using "and": if 0.8 < threshold <= 0.9: etc. Are you aware of Python's if - elif - else structure? In your case it could be: if 0.8 < threshold <= 0.9: elif 0.4 < threshold <= 0.8: elif 0.1 < threshold <= 0.4: elif threshold <= 0.1: # What about negative values of threshold? else: I'll leave your other questions to someone else. HAPPY NEW YEAR TO YOU AND YOURS!!! -- boB From mats at wichmann.us Wed Jan 1 15:08:06 2020 From: mats at wichmann.us (Mats Wichmann) Date: Wed, 1 Jan 2020 13:08:06 -0700 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: On 1/1/20 9:20 AM, Robert Alexander wrote: > Not even quite sure that using floats (albeit single decimal) as dictionary > keys will be ok. It's not ideal - it isn't illegal, but comparison of binary floats can be trickier than you expect (not just in Python!), and because dictionary lookups are based on hashing the key, you can get interesting problems. It's fairly easy to mitigate that problem - round your threshold value, or use decimal floats, or use the old trick of multiplying by 10 and calling it an int - that is, for 0.4, use 4, etc. dictionaries, however, aren't really that natural a fit for this kind of thing. They're perfect when you're doing an exact match - give me the value for key "foo". But you want find where you fall in ranges instead. As others have said, I think the if/elif sequence would be fine for this, although it will look ugly if you have too many different buckets. From robertvstepp at gmail.com Wed Jan 1 17:27:57 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Wed, 1 Jan 2020 16:27:57 -0600 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: On Wed, Jan 1, 2020 at 2:08 PM Mats Wichmann wrote: > As others have said, I think the if/elif sequence would be fine for > this, although it will look ugly if you have too many different buckets. Is there any sensible/better thing to do if one is stuck with a long sequence of "if" conditions? I seem to recall some discussion on this topic on the main list, but do not recollect anything gaining a consensus as to best practice. HAPPY NEW YEAR TO YOU AND YOURS!!! -- boB From Richard at Damon-Family.org Wed Jan 1 17:45:54 2020 From: Richard at Damon-Family.org (Richard Damon) Date: Wed, 1 Jan 2020 17:45:54 -0500 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: <8f282d5a-0c64-8d53-c140-61af0f6cad66@Damon-Family.org> On 1/1/20 5:27 PM, boB Stepp wrote: > On Wed, Jan 1, 2020 at 2:08 PM Mats Wichmann wrote: > >> As others have said, I think the if/elif sequence would be fine for >> this, although it will look ugly if you have too many different buckets. > Is there any sensible/better thing to do if one is stuck with a long > sequence of "if" conditions? I seem to recall some discussion on this > topic on the main list, but do not recollect anything gaining a > consensus as to best practice. > > HAPPY NEW YEAR TO YOU AND YOURS!!! > IF you DO have a very long list, or otherwise want a data driven design, you could build a list of tuples, the tuples specifying the range and value, and search that list for the entry you want. If you build the list in a sorted order, you could binary search the list, and/or make it just hold the upper bounds. -- Richard Damon From wpdickey at usc.edu Wed Jan 1 18:22:22 2020 From: wpdickey at usc.edu (William Dickey) Date: Wed, 1 Jan 2020 15:22:22 -0800 Subject: [Tutor] Beginning String Problems Message-ID: I am learning Python via an online video "Beginning Python" by William Fiset. I am at the end of chapter 2 and I am supposed to be inputting a string that has an output of a persons full name in this order *First M. Last*, but when I copied what he was doing it is not working. Here is what I input: first_name = str(input("Please enter your first name: ")) middle_name = str(input("Please enter your middle name: ")) last_name = str(input("Please enter your last name: ")) first_name = first_name.capitalize() middle_name = middle_name.capitalize() last_name = last_name.capitalize() name_format = "{first} {middle:.1s} {last}" print(name_format.format(first=first_name, middle=middle_name, last=last_name)) and here is what I get when I open it in the command line I enter the file location and I get: Please enter your first name: I enter william and I get this error: Traceback (most recent call): File "C:\xxx\xxx\xxx\xxx", line 3, in first_name = str(input("Please enter your first name: ")) File "", line 1, in NameError: name 'william' is not defined I am using Windows 10 64 bit, Atom text editor, Python 3.4. Thank you William From PyTutor at danceswithmice.info Wed Jan 1 19:19:19 2020 From: PyTutor at danceswithmice.info (DL Neil) Date: Thu, 2 Jan 2020 13:19:19 +1300 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: Message-ID: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Echo: Best New Year wishes to you and yours. Surprise: I run a series? OK, yes it must be true. On-line it is usually an extension of discussions, I or A.N.Other has provoked over-the-tea-cups/during a tea/coffee/meal break, which has become something of a Friday tradition amongst one of the teams with which I often work. It is a mentoring program(me) - a training opportunity for 'master' level programmers and a learning-opportunity for 'apprentices', and often, 'journeymen'. (all due apologies...) On 2/01/20 7:30 AM, boB Stepp wrote: > In a November thread I started, "How to type annotate complex > dictionary containing lambdas?" > (https://mail.python.org/pipermail/tutor/2019-November/115726.html), I > justifiably received multiple critiques on what an awful dictionary I > had created. Eventually I responded that the program that that > dictionary was from (and other dictionaries like it) came from using > what originally was a very simple script which over time became rather > complex and gnarly from using it to try out different things I was > learning about over time. At the time I started the aforementioned > thread my learning topic of the day was type annotations, and, of > course, I used my go to "simple" (Hah!) script to play around with > that concept. So perhaps in line with D. L. Neil's "Friday Finking" > series on the main Python list, I would like to start a discussion on > how to properly transform a simple Python script into a > battle-hardened program suitable for release to the general public. ... > So I am curious as to how you pros would approach the refactoring > process. I think that such a demonstration would surely be > educational for myself, and hopefully others. But I do realize that > everyone does their thing voluntarily and my thoughts might not align > with yours. So I am *hoping* a few experts go along with this, but, > regardless, in typical boB fashion I will plow away and post my future > efforts to transform version1.py into something more robust. Then > those that wish to can critique version2.py leading ultimately to > version-who-knows-what.py ... > > Anyway, it seems like a useful New Year's exercise that might actually > be helpful. > > HAPPY NEW YEAR TO YOU AND YOURS!!! Why did I cut-out the code and the suggested improvements? Because the reason why many of us are reluctant to "maintain" or "improve" someone-else's code is an anxiety that if we fail to understand every nuance of thinking that went into the original author's work, we will introduce errors! Indeed, any number of programming 'management' books (or chapters in 'advanced' texts) will recount how programming (dept) productivity declines with the age of a (any) system. The reason is not merely the lethargy or reluctance devs have for maintenance work, but the reality of the system's growing complexity requiring every 'change' to be the culmination of an every-growing effort to investigate any 'corner' of the system which may contribute input or require output from the code in question! Even looking at this single routine, not only will the code-complexity creep upwards (as described, above), but 'just a little change' and 'quick fix' type approaches lead to such ghastly artifacts becoming common-place! Accordingly, should we pay any regard to 'the rest', before completing: > 6) There are no tests, which makes it more difficult to grow/maintain > the program in the future. I've just re-read an interesting observation of the collection of tests one might build-up, as a system grows - a challenging question to ponder: - if the code and all other documentation of a program/system were 'lost', could you?should you, be able to reconstruct the whole from the test-set alone? I am currently working on code which aims to re-use other classes. There are three different data-structures which may be used as input, from basic strings, through PSL class, to a locally-coded data-object; the consideration then becomes: were all the inputs provided or do (different for the various sub-classes) defaults apply; then there are all the different real-world combinations which the data represents, eg does the transaction currently 'exist' or is it hypothetical at this point; and they're only the first three considerations which generate permutations or combinations! During the task, I have found that the new code has failed because I didn't adequately code around my own tests (TDD's intent), but also two further conditions: 1 my assumptions about how the underlying classes work were incomplete/faulty - and thus my 'new code' was inadequate, and 2 that when certain things didn't work, it was a fault in the provided classes - apparently a combination of factors they did not anticipate, and in at least one case, had no test to reveal (but my tests of my code (unintentionally) did). Whilst the latter was frustrating (which is one of the points I'm making), the advantage of being given not just the code (classes, etc) for re-use, but the tests to verify that code (and consequently the superstructure I was building 'above' it) have been an enormous advantage - had I needed to 'read the code' with no further help, perhaps I would have thrown it out and 're-invented the wheel'! (which decision, strangely enough, has justified many an (expensive!) system-rewrite project!) -- Regards =dn From robertvstepp at gmail.com Wed Jan 1 20:01:42 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Wed, 1 Jan 2020 19:01:42 -0600 Subject: [Tutor] Beginning String Problems In-Reply-To: References: Message-ID: Greetings! On Wed, Jan 1, 2020 at 6:15 PM William Dickey wrote: > > I am learning Python via an online video "Beginning Python" by William > Fiset. I am at the end of chapter 2 and I am supposed to be inputting a > string that has an output of a persons full name in this order *First M. > Last*, but when I copied what he was doing it is not working. Here is what > I input: > > first_name = str(input("Please enter your first name: ")) > middle_name = str(input("Please enter your middle name: ")) > last_name = str(input("Please enter your last name: ")) N.B.: Using str() on the above 3 lines is unnecessary as input() always returns a string from the user's input. > first_name = first_name.capitalize() > middle_name = middle_name.capitalize() > last_name = last_name.capitalize() > > name_format = "{first} {middle:.1s} {last}" > print(name_format.format(first=first_name, middle=middle_name, > last=last_name)) > > and here is what I get when I open it in the command line > > I enter the file location and I get: > Please enter your first name: > > I enter william and I get this error: > > Traceback (most recent call): > File "C:\xxx\xxx\xxx\xxx", line 3, in > first_name = str(input("Please enter your first name: ")) > File "", line 1, in > NameError: name 'william' is not defined > > I am using Windows 10 64 bit, Atom text editor, Python 3.4. I am just a hobbyist programmer. But when I copy and pasted your code as given in your email into a file and then ran it in the terminal (I'm on Linux, using Python 3.7.5) everything worked well: bob at Dream-Machine1:~/Projects/Tutor_Help$ python3 ./test.py Please enter your first name: boB Please enter your middle name: Vincent Please enter your last name: Stepp Bob V Stepp So I am puzzled. Are you certain that you do not have your name "william" written on the top line of your Python file? If yes, then Python will see that as an undefined variable name, since you wouldn't have assigned anything to it. That is my best guess anyway. BTW, for a first time poster I feel you did a rather good job asking your question! HAPPY NEW YEAR TO YOU AND YOURS!!! -- boB From mats at wichmann.us Wed Jan 1 20:06:17 2020 From: mats at wichmann.us (Mats Wichmann) Date: Wed, 1 Jan 2020 18:06:17 -0700 Subject: [Tutor] Beginning String Problems In-Reply-To: References: Message-ID: On 1/1/20 6:01 PM, boB Stepp wrote: > Greetings! > > On Wed, Jan 1, 2020 at 6:15 PM William Dickey wrote: >> I am using Windows 10 64 bit, Atom text editor, Python 3.4. this has *nothing* to do with your issue, which I can't think about because I have to finish putting dinner on the table :) but... don't use 3.4. It's quite old, going on six years if I recall. 3.8 is the current release. If your material says "install 3.4", you should be able to safely use 3.8 in all but some real edge cases that no course would likely ever touch (the alternative is not true: something written for 3.8 might hit more issues using 3.4). > > I am just a hobbyist programmer. But when I copy and pasted your code > as given in your email into a file and then ran it in the terminal > (I'm on Linux, using Python 3.7.5) everything worked well: > > bob at Dream-Machine1:~/Projects/Tutor_Help$ python3 ./test.py > Please enter your first name: boB > Please enter your middle name: Vincent > Please enter your last name: Stepp > Bob V Stepp > > So I am puzzled. Are you certain that you do not have your name > "william" written on the top line of your Python file? If yes, then > Python will see that as an undefined variable name, since you wouldn't > have assigned anything to it. That is my best guess anyway. > > BTW, for a first time poster I feel you did a rather good job asking > your question! > > HAPPY NEW YEAR TO YOU AND YOURS!!! > From alan.gauld at yahoo.co.uk Wed Jan 1 20:07:42 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 2 Jan 2020 01:07:42 +0000 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: On 01/01/2020 22:27, boB Stepp wrote: > Is there any sensible/better thing to do if one is stuck with a long > sequence of "if" conditions? Richard already suggested a sorted list of tuples, which is usually the simplest option, although that carries the overhead of a loop to test the list entries. The other option is to use a sparse array where the values are the functions to be called and the test values are the indices into the array. (multiple indices potentially mapping to the same code.) However that requires that you can reduce your test values to some kind of quantum value (possibly by multiplying the float by 10,100 etc and then using rounding) You can use a statically created array or build a class that generates the required array from a set of input values. -- 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 From alan.gauld at yahoo.co.uk Wed Jan 1 20:17:01 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 2 Jan 2020 01:17:01 +0000 Subject: [Tutor] Beginning String Problems In-Reply-To: References: Message-ID: <1527b498-92dc-90fb-ddfa-b2a7f27f6463@yahoo.co.uk> On 01/01/2020 23:22, William Dickey wrote: > first_name = str(input("Please enter your first name: ")) > I enter william and I get this error: > > Traceback (most recent call): > File "C:\xxx\xxx\xxx\xxx", line 3, in > first_name = str(input("Please enter your first name: ")) > File "", line 1, in > NameError: name 'william' is not defined > > I am using Windows 10 64 bit, Atom text editor, Python 3.4. How are you running the code? The error you get is what I would expect if you were using Python v2. If you are just typing python script.py Then you may be picking up a default v2 install of Python. Try changing the command to python3 script.py There is also a tool called py.exe, which is supposed to help with this kind of thing on Windows, but I've never used it so don't know how it works... -- 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 From robertvstepp at gmail.com Wed Jan 1 21:38:20 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Wed, 1 Jan 2020 20:38:20 -0600 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: Message-ID: On Wed, Jan 1, 2020 at 12:30 PM boB Stepp wrote: > A simple throwaway script> > ============================================================================ > #!/usr/bin/env python3 > """Calculate the number of pages per day needed to read a book by a > given date.""" > > from datetime import date > > COUNT_TODAY = 1 > > num_pages_in_book = int(input("How many pages are there in your book? ")) > num_pages_read = int(input("How many pages have you read? ")) > today_date = date.today() > goal_date = date.fromisoformat( > input("What is your date to finish the book (yyyy-mm-dd)? ") > ) > > days_to_goal = (goal_date - today_date).days > pages_per_day = (num_pages_in_book - num_pages_read) / (days_to_goal + > COUNT_TODAY) > print( > "\nYou must read", > pages_per_day, > "pages each day (starting today) to reach your goal.", > ) > ============================================================================ > > As this script is I believe it to be easy to read and understand by an > outside reader. For my original purposes it quickly gave me what I > wanted out of it. But if I wanted to "give it away" to the general > public there are some things that bother me: > > 1) There is no error checking. If someone deliberately or mistakenly > enters incorrect input the program will terminate with a ValueError > exception. > 2) There are no checks for logic errors. For instance, a person > could enter a goal date that is prior to today or enter a date in the > future that falls well beyond any human's possible lifespan (with > current technology). > 3) There is no formatting of output. It is possible to generate > float output with many decimal places, something most users would not > want to see. And does it make any sense to have non-integral numbers > of pages anyway? > 4) There are duplicated patterns of input in the code as is. Surely > that duplication could be removed? > 5) A minor, but bothersome quibble: If the reader need only read one > page per day, the program would display an annoying "... 1.0 pages > ...", which is grammatically incorrect. > 6) There are no tests, which makes it more difficult to grow/maintain > the program in the future. > 7) The user must restart the program each time he/she wishes to try > out different goal dates. > 8) [Optional] There are no type annotations. OK, I have made some progress, but this has been for me a more difficult exercise than I thought it would be. I feel that in my efforts to be DRY that I am coding as if I were wet behind my ears! Anyway, here is what I currently have: Not all goals accomplished -- yet.> ============================================================================ #!/usr/bin/env python3 """Calculate the number of pages per day needed to read a book by a given date.""" from datetime import date def get_input(str_converter, msg): """Prompt user with a message and return user input with the correct data type.""" while True: try: return str_converter(input(msg)) except ValueError: if str_converter == int: print("\nPlease enter a positive integer!") elif str_converter == date.fromisoformat: print("\nPlease enter a valid date in the following format yyyy-mm-dd!") def get_input_params(): """Collect all needed input parameters.""" str_converters = [int, int, date.fromisoformat] input_msgs = [ "How many pages are there in your book? ", "How many pages have you read? ", "What is your date to finish the book (yyyy-mm-dd)? ", ] num_pages_in_book, num_pages_read, goal_date = map( get_input, str_converters, input_msgs ) days_to_goal = (goal_date - date.today()).days return num_pages_in_book, num_pages_read, days_to_goal def calc_pages_per_day(num_pages_in_book, num_pages_read, days_to_goal): """Return number of pages to read each day to attain goal.""" COUNT_TODAY = 1 pages_per_day = (num_pages_in_book - num_pages_read) / (days_to_goal + COUNT_TODAY) return pages_per_day def main(): """Run program and display results.""" input_params = get_input_params() print( "\nYou must read", calc_pages_per_day(*input_params), "pages each day (starting today) to reach your goal.", ) if __name__ == "__main__": main() ============================================================================ I think I have removed all "perceived" duplication, but I had to struggle to get to this point. I have never used the map() function before, but it was the only way I was able to avoid writing multiple calls to get_input() as separate lines. ValueErrors are now handled, but I don't have checks in place yet for input that does not raise an unhandled exception, but would cause erroneous results, such as negative numbers of days, in the past dates, etc. I *think* the code still reads reasonably well, but is not as straightforward to understand as the original simple script. I have not written tests yet (Bad boB!). Nor have I addressed better display formatting. I still think that you guys would do something remarkably easier, so I am awaiting your comments with bated breath. HAPPY NEW YEAR TO YOU AND YOURS!!! -- boB From robertvstepp at gmail.com Wed Jan 1 22:56:31 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Wed, 1 Jan 2020 21:56:31 -0600 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Message-ID: On Wed, Jan 1, 2020 at 6:19 PM DL Neil via Tutor wrote: > Why did I cut-out the code and the suggested improvements? Because the > reason why many of us are reluctant to "maintain" or "improve" > someone-else's code is an anxiety that if we fail to understand every > nuance of thinking that went into the original author's work, we will > introduce errors! Methinks you giveth me too much credit for potential subtlety! If there are any "nuances" in the simple script I posted I am totally oblivious to them! [...] > Accordingly, should we pay any regard to 'the rest', before completing: > > > 6) There are no tests, which makes it more difficult to grow/maintain > > the program in the future. Aha! You are holding me to a proper standard of coding expectations. Alas, I am a mere hobbyist programmer with overly high aspirations who routinely falls short of the lofty heights for which I strive (SPLAT!). > I've just re-read an interesting observation of the collection of tests > one might build-up, as a system grows - a challenging question to ponder: > > - if the code and all other documentation of a program/system were > 'lost', could you?should you, be able to reconstruct the whole from the > test-set alone? That is a very interesting question! My initial reaction is no, but the better the test suite the better the precise intent of the unseen source code should be evident. > I am currently working on code which aims to re-use other classes. There > are three different data-structures which may be used as input, from > basic strings, through PSL class, to a locally-coded data-object; the > consideration then becomes: were all the inputs provided or do > (different for the various sub-classes) defaults apply; then there are > all the different real-world combinations which the data represents, eg > does the transaction currently 'exist' or is it hypothetical at this > point; and they're only the first three considerations which generate > permutations or combinations! > > During the task, I have found that the new code has failed because I > didn't adequately code around my own tests (TDD's intent), but also two > further conditions: > > 1 my assumptions about how the underlying classes work were > incomplete/faulty - and thus my 'new code' was inadequate, and > > 2 that when certain things didn't work, it was a fault in the provided > classes - apparently a combination of factors they did not anticipate, > and in at least one case, had no test to reveal (but my tests of my code > (unintentionally) did). > > > Whilst the latter was frustrating (which is one of the points I'm > making), the advantage of being given not just the code (classes, etc) > for re-use, but the tests to verify that code (and consequently the > superstructure I was building 'above' it) have been an enormous > advantage - had I needed to 'read the code' with no further help, > perhaps I would have thrown it out and 're-invented the wheel'! > > (which decision, strangely enough, has justified many an (expensive!) > system-rewrite project!) Very interesting and thought provoking! -- boB From martin at linux-ip.net Wed Jan 1 22:25:49 2020 From: martin at linux-ip.net (Martin A. Brown) Date: Wed, 1 Jan 2020 19:25:49 -0800 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: Hello there, >First of all let me wish a great 2020 for all of you kind souls and seekers >of knowledge :) > >I am trying to setup a varying time.sleep value (in house) depending on a >priorly determined threshold. > >If threshold <= .9 AND > .8 then sleep should be 6 hours >if threshold <= 0.8 AND > .4 then sleep should be 4 hours >if threshold <= .4 AND > .1 then sleep should be 3 hours >if threshold <= .1 then sleep should be set to 1 hour > >Instead of a complex/lengthy series of (nested?) if/ands based on the >above, I was wondering if I could declare a dictionary such as: > >sleep_thresholds_hours = {.9:6, .8:4, .4:3, .1:1} > >And then use some sort of single statement to set the sleep_time to the the >proper value. I saw the thread here and thought I'd spend a few minutes writing a few functions that I think you might find helpful. I like the suggestion Mats had about multiplying (or using some other mathematical function) based on the input value. But, that may not work in your case. I don't know how to apply the sparse array solution, suggested by Alan. Here is an example or two, of possible utility. If you are simply testing ranges, you can always cover the search space in order (I start from the "bottom"): def series_of_ifs(val): assert val == abs(val) # -- ineffably positive if val <= 0.0: return None if val <= 0.1: return 1 if val <= 0.4: return 3 if val <= 0.8: return 4 if val <= 0.9: return 6 return None def data_driven_dict(val): assert val == abs(val) # -- ineffably positive thresholds = [(0, None), (0.1, 1), (0.4, 3), (0.8, 4), (0.9, 6) ] for threshold, result in thresholds: if val <= threshold: return result return None Attached is a small script with these functions and a little testing code around them, that should allow you to test other ways of writing what you want. If I knew I would want to monkey with the thresholds later, I'd use the data_driven_dict() solution. Oh, and I didn't know what you wanted to do with values above 0.9, so I just chose to "return None" for those. Also, given your sample data, I sort of assumed that your data were in the range of [0, 1), but if they are not, you may need to handle negative, as well. I chose to assert, effectively terminating the program if the value is negative. >Not even quite sure that using floats (albeit single decimal) as dictionary >keys will be ok. Yep! This will be quite OK. This may be a bit odd, but from a Python interpreter, I used something as a key, and then, for no specific reason, I assigned the value to the type of that value. Which had the amusing side effect that after I was done, I could write a little loop to assert that the key (retrieved in a loop) was actually of the type indicated. >>> d = dict() >>> d[17] = int >>> d[0.2] = float >>> d['hedgehog'] = str >>> d[('a', 'b', 7)] = tuple >>> d {17: , ('a', 'b', 7): , 0.2: , 'hedgehog': } >>> for k, v in d.items(): ... assert type(k) is v ... >>> Best of luck, -Martin -- Martin A. Brown http://linux-ip.net/ From PyTutor at DancesWithMice.info Wed Jan 1 23:45:06 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Thu, 2 Jan 2020 17:45:06 +1300 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: <67a7e2a2-23fe-17f7-5ca7-f389f079a5f8@DancesWithMice.info> On 2/01/20 5:20 AM, Robert Alexander wrote: > I am trying to setup a varying time.sleep value (in house) depending on a > priorly determined threshold. > > If threshold <= .9 AND > .8 then sleep should be 6 hours > if threshold <= 0.8 AND > .4 then sleep should be 4 hours > if threshold <= .4 AND > .1 then sleep should be 3 hours > if threshold <= .1 then sleep should be set to 1 hour > > Instead of a complex/lengthy series of (nested?) if/ands based on the > above, I was wondering if I could declare a dictionary such as: > > sleep_thresholds_hours = {.9:6, .8:4, .4:3, .1:1} > > And then use some sort of single statement to set the sleep_time to the the > proper value. > > Not even quite sure that using floats (albeit single decimal) as dictionary > keys will be ok. > > Getting old and in 2020 even older and my brain fogged by too much food :) A worked-solution... Perhaps the seasonal influences explain it, but my (aged and decrepit) mind prefers to construct the problem in the reverse of how it is stated here... Familiar with Cartesian coordinates and similar, to me the x-axis/time-line starts at zero and goes 'up' to the right - and the y-axis starts at zero and goes 'up', um, as the y-value goes 'up'. That said, increasing/rising ranges seem easier (for me) to visualise: <= .1 1 .4 3 .8 4 .9 6 (per 'excuses', below, am ignoring values of .9 or above, and similarly, numbers which may/not be 'below' the illustrated range) Notice that (in mathematical terminology) we now have two (ordered) vectors (or 1-D matrices - or a single 2-D matrix, if you prefer. Yeah/nah, leave that until the end...) Let's call the left column-vector "sleep_threshold", and the right, "sleep_hours". In Python: sleep_threshold = [.1, .4, .8, .9] sleep_hours = [ 1, 3, 4, 6 ] Proving that we know what we're doing (maybe):- for ptr in range( 0, len( sleep_threshold ) ): print( f"{ ptr } { sleep_threshold[ ptr ] } { sleep_hours[ ptr ] }" ) 0 0.1 1 1 0.4 3 2 0.8 4 3 0.9 6 (apologies, email word-wrapping may produce a less-than pleasing graphological presentation. Similarly, when coding tests/"proofs", I've saved vertical-space by typing the entire for-loop on a single line - which many of my colleagues will (quite rightly) decry...) Right, time to get to work! We could (per an earlier suggestion) try iterating through the "sleep_threshold" list, in order to select the corresponding "sleep_hours" element: def sleepy( value ): for ptr in range( 0, len( sleep_threshold ) ): if value < sleep_threshold[ ptr ]: print( f"{ value } results in sleep for { sleep_hours[ ptr ] }" ) break Yes, a function *should* be named using a verb which describes what it does. I don't really know what you want it to do! (what a feeble excuse!) Also, in this case, I'm only bothering to print the amount of sleep-time by way of illustration. Presumably, you will want such a function to return the sleep_hours value, or to execute the sleep right there-and-then...] What more criticism? Yes! Why did I put this code inside a function? 1. Presumption on my part (of your needs) 2. Programming style - each separate action is to be carried-out by a single function/method ("separation of concerns") 3. For ease of testing! Speaking of testing: how would we know if our magnificent-creation is working, without testing the code? Normally, I would use PyTest, but as this is being presented as a running-commentary, let's code some tests, in-line:- test_data = [ .05, .1, .2, .6, .85 ] for test_value in test_data: print( test_value ) 0.05 0.1 0.2 0.6 0.85 Please note that I've only used one 'border' value (0.1) and ignored any concept of "outliers". (I'm going to claim simplicity. You can say 'laziness'...). Now we're ready to experiment:- for test_value in test_data: sleepy( test_value ) 0.05 results in sleep for 1 0.1 results in sleep for 3 0.2 results in sleep for 3 0.6 results in sleep for 4 0.85 results in sleep for 6 What brilliance! It's almost as good-looking as am I! Did you notice the 'oops!' in the test-data output? (*and* in the code?) A very common error when coding such a 'ladder', is the comparison. How do we regard values 'on the border' - are they 'in' or 'out' (in mathematics: "inclusive" or "exclusive" of the range)? In this case, your outline clearly shows them as "inclusive" and therefore the second test (0.1 to sleep for 3) *failed*. Let's correct 'my' error (and evaluate the results, again), before we go any further... def sleepy( value ): for ptr in range( 0, len( sleep_threshold ) ): if value <= sleep_threshold[ ptr ]: print( f"{ value } results in sleep for { sleep_hours[ ptr ] }" ) break for test_value in test_data: sleepy( test_value ) 0.05 results in sleep for 1 0.1 results in sleep for 1 0.2 results in sleep for 3 0.6 results in sleep for 4 0.85 results in sleep for 6 That's better! Now are you happy? Good, but I'm not! (some say I'm never happy...) A second, similar, and also startlingly-common, error, in this coding idiom is the "out by one error". In this case, I've not fallen into such a 'trap', but essentially the 'danger' is in looping (in this case, over the test data) one time too few, or one time too many, ie "out by one"! Is this correct: for ptr in range( 0, len( sleep_threshold ) )? I describe the above "style" as coding in a Java or C (or ...) idiom. A 'purist' would say that it has *no* place in Python. Python's language-design gives us a significantly more-powerful for statement, which can be differentiated from 'the others' by terming it a "for-each" statement, ie for-each element in a collection, do... Thus, we don't need to know how large the collection happens to be (len()), and the 'out by one' error goes away - 'out by one' thrown-out? - consigned to languish in the history of cyber-space... OK, so with for-each in mind, we could try: for threshold in sleep_threshold: etc but what to do when we want to access the corresponding element in sleep_hours, ie sleep_hours[ ptr ]??? Back to the drawing-board? Back to using a "pointer"? No! Python has another trick up its sleeve (another 'battery' - when they say that Python has "batteries included"). Somehow, we need to link the two lists/column-vectors (sleep_threshold and sleep_hours). We can do this with zip() - we zip the two lists into a single list, such that each element of the new list is comprised of two values. (remember how other posts talked of using a Python dictionary? A dictionary's elements have two components, a "key", and a "value". Similar motivation: finding a method of keeping the two 'together') NB 'linking' lists using zip() is *not* the same as a ComSc concept or algorithm called "linked lists"! Onwards:- sleep_thresholds_hours = zip( sleep_threshold, sleep_hours ) print( sleep_thresholds_hours ) Oh dear, it's not going to show us what we want to see. Let's use a bit more power - more POWER!!! for row in sleep_thresholds_hours: print( row ) (0.1, 1) (0.4, 3) (0.8, 4) (0.9, 6) Will you look at that, I'm back to mathematics and raving about matrices/matrixes! Someone (please) stop him... Here's revised code, written to be "pythonic", ie using a Python idiom, and ridding us of any distasteful Java-influence (or whatever) - "we" are so much better than "they"! Engaging the powers of zip() and for-each:- def sleepy_zip( value ): for sleep_threshold, sleep_hours in sleep_thresholds_hours: if value <= sleep_threshold: print( f"{ threshold } results in sleep for { sleep_hours }" ) break for test_value in test_data: sleepy_zip( test_value ) Incidentally, if it will help comprehension, the twin-components of each sleep_thresholds_hours element are a tuple - and therefore you might prefer the more *explicit* syntax:- for ( sleep_threshold, sleep_hours ) in sleep_thresholds_hours: The 'Zen of Python' does recommend preferring the explicit over implications! Yahoo! Finally, it's time to go home... No! Not so fast. Where's the output? Um, there was none! Do you 'see' any problem with this? What happened is that zip() produces an iterator object (https://docs.python.org/3.5/library/functions.html#zip), and iterators can only be "consumed" once. Think about it, if you zip 'up' a piece of clothing (ie use the zip()), and then "consume" the zip in the for-loop (unzipping it), you can't thereafter "consume" it again - the zip has been unzipped all the way 'down' and so can't be unzipped any further... (As 'home-spun wisdom' goes, that's not too bad - or is it "home-zipped wisdom"?) So, when I demonstrated the results of the zip() by printing-out the contents for you, It was "consumed". Thus, when we came to run sleepy_zip(), there was no 'zip' left to 'unzip', and to borrow from another piece of 'wisdom': no data in, no data out. My fault, AGAIN! Sure enough:- print( "v"*8 ) for row in sleep_thresholds_hours: print( row ) print( "^"*8 ) vvvvvvvv ^^^^^^^^ (the 'results' should appear between the two lines of 'arrows') Let's re-start, this time with feeling (yes, I can feel that zip going 'up'!). This time, let's put the zip() where it is going to be consumed, ie where there's no chance that someone, or something else, will consume it before we execute the function/do the business:- def sleepy_zip( value ): for threshold, hours in zip( sleep_threshold, sleep_hours ): if value <= threshold: print( f"{ value } results in sleep for { hours }" ) break for test_value in test_data: sleepy_zip( test_value ) 0.05 results in sleep for 1 0.1 results in sleep for 1 0.2 results in sleep for 3 0.6 results in sleep for 4 0.85 results in sleep for 6 BTW have you spotted the virtue of using "break" - or what might happen if we didn't? Mind you, I do feel a little short of sleep - so I'm not complaining! Hope this helps. If I have misunderstood any of the 'real' criteria, hopefully you will be able to adapt-to-suit. As mentioned, normally I use PyTest to help prove my code. Thus the "for test_value etc" lines would be located elsewhere (rather than in-line). Also, were I to start writing this all over again, but still using in-line testing, I'd zip() test_value with the expected results - if we go to the trouble of assembling a test-data-set, then let's ensure we also know/remember the (correct) answers! In fact, you might even ask the computer to check its own results, then:- assert returned_result == expected_result Python will come to an abrupt halt if things are not tickety-boo! Similarly, you could improve the output by formatting the f-strings within our print()-s. More power to you! However, in time-honored fashion, I shall leave such 'extras' to you, "as an exercise" (and/or when you're ready for such more-advanced coding techniques)... -- Regards =dn From gogonegro at gmail.com Thu Jan 2 04:19:09 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Thu, 2 Jan 2020 10:19:09 +0100 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: <67a7e2a2-23fe-17f7-5ca7-f389f079a5f8@DancesWithMice.info> References: <67a7e2a2-23fe-17f7-5ca7-f389f079a5f8@DancesWithMice.info> Message-ID: Thank you all for your great replies which go a long way into making me better understand python and my brain :) While you prepared your better replies I actually had wrapped my beginner head around the simple nested if as follows: def set_frequency(down, up, loss): """Depending on the speed degradation change email frequency. The returned frequency is expressed in hours. """ down_degradation = down / TD if down_degradation <= .1: return 1 elif (down_degradation > .1) and (down_degradation <= .4): return 3 elif (down_degradation > .4) and (down_degradation <= .8): return 4 elif (down_degradation > .8) and (down_degradation <= .9): return 6 else: return DEFAULT_TEST_FREQUENCY As you can surmise DEFAULT_TEST_FREQUENCY is an external constant (set to 24) and currently uses only the passed download value. As you suggested I had tested this function with a small main calling it with random.random() value that provides a float between 0 and 1 and printing the value it returned. I am satisfies by the behaviour. I will spend the next month poring through the wisdom and arcane :) tricks in your emails. Would it be of any value for me to share the whole program (on GitHub) on the list when it?s soundly working to see if you can suggest some overall betterment of the style or flow? Again very grateful for your inputs. Ciao from Rome, Italy PS The program monitors your ADSL speeds and when values drop under thresholds sends a warning email PPS The critical feature I still have to implement is to be able to reply in a safe way to the email and stop the monitor in case I am traveling around the planet and the ADSL goes bonkers and programs spits out an email per hour ;) On 2 January 2020 at 05:45:38, David L Neil via Tutor (tutor at python.org) wrote: On 2/01/20 5:20 AM, Robert Alexander wrote: > I am trying to setup a varying time.sleep value (in house) depending on a > priorly determined threshold. > > If threshold <= .9 AND > .8 then sleep should be 6 hours > if threshold <= 0.8 AND > .4 then sleep should be 4 hours > if threshold <= .4 AND > .1 then sleep should be 3 hours > if threshold <= .1 then sleep should be set to 1 hour > > Instead of a complex/lengthy series of (nested?) if/ands based on the > above, I was wondering if I could declare a dictionary such as: > > sleep_thresholds_hours = {.9:6, .8:4, .4:3, .1:1} > > And then use some sort of single statement to set the sleep_time to the the > proper value. > > Not even quite sure that using floats (albeit single decimal) as dictionary > keys will be ok. > > Getting old and in 2020 even older and my brain fogged by too much food :) A worked-solution... Perhaps the seasonal influences explain it, but my (aged and decrepit) mind prefers to construct the problem in the reverse of how it is stated here... Familiar with Cartesian coordinates and similar, to me the x-axis/time-line starts at zero and goes 'up' to the right - and the y-axis starts at zero and goes 'up', um, as the y-value goes 'up'. That said, increasing/rising ranges seem easier (for me) to visualise: <= .1 1 .4 3 .8 4 .9 6 (per 'excuses', below, am ignoring values of .9 or above, and similarly, numbers which may/not be 'below' the illustrated range) Notice that (in mathematical terminology) we now have two (ordered) vectors (or 1-D matrices - or a single 2-D matrix, if you prefer. Yeah/nah, leave that until the end...) Let's call the left column-vector "sleep_threshold", and the right, "sleep_hours". In Python: sleep_threshold = [.1, .4, .8, .9] sleep_hours = [ 1, 3, 4, 6 ] Proving that we know what we're doing (maybe):- for ptr in range( 0, len( sleep_threshold ) ): print( f"{ ptr } { sleep_threshold[ ptr ] } { sleep_hours[ ptr ] }" ) 0 0.1 1 1 0.4 3 2 0.8 4 3 0.9 6 (apologies, email word-wrapping may produce a less-than pleasing graphological presentation. Similarly, when coding tests/"proofs", I've saved vertical-space by typing the entire for-loop on a single line - which many of my colleagues will (quite rightly) decry...) Right, time to get to work! We could (per an earlier suggestion) try iterating through the "sleep_threshold" list, in order to select the corresponding "sleep_hours" element: def sleepy( value ): for ptr in range( 0, len( sleep_threshold ) ): if value < sleep_threshold[ ptr ]: print( f"{ value } results in sleep for { sleep_hours[ ptr ] }" ) break Yes, a function *should* be named using a verb which describes what it does. I don't really know what you want it to do! (what a feeble excuse!) Also, in this case, I'm only bothering to print the amount of sleep-time by way of illustration. Presumably, you will want such a function to return the sleep_hours value, or to execute the sleep right there-and-then...] What more criticism? Yes! Why did I put this code inside a function? 1. Presumption on my part (of your needs) 2. Programming style - each separate action is to be carried-out by a single function/method ("separation of concerns") 3. For ease of testing! Speaking of testing: how would we know if our magnificent-creation is working, without testing the code? Normally, I would use PyTest, but as this is being presented as a running-commentary, let's code some tests, in-line:- test_data = [ .05, .1, .2, .6, .85 ] for test_value in test_data: print( test_value ) 0.05 0.1 0.2 0.6 0.85 Please note that I've only used one 'border' value (0.1) and ignored any concept of "outliers". (I'm going to claim simplicity. You can say 'laziness'...). Now we're ready to experiment:- for test_value in test_data: sleepy( test_value ) 0.05 results in sleep for 1 0.1 results in sleep for 3 0.2 results in sleep for 3 0.6 results in sleep for 4 0.85 results in sleep for 6 What brilliance! It's almost as good-looking as am I! Did you notice the 'oops!' in the test-data output? (*and* in the code?) A very common error when coding such a 'ladder', is the comparison. How do we regard values 'on the border' - are they 'in' or 'out' (in mathematics: "inclusive" or "exclusive" of the range)? In this case, your outline clearly shows them as "inclusive" and therefore the second test (0.1 to sleep for 3) *failed*. Let's correct 'my' error (and evaluate the results, again), before we go any further... def sleepy( value ): for ptr in range( 0, len( sleep_threshold ) ): if value <= sleep_threshold[ ptr ]: print( f"{ value } results in sleep for { sleep_hours[ ptr ] }" ) break for test_value in test_data: sleepy( test_value ) 0.05 results in sleep for 1 0.1 results in sleep for 1 0.2 results in sleep for 3 0.6 results in sleep for 4 0.85 results in sleep for 6 That's better! Now are you happy? Good, but I'm not! (some say I'm never happy...) A second, similar, and also startlingly-common, error, in this coding idiom is the "out by one error". In this case, I've not fallen into such a 'trap', but essentially the 'danger' is in looping (in this case, over the test data) one time too few, or one time too many, ie "out by one"! Is this correct: for ptr in range( 0, len( sleep_threshold ) )? I describe the above "style" as coding in a Java or C (or ...) idiom. A 'purist' would say that it has *no* place in Python. Python's language-design gives us a significantly more-powerful for statement, which can be differentiated from 'the others' by terming it a "for-each" statement, ie for-each element in a collection, do... Thus, we don't need to know how large the collection happens to be (len()), and the 'out by one' error goes away - 'out by one' thrown-out? - consigned to languish in the history of cyber-space... OK, so with for-each in mind, we could try: for threshold in sleep_threshold: etc but what to do when we want to access the corresponding element in sleep_hours, ie sleep_hours[ ptr ]??? Back to the drawing-board? Back to using a "pointer"? No! Python has another trick up its sleeve (another 'battery' - when they say that Python has "batteries included"). Somehow, we need to link the two lists/column-vectors (sleep_threshold and sleep_hours). We can do this with zip() - we zip the two lists into a single list, such that each element of the new list is comprised of two values. (remember how other posts talked of using a Python dictionary? A dictionary's elements have two components, a "key", and a "value". Similar motivation: finding a method of keeping the two 'together') NB 'linking' lists using zip() is *not* the same as a ComSc concept or algorithm called "linked lists"! Onwards:- sleep_thresholds_hours = zip( sleep_threshold, sleep_hours ) print( sleep_thresholds_hours ) Oh dear, it's not going to show us what we want to see. Let's use a bit more power - more POWER!!! for row in sleep_thresholds_hours: print( row ) (0.1, 1) (0.4, 3) (0.8, 4) (0.9, 6) Will you look at that, I'm back to mathematics and raving about matrices/matrixes! Someone (please) stop him... Here's revised code, written to be "pythonic", ie using a Python idiom, and ridding us of any distasteful Java-influence (or whatever) - "we" are so much better than "they"! Engaging the powers of zip() and for-each:- def sleepy_zip( value ): for sleep_threshold, sleep_hours in sleep_thresholds_hours: if value <= sleep_threshold: print( f"{ threshold } results in sleep for { sleep_hours }" ) break for test_value in test_data: sleepy_zip( test_value ) Incidentally, if it will help comprehension, the twin-components of each sleep_thresholds_hours element are a tuple - and therefore you might prefer the more *explicit* syntax:- for ( sleep_threshold, sleep_hours ) in sleep_thresholds_hours: The 'Zen of Python' does recommend preferring the explicit over implications! Yahoo! Finally, it's time to go home... No! Not so fast. Where's the output? Um, there was none! Do you 'see' any problem with this? What happened is that zip() produces an iterator object (https://docs.python.org/3.5/library/functions.html#zip), and iterators can only be "consumed" once. Think about it, if you zip 'up' a piece of clothing (ie use the zip()), and then "consume" the zip in the for-loop (unzipping it), you can't thereafter "consume" it again - the zip has been unzipped all the way 'down' and so can't be unzipped any further... (As 'home-spun wisdom' goes, that's not too bad - or is it "home-zipped wisdom"?) So, when I demonstrated the results of the zip() by printing-out the contents for you, It was "consumed". Thus, when we came to run sleepy_zip(), there was no 'zip' left to 'unzip', and to borrow from another piece of 'wisdom': no data in, no data out. My fault, AGAIN! Sure enough:- print( "v"*8 ) for row in sleep_thresholds_hours: print( row ) print( "^"*8 ) vvvvvvvv ^^^^^^^^ (the 'results' should appear between the two lines of 'arrows') Let's re-start, this time with feeling (yes, I can feel that zip going 'up'!). This time, let's put the zip() where it is going to be consumed, ie where there's no chance that someone, or something else, will consume it before we execute the function/do the business:- def sleepy_zip( value ): for threshold, hours in zip( sleep_threshold, sleep_hours ): if value <= threshold: print( f"{ value } results in sleep for { hours }" ) break for test_value in test_data: sleepy_zip( test_value ) 0.05 results in sleep for 1 0.1 results in sleep for 1 0.2 results in sleep for 3 0.6 results in sleep for 4 0.85 results in sleep for 6 BTW have you spotted the virtue of using "break" - or what might happen if we didn't? Mind you, I do feel a little short of sleep - so I'm not complaining! Hope this helps. If I have misunderstood any of the 'real' criteria, hopefully you will be able to adapt-to-suit. As mentioned, normally I use PyTest to help prove my code. Thus the "for test_value etc" lines would be located elsewhere (rather than in-line). Also, were I to start writing this all over again, but still using in-line testing, I'd zip() test_value with the expected results - if we go to the trouble of assembling a test-data-set, then let's ensure we also know/remember the (correct) answers! In fact, you might even ask the computer to check its own results, then:- assert returned_result == expected_result Python will come to an abrupt halt if things are not tickety-boo! Similarly, you could improve the output by formatting the f-strings within our print()-s. More power to you! However, in time-honored fashion, I shall leave such 'extras' to you, "as an exercise" (and/or when you're ready for such more-advanced coding techniques)... -- Regards =dn _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From alan.gauld at yahoo.co.uk Thu Jan 2 05:16:10 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 2 Jan 2020 10:16:10 +0000 Subject: [Tutor] Setting thresholds in a compact way In-Reply-To: References: Message-ID: On 02/01/2020 03:25, Martin A. Brown wrote: > I don't know how to apply the sparse array solution, suggested by Alan. Sparse arrays are a bit of a faff and only worthwhile in the specific scenario where there are a lot of choices, so an if/elif ladder would be cumbersome - I'm thinking at least double digit lengths...20 or 30 say. They are also effective where you have a broad range of possible test values but only a relatively few sub-ranges which require action. It was purely a response to Bob's question about how to handle the long ladder case. Not a general recommendation. Certainly not for this particular problem. -- 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 From gogonegro at gmail.com Thu Jan 2 13:12:36 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Thu, 2 Jan 2020 19:12:36 +0100 Subject: [Tutor] Please correct my error handling Message-ID: I am having an hard time to properly address some errors in external programs launched by my python code. Here follows a sample, in which you can see 3 similar ?stanzas?, first is to check behaviour of a well formed external command and is all as expected, the second shows the same command with an illegal option and again all behaves as expected. The problem I have is to catch the ?non existing command? as per the third example. Thank you very much. Robert ============== test.py import subprocess # 1) try a legit (*nix) command process = subprocess.Popen( ['/bin/ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = process.communicate() if not stderr: print('--No errors--\n', stdout.decode()) else: print('--Error found--\n', stderr.decode()) # 2) repeat with an illegal option process = subprocess.Popen( ['/bin/ls', '-I'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = process.communicate() if not stderr: print('--No errors--\n', stdout.decode()) else: print('--Error found--\n', stderr.decode()) # 3) repeat with an illegal ;) command process = subprocess.Popen( ['/bin/lsd', '-I'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = process.communicate() if not stderr: print('--No errors--\n', stdout.decode()) else: print('--Error found--\n', stderr.decode()) From mats at wichmann.us Thu Jan 2 13:29:58 2020 From: mats at wichmann.us (Mats Wichmann) Date: Thu, 2 Jan 2020 11:29:58 -0700 Subject: [Tutor] Please correct my error handling In-Reply-To: References: Message-ID: <333804d7-8544-5454-9df6-43998ef29c25@wichmann.us> On 1/2/20 11:12 AM, Robert Alexander wrote: > I am having an hard time to properly address some errors in external > programs launched by my python code. Popen fails directly if it can't open a process for the requested command, so you have to check a different way for that case. Try this: > # 3) repeat with an illegal ;) command try: process = subprocess.Popen( ['/bin/lsd', '-E'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) except FileNotFoundError as e: print("--Command not found--\n", e) sys.exit(2) # or you could just "raise": stdout, stderr = process.communicate() if not stderr: print('--No errors--\n', stdout.decode()) else: print('--Error found--\n', stderr.decode()) From david at graniteweb.com Thu Jan 2 13:39:18 2020 From: david at graniteweb.com (David Rock) Date: Thu, 2 Jan 2020 12:39:18 -0600 Subject: [Tutor] Please correct my error handling In-Reply-To: References: Message-ID: <20200102183918.fpt53zvu7g72oxxy@apple.graniteweb.com> * Robert Alexander [2020-01-02 19:12]: > I am having an hard time to properly address some errors in external > programs launched by my python code. > # 3) repeat with an illegal ;) command > process = subprocess.Popen( > ['/bin/lsd', '-I'], > stdout=subprocess.PIPE, > stderr=subprocess.PIPE, > ) > stdout, stderr = process.communicate() > if not stderr: > print('--No errors--\n', stdout.decode()) > else: > print('--Error found--\n', stderr.decode()) The problem is process.communicate() is thowing an exception. That's not the same thing as getting stdout, stderr Traceback (most recent call last): File "test.py", line 6, in stderr=subprocess.PIPE, File "/usr/lib/python3.5/subprocess.py", line 676, in __init__ restore_signals, start_new_session) File "/usr/lib/python3.5/subprocess.py", line 1282, in _execute_child raise child_exception_type(errno_num, err_msg) FileNotFoundError: [Errno 2] No such file or directory: '/bin/lsd' The FileNotFoundError is an exception from process.communicate() failing outright. You need to catch that exception and do something with it. For example, put the call to process.communicate() in a try block. try: stdout, stderr = process.communicate() except FileNotFoundError: print( "command not found" ) else: if not stderr: print('--No errors--\n', stdout.decode()) else: print('--Error found--\n', stderr.decode()) -- David Rock david at graniteweb.com From david at graniteweb.com Thu Jan 2 13:43:06 2020 From: david at graniteweb.com (David Rock) Date: Thu, 2 Jan 2020 12:43:06 -0600 Subject: [Tutor] Please correct my error handling In-Reply-To: <20200102183918.fpt53zvu7g72oxxy@apple.graniteweb.com> References: <20200102183918.fpt53zvu7g72oxxy@apple.graniteweb.com> Message-ID: <20200102184306.hk5a2jem52hxmx43@apple.graniteweb.com> * David Rock [2020-01-02 12:39]: > > The problem is process.communicate() is thowing an exception. That's > not the same thing as getting stdout, stderr > > Traceback (most recent call last): > File "test.py", line 6, in > stderr=subprocess.PIPE, > File "/usr/lib/python3.5/subprocess.py", line 676, in __init__ > restore_signals, start_new_session) > File "/usr/lib/python3.5/subprocess.py", line 1282, in _execute_child > raise child_exception_type(errno_num, err_msg) > FileNotFoundError: [Errno 2] No such file or directory: '/bin/lsd' > Oops, I should look closer at my own tracebacks. As mentioned, this is an exception in popen, not subprocess.communicate(). It's not even getting that far. -- David Rock david at graniteweb.com From mats at wichmann.us Thu Jan 2 13:44:56 2020 From: mats at wichmann.us (Mats Wichmann) Date: Thu, 2 Jan 2020 11:44:56 -0700 Subject: [Tutor] Please correct my error handling In-Reply-To: References: Message-ID: On 1/2/20 11:12 AM, Robert Alexander wrote: > I am having an hard time to properly address some errors in external > programs launched by my python code. > > Here follows a sample, in which you can see 3 similar ?stanzas?, first is > to check behaviour of a well formed external command and is all as > expected, the second shows the same command with an illegal option and > again all behaves as expected. The problem I have is to catch the ?non > existing command? as per the third example. also... there are "simpler" ways to do this vs Popen + communicate. I ran a "command fails" sequence in the interpreter: >>> x = subprocess.run(['/bin/ls', '-E'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) >>> type(x) >>> >>> help(subprocess.CompletedProcess) Help on class CompletedProcess in module subprocess: class CompletedProcess(builtins.object) | CompletedProcess(args, returncode, stdout=None, stderr=None) | | A process that has finished running. | | This is returned by run(). | | Attributes: | args: The list or str args passed to run(). | returncode: The exit code of the process, negative for signals. | stdout: The standard output (None if not captured). | stderr: The standard error (None if not captured). | | Methods defined here: | | __init__(self, args, returncode, stdout=None, stderr=None) | Initialize self. See help(type(self)) for accurate signature. | | __repr__(self) | Return repr(self). | | check_returncode(self) | Raise CalledProcessError if the exit code is non-zero. | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) >>> x.args ['/bin/ls', '-E'] >>> x.returncode 2 >>> x.stdout b'' >>> x.stderr b"/bin/ls: invalid option -- 'E'\nTry '/bin/ls --help' for more information.\n" >>> From basicbare at gmail.com Thu Jan 2 06:19:54 2020 From: basicbare at gmail.com (Alan Thwaits) Date: Thu, 2 Jan 2020 06:19:54 -0500 Subject: [Tutor] Beginning String Problems In-Reply-To: References: Message-ID: I copied, pasted, and ran your code, and it worked well. I'm running Anaconda3 and Spyder on a Windows 10 64-bit machine. I'm very much a beginning Python user, so don't know enough to suggest why it's not working for you. Just thought I'd let you know that your code seems alright. Cheers! Alan On Wed, Jan 1, 2020 at 7:15 PM William Dickey wrote: > I am learning Python via an online video "Beginning Python" by William > Fiset. I am at the end of chapter 2 and I am supposed to be inputting a > string that has an output of a persons full name in this order *First M. > Last*, but when I copied what he was doing it is not working. Here is what > I input: > > first_name = str(input("Please enter your first name: ")) > middle_name = str(input("Please enter your middle name: ")) > last_name = str(input("Please enter your last name: ")) > > first_name = first_name.capitalize() > middle_name = middle_name.capitalize() > last_name = last_name.capitalize() > > name_format = "{first} {middle:.1s} {last}" > print(name_format.format(first=first_name, middle=middle_name, > last=last_name)) > > and here is what I get when I open it in the command line > > I enter the file location and I get: > Please enter your first name: > > I enter william and I get this error: > > Traceback (most recent call): > File "C:\xxx\xxx\xxx\xxx", line 3, in > first_name = str(input("Please enter your first name: ")) > File "", line 1, in > NameError: name 'william' is not defined > > I am using Windows 10 64 bit, Atom text editor, Python 3.4. > > Thank you > William > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > From gogonegro at gmail.com Fri Jan 3 09:54:15 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Fri, 3 Jan 2020 15:54:15 +0100 Subject: [Tutor] Please correct my error handling In-Reply-To: References: Message-ID: Thank you guys. So as you pointed out I was catching the error at the wrong place. Following is the corrected code. I?ll also investigate the easier alternatives you proposed later on. Take care import subprocess # first try a legit (*nix) command process = subprocess.Popen( ["/bin/ls", "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = process.communicate() if not stderr: print("--No errors--\n", stdout.decode()) else: print("--Error found--\n", stderr.decode()) # now repeat with an illegal option process = subprocess.Popen( ["/bin/ls", "-I"], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = process.communicate() if not stderr: print("--No errors--\n", stdout.decode()) else: print("--Error found--\n", stderr.decode()) # now repeat with an inexisting command # and catch the Popen error this time try: process = subprocess.Popen( ["/bin/lsd", "-I"], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) except FileNotFoundError: print("Command not found. Did you install it?") else: stdout, stderr = process.communicate() if not stderr: print("--No errors--\n", stdout.decode()) else: print("--Error found--\n", stderr.decode()) From robertvstepp at gmail.com Sat Jan 4 02:10:43 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Sat, 4 Jan 2020 01:10:43 -0600 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Message-ID: I now believe that I have a finalized version2.py that I am satisfied with. I have not done the optional type annotations and am in progress adding tests. I may have some questions about the resulting tests once I complete them. But for my New Year's Python 3 exercise, I wish to throw out the below code for code review by anyone who wishes to assist boB on his ongoing learning journey. Let me know of *anything* that you think I should have done better or more professionally or in a more Pythonic style, etc. Another goal I had hoped to accomplish, but have apparently failed at, was to draw in fellow Python learners into the discussion. So many times we are asked on this list what can we do to learn how to program in Python, and the oft-recommended advice is to come up with a project and code it, asking for help as necessary. I thought showing a simple program and working on improving it might be helpful for other learners. Of course the end result to this point has grown from an original 23 LOC to 97 LOC (including blank lines), so have I really improved the original product? I await the scores from the judges! ============================================================================ #!/usr/bin/env python3 """Calculate the number of pages per day needed to read a book by a given date.""" from datetime import date def get_input(str_converter, msg): """Prompt user with a message and return user input with the correct data type.""" while True: try: return str_converter(input(msg)) except ValueError: if str_converter == int: print("\nPlease enter a whole number!") elif str_converter == date.fromisoformat: print("\nPlease enter a valid date in the following format yyyy-mm-dd!") def get_input_params(): """Collect all needed input parameters.""" while True: str_converters = [int, int, date.fromisoformat] input_msgs = [ "How many pages are there in your book? ", "How many pages have you read? ", "What is your date to finish the book (yyyy-mm-dd)? ", ] num_pages_in_book, num_pages_read, goal_date = map( get_input, str_converters, input_msgs ) input_params = num_pages_in_book, num_pages_read, goal_date if validate_input_params(*input_params): return input_params print() def validate_input_params(num_pages_in_book, num_pages_read, goal_date): """Verify input parameters for logical correctness.""" valid_input = True print() if num_pages_in_book <= 0: print("The number of pages in a book must be a positive integer!") valid_input = False if num_pages_read < 0: print("The number of pages read must be a whole number!") valid_input = False if num_pages_read == num_pages_in_book and num_pages_in_book > 0: print( "You have already read the entire book! Why are you running this program?" ) if num_pages_read > num_pages_in_book: print("You cannot have read more pages than there are in the book!") valid_input = False if goal_date < date.today(): print("You cannot set a goal date in the past!") valid_input = False return valid_input def calc_pages_per_day(num_pages_in_book, num_pages_read, goal_date): """Return number of pages to read each day to attain goal.""" COUNT_TODAY = 1 days_to_goal = (goal_date - date.today()).days pages_per_day = (num_pages_in_book - num_pages_read) / (days_to_goal + COUNT_TODAY) return pages_per_day def display_result(pages_per_day): """Display how many pages per day the reader must read to finish the book.""" if 0 < pages_per_day <= 1: word_to_display = "page" else: word_to_display = "pages" print( "You must read {0:.2n} {1} per day to reach your goal.".format( pages_per_day, word_to_display ) ) def main(): """Run program and display results.""" print( 'Welcome to "HOW MANY PAGES???"\n' "Follow on-screen directions to determine how many pages to read each day to " "finish your book.\n\n" ) while True: input_params = get_input_params() display_result(calc_pages_per_day(*input_params)) run_again = input("Enter 'q' to quit or anything else to continue: ") if run_again.lower().startswith("q"): break if __name__ == "__main__": main() ============================================================================ boB From joel.goldstick at gmail.com Sat Jan 4 16:33:04 2020 From: joel.goldstick at gmail.com (Joel Goldstick) Date: Sat, 4 Jan 2020 16:33:04 -0500 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Message-ID: I'll chime in. My comments interleaved with your code On Sat, Jan 4, 2020 at 2:11 AM boB Stepp wrote: > > I now believe that I have a finalized version2.py that I am satisfied > with. I have not done the optional type annotations and am in > progress adding tests. I may have some questions about the resulting > tests once I complete them. But for my New Year's Python 3 exercise, > I wish to throw out the below code for code review by anyone who > wishes to assist boB on his ongoing learning journey. Let me know of > *anything* that you think I should have done better or more > professionally or in a more Pythonic style, etc. > > Another goal I had hoped to accomplish, but have apparently failed at, > was to draw in fellow Python learners into the discussion. So many > times we are asked on this list what can we do to learn how to program > in Python, and the oft-recommended advice is to come up with a project > and code it, asking for help as necessary. I thought showing a simple > program and working on improving it might be helpful for other > learners. Of course the end result to this point has grown from an > original 23 LOC to 97 LOC (including blank lines), so have I really > improved the original product? I await the scores from the judges! > > > ============================================================================ > #!/usr/bin/env python3 > """Calculate the number of pages per day needed to read a book by a > given date.""" > > from datetime import date > > The above looks fine to me. You may want to say what you want the user to input in order to accomplish this, and what is valid for them to input. > def get_input(str_converter, msg): > """Prompt user with a message and return user input with the > correct data type.""" > while True: > try: > return str_converter(input(msg)) > except ValueError: > if str_converter == int: > print("\nPlease enter a whole number!") > elif str_converter == date.fromisoformat: > print("\nPlease enter a valid date in the following > format yyyy-mm-dd!") > This one baffles me a little. If str_converter(input(msg)) doesn't produce a ValueError exception, it returns data, otherwise it prints something, and returns nothing. > > def get_input_params(): > """Collect all needed input parameters.""" > while True: > str_converters = [int, int, date.fromisoformat] > input_msgs = [ > "How many pages are there in your book? ", > "How many pages have you read? ", > "What is your date to finish the book (yyyy-mm-dd)? ", > ] > num_pages_in_book, num_pages_read, goal_date = map( > get_input, str_converters, input_msgs > ) > input_params = num_pages_in_book, num_pages_read, goal_date > if validate_input_params(*input_params): > return input_params > print() > Your docstring is useless. A docstring should describe what the code requires coming in, what it returns, and what function it performs. Its not useful to say something like you have: it does what it needs to do? Also, get_input_parameters has no arguments. and it either returns something, or it returns nothing. > > def validate_input_params(num_pages_in_book, num_pages_read, goal_date): > """Verify input parameters for logical correctness.""" > valid_input = True > print() > if num_pages_in_book <= 0: > print("The number of pages in a book must be a positive integer!") > valid_input = False > if num_pages_read < 0: > print("The number of pages read must be a whole number!") > valid_input = False > if num_pages_read == num_pages_in_book and num_pages_in_book > 0: > print( > "You have already read the entire book! Why are you > running this program?" > ) > if num_pages_read > num_pages_in_book: > print("You cannot have read more pages than there are in the book!") > valid_input = False > if goal_date < date.today(): > print("You cannot set a goal date in the past!") > valid_input = False > return valid_input > Again, you are too general in your docstring. And you don't spell out what this thing returns > > def calc_pages_per_day(num_pages_in_book, num_pages_read, goal_date): > """Return number of pages to read each day to attain goal.""" > COUNT_TODAY = 1 > days_to_goal = (goal_date - date.today()).days > pages_per_day = (num_pages_in_book - num_pages_read) / > (days_to_goal + COUNT_TODAY) > return pages_per_day > This one is ok i guess > > def display_result(pages_per_day): > """Display how many pages per day the reader must read to finish > the book.""" > if 0 < pages_per_day <= 1: > word_to_display = "page" > else: > word_to_display = "pages" > print( > "You must read {0:.2n} {1} per day to reach your goal.".format( > pages_per_day, word_to_display > ) > ) > I would prefer that I do prints with values returned from functions, rather than have the function print some value. > > def main(): > """Run program and display results.""" > print( > 'Welcome to "HOW MANY PAGES???"\n' > "Follow on-screen directions to determine how many pages to > read each day to " > "finish your book.\n\n" > ) > while True: > input_params = get_input_params() > display_result(calc_pages_per_day(*input_params)) > run_again = input("Enter 'q' to quit or anything else to continue: ") > if run_again.lower().startswith("q"): > break > This might be simplified like: while True: display_results(calc_pages_per_day(*get_input_params()) if input("bla bla").lower().startswith('q'): break; > > if __name__ == "__main__": > main() > ============================================================================ > > boB > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor -- Joel Goldstick http://joelgoldstick.com/blog http://cc-baseballstats.info/stats/birthdays From robertvstepp at gmail.com Sat Jan 4 18:05:50 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Sat, 4 Jan 2020 17:05:50 -0600 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Message-ID: Thank you, Joel, for your comments. I have found them valuable. On first reading, the bulk of your comments are about code documentation, and, I believe, expose weaknesses in my understanding of what is expected and required. After I complete this initial reaction to your comments, I am going to hit the 'Net and review docstrings and code commenting and then return with some code updates to see if my understanding has improved. One thing that struck me on my initial read is that while I was trying to refactor the original simple script into something more robust, I was still retaining a "simple script" mentality for my code documentation. I expect this is a faulty habit I have fallen into from not having any oversight of my coding efforts (Other than my questions on this list.). One question I do wish to ask up front: _If_ type annotations are used, does this affect how the code is documented? My impression is that with my code as is (no type annotations) I should have added a section for each function explaining the expected type and function of each parameter and value returned. But if I do use type annotations, that would seem redundant. On Sat, Jan 4, 2020 at 3:33 PM Joel Goldstick wrote: > On Sat, Jan 4, 2020 at 2:11 AM boB Stepp wrote: > > > > ============================================================================ > > #!/usr/bin/env python3 > > """Calculate the number of pages per day needed to read a book by a > > given date.""" > > > > from datetime import date > > > > > The above looks fine to me. You may want to say what you want the > user to input in order to accomplish this, and what is valid for them > to input. > > > def get_input(str_converter, msg): > > """Prompt user with a message and return user input with the > > correct data type.""" > > while True: > > try: > > return str_converter(input(msg)) > > except ValueError: > > if str_converter == int: > > print("\nPlease enter a whole number!") > > elif str_converter == date.fromisoformat: > > print("\nPlease enter a valid date in the following > > format yyyy-mm-dd!") > > > > This one baffles me a little. If str_converter(input(msg)) doesn't > produce a ValueError exception, it returns data, otherwise it prints > something, and returns nothing. My intent was to continue looping, warning the user of his/her error, until the user enters syntactically valid input, and only then returning the input for further validation. Should I have approached this differently? > > def get_input_params(): > > """Collect all needed input parameters.""" > > while True: > > str_converters = [int, int, date.fromisoformat] > > input_msgs = [ > > "How many pages are there in your book? ", > > "How many pages have you read? ", > > "What is your date to finish the book (yyyy-mm-dd)? ", > > ] > > num_pages_in_book, num_pages_read, goal_date = map( > > get_input, str_converters, input_msgs > > ) > > input_params = num_pages_in_book, num_pages_read, goal_date > > if validate_input_params(*input_params): > > return input_params > > print() > > > > Your docstring is useless. A docstring should describe what the code > requires coming in, what it returns, and what function it performs. > Its not useful to say something like you have: it does what it needs > to do? I did restate the obvious, didn't I? I know I am supposed to have some sort of meaningful one-liner to start the docstring before providing more detailed information below the opening line. I am obviously struggling on what to put on this summary line. > Also, get_input_parameters has no arguments. and it either returns > something, or it returns nothing. Again, I am looping until the user provides logically valid input before considering the input valid and returning it. Same question: Should I have done this differently? > > def validate_input_params(num_pages_in_book, num_pages_read, goal_date): > > """Verify input parameters for logical correctness.""" > > valid_input = True > > print() > > if num_pages_in_book <= 0: > > print("The number of pages in a book must be a positive integer!") > > valid_input = False > > if num_pages_read < 0: > > print("The number of pages read must be a whole number!") > > valid_input = False > > if num_pages_read == num_pages_in_book and num_pages_in_book > 0: > > print( > > "You have already read the entire book! Why are you > > running this program?" > > ) > > if num_pages_read > num_pages_in_book: > > print("You cannot have read more pages than there are in the book!") > > valid_input = False > > if goal_date < date.today(): > > print("You cannot set a goal date in the past!") > > valid_input = False > > return valid_input > > > > Again, you are too general in your docstring. And you don't spell out > what this thing returns Duly noted. > > def calc_pages_per_day(num_pages_in_book, num_pages_read, goal_date): > > """Return number of pages to read each day to attain goal.""" > > COUNT_TODAY = 1 > > days_to_goal = (goal_date - date.today()).days > > pages_per_day = (num_pages_in_book - num_pages_read) / > > (days_to_goal + COUNT_TODAY) > > return pages_per_day > > > > This one is ok i guess > > > > def display_result(pages_per_day): > > """Display how many pages per day the reader must read to finish > > the book.""" > > if 0 < pages_per_day <= 1: > > word_to_display = "page" > > else: > > word_to_display = "pages" > > print( > > "You must read {0:.2n} {1} per day to reach your goal.".format( > > pages_per_day, word_to_display > > ) > > ) > > > > I would prefer that I do prints with values returned from functions, > rather than have the function print some value. I am not sure I am comprehending your point. Are you saying that the above function, display_result(pages_per_day), should not have passed in, pages_per_day, but instead called calc_pages_per_day(), and then printed that returned result? If this is correct, what are the significant differences between the two approaches? This is what I am not seeing. > > def main(): > > """Run program and display results.""" > > print( > > 'Welcome to "HOW MANY PAGES???"\n' > > "Follow on-screen directions to determine how many pages to > > read each day to " > > "finish your book.\n\n" > > ) > > while True: > > input_params = get_input_params() > > display_result(calc_pages_per_day(*input_params)) > > run_again = input("Enter 'q' to quit or anything else to continue: ") > > if run_again.lower().startswith("q"): > > break > > > This might be simplified like: > while True: > display_results(calc_pages_per_day(*get_input_params()) > if input("bla bla").lower().startswith('q'): > break; That does seem simpler and clearer. Thanks. > > if __name__ == "__main__": > > main() > > ============================================================================ -- boB From alan.gauld at yahoo.co.uk Sat Jan 4 18:43:16 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Sat, 4 Jan 2020 23:43:16 +0000 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Message-ID: <928a7db3-50a7-9bca-7ce3-01323222b237@yahoo.co.uk> On 04/01/2020 07:10, boB Stepp wrote: > I now believe that I have a finalized version2.py that I am satisfied > with. I've just joined this thread, but I believe the intent was to produce an industrial standard module? A few comments... > Terrible name for a module! :-) Rename it to express what it does. > ============================================================================ > #!/usr/bin/env python3 > """Calculate the number of pages per day needed to read a book by a > given date.""" Give some examples. eg. If a book has 400 pages and there is one day it will be 400, if 20 days, 20 pages/day. Something like that. > > from datetime import date > > > def get_input(str_converter, msg): > """Prompt user with a message and return user input with the > correct data type.""" That's what it says it does but.... > while True: > try: > return str_converter(input(msg)) > except ValueError: > if str_converter == int: > print("\nPlease enter a whole number!") > elif str_converter == date.fromisoformat: > print("\nPlease enter a valid date in the following > format yyyy-mm-dd!") And if its not one of those types? I get a ValueError raised then it does nothing. It just returns None. That's not very friendly. Maybe you should mention that. And describe the type limitations in the doc string. Or rename the function to get_number_input() This sounds like a reusable function, but in fact its pretty tied into the problem at hand. > def get_input_params(): > """Collect all needed input parameters.""" What? For anything? Wow! a magic function that I can use to get any input parameters I ever need. Fantastic. Except.... That's not really what it does. Again this is not reusable code, its just a bit of the main function broken out. Thats OK but you need to tell people that its problem specific and maybe indicate its not reusable by renaming it _get_input_params() or something? > while True: > str_converters = [int, int, date.fromisoformat] > input_msgs = [ > "How many pages are there in your book? ", > "How many pages have you read? ", > "What is your date to finish the book (yyyy-mm-dd)? ", > ] > num_pages_in_book, num_pages_read, goal_date = map( > get_input, str_converters, input_msgs > ) > input_params = num_pages_in_book, num_pages_read, goal_date > if validate_input_params(*input_params): > return input_params > print() Frankly this isn't helping with DRY, you could just as well put the innards into main(). It is all specific to the main function, there is only one call to the function. So you might as well put the code where it is needed. Personally, I think 3 calls to get_input() with the different arguments would be nicer too. Its not repeated code because you are passing different values. Its like using print(). Just because we call print() multiple times doesn't mean we should build some kind of output function that stores all of the output strings and then displays them, with pauses at key points etc. It doable (via yield etc) but not good code. We just call print() with different values. > def validate_input_params(num_pages_in_book, num_pages_read, goal_date): > """Verify input parameters for logical correctness.""" Needs much more work to describe what goes in, what comes out and what exceptions might be thrown.... see comments below on that topic. > valid_input = True > print() > if num_pages_in_book <= 0: > print("The number of pages in a book must be a positive integer!") > valid_input = False > if num_pages_read < 0: > print("The number of pages read must be a whole number!") > valid_input = False > if num_pages_read == num_pages_in_book and num_pages_in_book > 0: > print( > "You have already read the entire book! Why are you > running this program?" > ) Note, that last clause returns True. So even though it makes no sense you consider these valid inputs? Maybe that needs to be explained in the doc-string too. But... Validation functions really shouldn't have lots of prints in them. Separate logic from presentation. This should either return a boolean. Or you can throw suitable errors as found. Let the caller decide what to tell the user. This function should only validate the inputs. > if num_pages_read > num_pages_in_book: > print("You cannot have read more pages than there are in the book!") > valid_input = False > if goal_date < date.today(): > print("You cannot set a goal date in the past!") > valid_input = False > return valid_input > Personally I'd opt to raise ValueErrors for te fails with the strings attached. Then return True if the function passes (so you can use it in a test). > def calc_pages_per_day(num_pages_in_book, num_pages_read, goal_date): > """Return number of pages to read each day to attain goal.""" I'd remove the calc from the name. Call it after what it returns. I'd rather read ppd = pages_per_day(bp, rp, due) than ppd = calc_pages_per_day(bp,rp,due) Maybe its just me but the calc seems implied by the function call. > COUNT_TODAY = 1 > days_to_goal = (goal_date - date.today()).days > pages_per_day = (num_pages_in_book - num_pages_read) / > (days_to_goal + COUNT_TODAY) > return pages_per_day > def display_result(pages_per_day): > """Display how many pages per day the reader must read to finish > the book.""" > if 0 < pages_per_day <= 1: > word_to_display = "page" Would you really say page if it was 0.5 pages per day? I'd only use 'page' if it was exactly 1. But since its a float you need to use an epsilon value. So e = 0.0001 if 1-e < pages_per_day < 1+e: word_to_display = 'page' else: word_to_display = 'pages' > else: > word_to_display = "pages" > print( > "You must read {0:.2n} {1} per day to reach your goal.".format( > pages_per_day, word_to_display > ) > ) > def main(): > """Run program and display results.""" > print( > 'Welcome to "HOW MANY PAGES???"\n' > "Follow on-screen directions to determine how many pages to > read each day to " > "finish your book.\n\n" > ) Why the mixed quotes? Make them all single quotes? Or use one pair of triple quotes? > while True: > input_params = get_input_params() > display_result(calc_pages_per_day(*input_params)) > run_again = input("Enter 'q' to quit or anything else to continue: ") > if run_again.lower().startswith("q"): > break > > if __name__ == "__main__": > main() In summary: 1) expand the doc-strings to describe input/output/errors and limitations. Think about using the type inference style notation for showing input and return types. 2) if a function isn't generally reusable make it obvious by putting an underscore in front of the name. (or make it generally reusable...usually much harder :-) 3) separate presentation and logic. 4) use exceptions more Finally, consider wider usage. eBooks don't have consistent numbers of pages - it depends on font-size set by the user etc. So maybe an option to use lines or characters instead of pages? Or even percent as reported by a Kindle? The calculations are the same. The validation might need extra work. The output display would be different - needing a unit added? -- 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 From mats at wichmann.us Sat Jan 4 18:45:45 2020 From: mats at wichmann.us (Mats Wichmann) Date: Sat, 4 Jan 2020 16:45:45 -0700 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Message-ID: On 1/4/20 4:05 PM, boB Stepp wrote: > Thank you, Joel, for your comments. I have found them valuable. ... > One question I do wish to ask up front: _If_ type annotations are > used, does this affect how the code is documented? My impression is > that with my code as is (no type annotations) I should have added a > section for each function explaining the expected type and function of > each parameter and value returned. But if I do use type annotations, > that would seem redundant. I think there may not be full consensus yet, as type hints are still kind of new to people, and with many people having to live with Python 2 use, either "until recently" or "for a while yet because of our project", haven't really been fully utilized. The idea is to convey information so it's useful to humans _and_ to tools you may choose to use. If you were targeting a Python version that didn't have the hints, they had to go in an external hints file for the tools to find, and that provides no help to human readers. In 2020, maybe that consideration isn't as important, with many people able to target just "modern" Python. Yes, if you have a type hint, the type part of docstring parameter markup is redundant, and can be left out. You probably still want to say other things about the parameter, however. That is, unannotated, you could express validate_input_parameters as (using the "Google docstring format"): def validate_input_params(num_pages_in_book, num_pages_read, goal_date): """Verify input parameters for logical correctness. Args: num_pages_in_book (int): number of pages available num_pages_read (int): number of pages read goal_date (datetime.date): target finish date Returns: bool: input validity """ but with annotations, maybe: def validate_input_params(num_pages_in_book: int, num_pages_read: int, goal_date: datetime.date) -> bool: """Verify input parameters for logical correctness. Args: num_pages_in_book: number of pages available num_pages_read: number of pages read goal_date: target finish date """ Or not... as you know, there's nothing mandatory about this, it's how you think the information is best conveyed - to future you, and to any other readers (or if part of a larger project, following the conventions that project follows). You could argue, for example, that you've chosen such exquisite variable names that you don't need to bother saying what they're for. In the case of this function, that wouldn't be a totally off-base assertion. From PyTutor at danceswithmice.info Sat Jan 4 21:06:36 2020 From: PyTutor at danceswithmice.info (DL Neil) Date: Sun, 5 Jan 2020 15:06:36 +1300 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> Message-ID: <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> On 5/01/20 12:05 PM, boB Stepp wrote: > On first reading, the bulk of your comments are about code > documentation, and, I believe, expose weaknesses in my understanding > of what is expected and required. After I complete this initial > reaction to your comments, I am going to hit the 'Net and review > docstrings and code commenting and then return with some code updates > to see if my understanding has improved. One thing that struck me on > my initial read is that while I was trying to refactor the original > simple script into something more robust, I was still retaining a > "simple script" mentality for my code documentation. I expect this is > a faulty habit I have fallen into from not having any oversight of my > coding efforts (Other than my questions on this list.). Hopefully the readings will answer that level of your question. +1 to the mind-set of "simple-script" being incorrect, even, unprofessional. When you think about it every function/method should be "simple"! If this is not immediately-apparent, recommend adding to your DRY-intent a paper/page or two about the principles of "successive refinement" and "separation of concerns". If you take the principle of "re-use" (not quite to the point of absurdity), is there much of a concept of a "simple script mentality"? +1 your attitude to improving/re-factoring code, towards improvement. If you do not consider OOP to be (too) 'advanced', what may help is drawing a UML diagram - if UML is not in your portfolio (and to ease email-presentation), instead make two (equivalent to UML) lists:- What is the subject of the computation? A book. How are you measuring its size? Nr Pages. What is the objective of the computation: set a reading-rate in pages per day. (more/better expression?) List 1: what pieces of data do you need to know, to describe the book, and carry-out computation List 2: what computations do you need to carry-out (in relation to the book), eg can we have a book with a non-positive number of pages? with a 'number' of pages (in book, or read) which is non-numeric? Does this process help to 'refine' the problem into smaller problems? Does it help to organise the data? Does it help to see what you might need to code as functions/methods? NB it doesn't really matter which way-around you write the lists, and in-truth you may find that writing one stimulates additions/alterations to what you've already written on the other. NBB some will argue about whether considerations of the number of pages in a book belong with "book" or are better tackled as 'input', but for this particular/introductory/illustrative thought-process it will not matter... After such an exercise, does it (combined with earlier comments about functionality) help your thinking towards program design? > One question I do wish to ask up front: _If_ type annotations are > used, does this affect how the code is documented? My impression is > that with my code as is (no type annotations) I should have added a > section for each function explaining the expected type and function of > each parameter and value returned. But if I do use type annotations, > that would seem redundant. One of my favorite comments about comments (and docstrings) was "mumbling". My recollection is that this was describing comments that were either of no real use, eg "# adding a to b"; or were purely there in order to satisfy 'requirements' - I've been known to persuade a linter to shut-up by writing ''' Docstring. ''' (don't forget the u/case nor the final punctuation!) BTW I think I'm fooling the linter, but I'm really only fooling myself... Others have recommended improved names (and the above design-exercise may also help with that). If a name is 'good', it requires no comment: number_of_pages # book size in pages <<<--- mumbling! That said, does that name refer to the number of pages in the book - what about the number of pages the reader has already consumed? Stating the data-type is useful. As you've already noted, the ratio of remaining pages must be computed with numeric values, whereas the input data was strings! Having this information presented as typing-annotations saves us from putting it in a comment. (which is 'better' is another discussion, for another day) That said, for your consideration: (with apologies that email will re-format/mess-up presentation) function calculate_remaining_pages( number_of_pages_in_book:int, number_of_pages_already_read:int, )->int: return ( number_of_pages_in_book - ( number_of_pages_in_book - number_of_pages_already_read ) ) With the choice of reasonable (if improvable - in your application) names, and very simple functionality, what could be added through 'documentation'? However, consider a function which had the objective of accepting ("only") a single numeric input value. Could you (anyone) write a function/method which required no supportive documentation? So, in the same way that functions/methods written in Python describe functionality, what is the purpose of 'documentation'? (and for bonus-points: who is documentation written to/for?) -- Regards =dn From robertvstepp at gmail.com Sun Jan 5 00:40:57 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Sat, 4 Jan 2020 23:40:57 -0600 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <928a7db3-50a7-9bca-7ce3-01323222b237@yahoo.co.uk> References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <928a7db3-50a7-9bca-7ce3-01323222b237@yahoo.co.uk> Message-ID: On Sat, Jan 4, 2020 at 5:43 PM Alan Gauld via Tutor wrote: > > On 04/01/2020 07:10, boB Stepp wrote: > I've just joined this thread, but I believe the intent was to produce an > industrial standard module? > > A few comments... Uh, oh. Alan is joining in. Now I am going to get it good! ~(:>)) > > > > Terrible name for a module! :-) > Rename it to express what it does. Drat! I was hoping to keep upping the number on the end... > > ============================================================================ > > #!/usr/bin/env python3 > > """Calculate the number of pages per day needed to read a book by a > > given date.""" > > Give some examples. eg. If a book has 400 pages and there is one day it > will be 400, if 20 days, 20 pages/day. Something like that. As I said in response to Joel's comments, I am currently engaged on some supplementary reading on this topic. But in particular for a stand-alone program file, I have found a lot of contradictory advice in the past of what *should* be put at the top of the file. > > > > from datetime import date > > > > > > def get_input(str_converter, msg): > > """Prompt user with a message and return user input with the > > correct data type.""" > > That's what it says it does but.... > > > while True: > > try: > > return str_converter(input(msg)) > > except ValueError: > > if str_converter == int: > > print("\nPlease enter a whole number!") > > elif str_converter == date.fromisoformat: > > print("\nPlease enter a valid date in the following > > format yyyy-mm-dd!") > > And if its not one of those types? I get a ValueError raised then it > does nothing. It just returns None. That's not very friendly. Huh? Joel said something similar. Maybe I am being dense, but it *does* do something. If the user types in something that is not one of the allowed types, he gets a helpful message telling him/her what the program is expecting, and the program loops back to the original message requesting input. The function will not terminate until the user enters the expected input. Once the user does enter syntactically valid input, it will return that value. What am I missing or not understanding? > Maybe you should mention that. And describe the type limitations > in the doc string. Or rename the function to get_number_input() But it also does date input. And if in the future I wanted other sorts of input I need only add new string converters for the new data types. > This sounds like a reusable function, but in fact its pretty tied into > the problem at hand. Again, I wonder if I am being dense, but my intent with this function is to handle a variety of input types. I would have to add new ValueError possibilities for new data types. Is this what bothers you? Hmm. I see that in case of ValueError it could look elsewhere for what sort of user friendly error message to display for a particular value of str_converter. Is this what you are getting at? > > def get_input_params(): > > """Collect all needed input parameters.""" > > What? For anything? Wow! a magic function that I can use to get any > input parameters I ever need. Fantastic. Except.... That's not > really what it does. Not a winning docstring selection. > Again this is not reusable code, its just a bit of the main function > broken out. Thats OK but you need to tell people that its problem > specific and maybe indicate its not reusable by renaming it > _get_input_params() or something? > > > > while True: > > str_converters = [int, int, date.fromisoformat] > > input_msgs = [ > > "How many pages are there in your book? ", > > "How many pages have you read? ", > > "What is your date to finish the book (yyyy-mm-dd)? ", > > ] > > num_pages_in_book, num_pages_read, goal_date = map( > > get_input, str_converters, input_msgs > > ) > > input_params = num_pages_in_book, num_pages_read, goal_date > > if validate_input_params(*input_params): > > return input_params > > print() > > Frankly this isn't helping with DRY, you could just as well put the > innards into main(). It is all specific to the main function, there is > only one call to the function. So you might as well put the code where > it is needed. > > Personally, I think 3 calls to get_input() with the different > arguments would be nicer too. Its not repeated code because you are > passing different values. Its like using print(). Just because we call > print() multiple times doesn't mean we should build some kind of output > function that stores all of the output strings and then displays them, > with pauses at key points etc. It doable (via yield etc) but not good > code. We just call print() with different values. This function/portion of the code is what I struggled with the most. Yes, its existence is to call get_input() as many times as needed for the total number of pieces of data it needs from the user and to validate each datum for logical correctness. And it is where a lot of problem specific things are being kept. str_converters and input_msgs and the identifiers for each datum needed from the user. I still feel awkward about this function, but it looks worse to me as originally presented in the "simple" script. I have quickly looked over D. L. Neil's comments, and, combined with yours, wonder if I should redesign the whole shebang from the ground up? Maybe this problem does need an OO approach. > > def validate_input_params(num_pages_in_book, num_pages_read, goal_date): > > """Verify input parameters for logical correctness.""" > > Needs much more work to describe what goes in, what comes out and what > exceptions might be thrown.... see comments below on that topic. > > > > valid_input = True > > print() > > if num_pages_in_book <= 0: > > print("The number of pages in a book must be a positive integer!") > > valid_input = False > > if num_pages_read < 0: > > print("The number of pages read must be a whole number!") > > valid_input = False > > if num_pages_read == num_pages_in_book and num_pages_in_book > 0: > > print( > > "You have already read the entire book! Why are you > > running this program?" > > ) > > Note, that last clause returns True. So even though it makes no sense > you consider these valid inputs? Maybe that needs to be explained in the > doc-string too. After some thought I decided that this should be logically valid input despite the user being a bit thick-headed in this case. So if the user has already read the book, he is dutifully told this and the program will ultimately tell him he needs to read zero pages per day. I did not explicitly code "valid_input = True" as that is the default. Perhaps I should have put in a comment regarding this design decision? > But... > Validation functions really shouldn't have lots of prints in them. > Separate logic from presentation. This should either return a boolean. > Or you can throw suitable errors as found. Let the caller decide what > to tell the user. This function should only validate the inputs. I grudgingly concede your point. Grudgingly because now I have to figure out how to accomplish this. ~(:>)) > > if num_pages_read > num_pages_in_book: > > print("You cannot have read more pages than there are in the book!") > > valid_input = False > > if goal_date < date.today(): > > print("You cannot set a goal date in the past!") > > valid_input = False > > return valid_input > > > > Personally I'd opt to raise ValueErrors for te fails with the strings > attached. Then return True if the function passes (so you can use > it in a test). > > > def calc_pages_per_day(num_pages_in_book, num_pages_read, goal_date): > > """Return number of pages to read each day to attain goal.""" > > I'd remove the calc from the name. Call it after what it returns. > I'd rather read > > ppd = pages_per_day(bp, rp, due) > > than > > ppd = calc_pages_per_day(bp,rp,due) > > Maybe its just me but the calc seems implied by the function call. But I thought the great preference was for function and method names to be verbs not nouns? > > COUNT_TODAY = 1 > > days_to_goal = (goal_date - date.today()).days > > pages_per_day = (num_pages_in_book - num_pages_read) / > > (days_to_goal + COUNT_TODAY) > > return pages_per_day > > > def display_result(pages_per_day): > > """Display how many pages per day the reader must read to finish > > the book.""" > > if 0 < pages_per_day <= 1: > > word_to_display = "page" > > Would you really say page if it was 0.5 pages per day? Actually, yes. Just as I would say a half page per day. OTOH, I would say zero pages per day. Maybe I need to dust off a grammar book... > I'd only use 'page' if it was exactly 1. > > But since its a float you need to use an epsilon value. > So > > e = 0.0001 > if 1-e < pages_per_day < 1+e: > word_to_display = 'page' > else: word_to_display = 'pages' Do I really need an epsilon value for this particular case? I am not seeing where my intentions would fail here. Can you provide an example? I am happy, say, if a user puts in some absurd goal date that results in a pages per day of 1.7 e -100, if he/she really wants to. Originally I thought I would prohibit such things, but I finally thought if the user wishes to be silly here, why not? > > else: > > word_to_display = "pages" > > print( > > "You must read {0:.2n} {1} per day to reach your goal.".format( > > pages_per_day, word_to_display > > ) > > ) > > > def main(): > > """Run program and display results.""" > > print( > > 'Welcome to "HOW MANY PAGES???"\n' > > "Follow on-screen directions to determine how many pages to > > read each day to " > > "finish your book.\n\n" > > ) > > Why the mixed quotes? Make them all single quotes? > Or use one pair of triple quotes? I originally in my code did not use mixed quotes. But when I saved the file my new code formatting tyrant, Black, changed my consistent quoting style to mixed quotes. Apparently it insists on all double quotes unless there are embedded quotes within when it converts those to single quotes. > > while True: > > input_params = get_input_params() > > display_result(calc_pages_per_day(*input_params)) > > run_again = input("Enter 'q' to quit or anything else to continue: ") > > if run_again.lower().startswith("q"): > > break > > > > if __name__ == "__main__": > > main() > > In summary: > 1) expand the doc-strings to describe input/output/errors > and limitations. Think about using the type inference > style notation for showing input and return types. > 2) if a function isn't generally reusable make it obvious > by putting an underscore in front of the name. (or > make it generally reusable...usually much harder :-) This is where I was hoping for some concrete, helpful examples. I do indeed find this "much harder". > 3) separate presentation and logic. Again, I find this not so easy. > 4) use exceptions more > > Finally, consider wider usage. eBooks don't have consistent > numbers of pages - it depends on font-size set by the user > etc. So maybe an option to use lines or characters instead > of pages? Or even percent as reported by a Kindle? > > The calculations are the same. > The validation might need extra work. > The output display would be different - needing a unit added? FEATURE CREEP! Actually sounds like an interesting addition. But I don't much like eReaders myself. I do read my newspaper on an iPad the company kindly supplied, but usually not books. I like the feel of a tangible book, but I'm an old fart, I guess. I'd have to research this some to consider implementing it. My wife would make a good source of info as she is always reading stuff on her phone or Surface Pro. Thanks for the detailed breakdown! boB From johnf at jfcomputer.com Sun Jan 5 01:22:08 2020 From: johnf at jfcomputer.com (john) Date: Sat, 4 Jan 2020 22:22:08 -0800 Subject: [Tutor] dealing with wheels Message-ID: <64ce7aaf-2228-19d8-94d9-91a94e679c70@jfcomputer.com> Hi, In some cases it is possible to download a .whl for use offline.? But in some cases I don't see the whl file.? It appears it (the module) can be installed using "pip install". So if can be installed using pip where is the .whl file? I need to be able to install while offline. Johnf From gogonegro at gmail.com Sun Jan 5 11:00:54 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Sun, 5 Jan 2020 17:00:54 +0100 Subject: [Tutor] Please help disentangle my program logic Message-ID: Dear friends and tutors, Also thanks to some of your help I have written my first little python program. It now lives on Github https://github.com/rjalexa/adsl-warn and its purpose is to monitor my home ADSL speeds, log them and if they degrade send me a Gmail email with increasing frequency. While is does work ok, thinking about it?s overall flow there are things I do not like. The next iteration should always log a line covering all cases from the LAN not functioning, to Internet not reachable (and of course in these cases I should log but not attempt to send an email), to the degraded (log + email) and normal (log only) speeds. I usually run this program as described in the requirements.txt file as "nohup python -u speedtest.py >> adsl.log &" As you can see in the current version the st_json function spits out two log lines which have a different format from the usual ones and quits altogether breaking the while True loop. This might be acceptable in the ?Speedtest prerequisite not installed? case but not for the ?Internet down case? which should be logged (the frequencies of logging are computed in the set_frequency function and currently do not contemplate the Internet-offline case). So currently the function calls in the main are a bit of spaghetti logic which makes it hard to define a standard modular log line that could be parseable in the future. Two main questions are: A) Are there tools to quickly/neatly look at a flow of your program? For example how would you modify the st_json function to handle both a successful measure but also the other two cases (Internet not reachable and speediest CLI not installed)? B) Suggestions to make this little program more rational? Thank you all very much in advance Robert From mayoadams at gmail.com Sun Jan 5 12:42:52 2020 From: mayoadams at gmail.com (Mayo Adams) Date: Sun, 5 Jan 2020 12:42:52 -0500 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: References: Message-ID: The pages of Phillip Guo might possibly help, though your stuff looks pretty advanced (pythontutor.com/visualize). On Sun, Jan 5, 2020 at 11:01 AM Robert Alexander wrote: > Dear friends and tutors, > Also thanks to some of your help I have written my first little python > program. > > It now lives on Github https://github.com/rjalexa/adsl-warn and its > purpose > is to monitor my home ADSL speeds, log them and if they degrade send me a > Gmail email with increasing frequency. > > While is does work ok, thinking about it?s overall flow there are things I > do not like. > > The next iteration should always log a line covering all cases from the LAN > not functioning, to Internet not reachable (and of course in these cases I > should log but not attempt to send an email), to the degraded (log + email) > and normal (log only) speeds. > > I usually run this program as described in the requirements.txt file as > "nohup python -u speedtest.py >> adsl.log &" > > As you can see in the current version the st_json function spits out two > log lines which have a different format from the usual ones and quits > altogether breaking the while True loop. This might be acceptable in the > ?Speedtest prerequisite not installed? case but not for the ?Internet down > case? which should be logged (the frequencies of logging are computed in > the set_frequency function and currently do not contemplate the > Internet-offline case). > > So currently the function calls in the main are a bit of spaghetti logic > which makes it hard to define a standard modular log line that could be > parseable in the future. > > Two main questions are: > > A) Are there tools to quickly/neatly look at a flow of your program? For > example how would you modify the st_json function to handle both a > successful measure but also the other two cases (Internet not reachable and > speediest CLI not installed)? > > B) Suggestions to make this little program more rational? > > Thank you all very much in advance > Robert > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > -- Mayo Adams 287 Erwin Rd. Chapel Hill, NC 27514 (919)-780-3917 mayoadams at gmail.com From gogonegro at gmail.com Sun Jan 5 12:47:44 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Sun, 5 Jan 2020 18:47:44 +0100 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: References: Message-ID: Thank you very much, but it gives a 404 not found error. On 5 January 2020 at 18:43:03, Mayo Adams (mayoadams at gmail.com) wrote: The pages of Phillip Guo might possibly help, though your stuff looks pretty advanced (pythontutor.com/visualize). On Sun, Jan 5, 2020 at 11:01 AM Robert Alexander wrote: > Dear friends and tutors, > Also thanks to some of your help I have written my first little python > program. > > It now lives on Github https://github.com/rjalexa/adsl-warn and its > purpose > is to monitor my home ADSL speeds, log them and if they degrade send me a > Gmail email with increasing frequency. > > While is does work ok, thinking about it?s overall flow there are things I > do not like. > > The next iteration should always log a line covering all cases from the LAN > not functioning, to Internet not reachable (and of course in these cases I > should log but not attempt to send an email), to the degraded (log + email) > and normal (log only) speeds. > > I usually run this program as described in the requirements.txt file as > "nohup python -u speedtest.py >> adsl.log &" > > As you can see in the current version the st_json function spits out two > log lines which have a different format from the usual ones and quits > altogether breaking the while True loop. This might be acceptable in the > ?Speedtest prerequisite not installed? case but not for the ?Internet down > case? which should be logged (the frequencies of logging are computed in > the set_frequency function and currently do not contemplate the > Internet-offline case). > > So currently the function calls in the main are a bit of spaghetti logic > which makes it hard to define a standard modular log line that could be > parseable in the future. > > Two main questions are: > > A) Are there tools to quickly/neatly look at a flow of your program? For > example how would you modify the st_json function to handle both a > successful measure but also the other two cases (Internet not reachable and > speediest CLI not installed)? > > B) Suggestions to make this little program more rational? > > Thank you all very much in advance > Robert > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > -- Mayo Adams 287 Erwin Rd. Chapel Hill, NC 27514 (919)-780-3917 mayoadams at gmail.com From mayoadams at gmail.com Sun Jan 5 13:11:19 2020 From: mayoadams at gmail.com (Mayo Adams) Date: Sun, 5 Jan 2020 13:11:19 -0500 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: References: Message-ID: If you "Google" Visualize Python, you might have better luck. I just did it, no problem.It's an interesting tool to help you understand program execution. Even if it's too basic for what you're up to, I think it's worthwhile to know about. On Sun, Jan 5, 2020 at 12:47 PM Robert Alexander wrote: > Thank you very much, but it gives a 404 not found error. > > > On 5 January 2020 at 18:43:03, Mayo Adams (mayoadams at gmail.com) wrote: > > The pages of Phillip Guo might possibly help, though your stuff looks > pretty advanced (pythontutor.com/visualize). > > On Sun, Jan 5, 2020 at 11:01 AM Robert Alexander > wrote: > >> Dear friends and tutors, >> Also thanks to some of your help I have written my first little python >> program. >> >> It now lives on Github https://github.com/rjalexa/adsl-warn and its >> purpose >> is to monitor my home ADSL speeds, log them and if they degrade send me a >> Gmail email with increasing frequency. >> >> While is does work ok, thinking about it?s overall flow there are things I >> do not like. >> >> The next iteration should always log a line covering all cases from the >> LAN >> not functioning, to Internet not reachable (and of course in these cases I >> should log but not attempt to send an email), to the degraded (log + >> email) >> and normal (log only) speeds. >> >> I usually run this program as described in the requirements.txt file as >> "nohup python -u speedtest.py >> adsl.log &" >> >> As you can see in the current version the st_json function spits out two >> log lines which have a different format from the usual ones and quits >> altogether breaking the while True loop. This might be acceptable in the >> ?Speedtest prerequisite not installed? case but not for the ?Internet down >> case? which should be logged (the frequencies of logging are computed in >> the set_frequency function and currently do not contemplate the >> Internet-offline case). >> >> So currently the function calls in the main are a bit of spaghetti logic >> which makes it hard to define a standard modular log line that could be >> parseable in the future. >> >> Two main questions are: >> >> A) Are there tools to quickly/neatly look at a flow of your program? For >> example how would you modify the st_json function to handle both a >> successful measure but also the other two cases (Internet not reachable >> and >> speediest CLI not installed)? >> >> B) Suggestions to make this little program more rational? >> >> Thank you all very much in advance >> Robert >> _______________________________________________ >> Tutor maillist - Tutor at python.org >> To unsubscribe or change subscription options: >> https://mail.python.org/mailman/listinfo/tutor >> > > > -- > Mayo Adams > > 287 Erwin Rd. > Chapel Hill, NC 27514 > (919)-780-3917 > mayoadams at gmail.com > > -- Mayo Adams 287 Erwin Rd. Chapel Hill, NC 27514 (919)-780-3917 mayoadams at gmail.com From mats at wichmann.us Sun Jan 5 13:18:11 2020 From: mats at wichmann.us (Mats Wichmann) Date: Sun, 5 Jan 2020 11:18:11 -0700 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: References: Message-ID: <3b4dcc39-c49f-c13a-1a4a-bf4eec9f5b33@wichmann.us> On 1/5/20 9:00 AM, Robert Alexander wrote: > Dear friends and tutors, > Also thanks to some of your help I have written my first little python > program. > > It now lives on Github https://github.com/rjalexa/adsl-warn and its purpose > is to monitor my home ADSL speeds, log them and if they degrade send me a > Gmail email with increasing frequency. > > While is does work ok, thinking about it?s overall flow there are things I > do not like. > > The next iteration should always log a line covering all cases from the LAN > not functioning, to Internet not reachable (and of course in these cases I > should log but not attempt to send an email), to the degraded (log + email) > and normal (log only) speeds. quick reaction: look into the logging module to control how, where and what to log. No need to reinvent the wheel. > > I usually run this program as described in the requirements.txt file as > "nohup python -u speedtest.py >> adsl.log &" You shouldn't need to depend on shell syntax to get this stuff to work... the logging module will handle appending, or you can do it yourself (print can take a a file= argument to show where to send the output, and you open that file first in the appropriate mode) From gogonegro at gmail.com Sun Jan 5 13:23:56 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Sun, 5 Jan 2020 19:23:56 +0100 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: <3b4dcc39-c49f-c13a-1a4a-bf4eec9f5b33@wichmann.us> References: <3b4dcc39-c49f-c13a-1a4a-bf4eec9f5b33@wichmann.us> Message-ID: Thank you Mats. Very interesting observation. May you kindly expand on why you are suggesting this? In my opinion specifying a log name at runtime gives me more flexibility but I might be not considering some other important reason. Ciao PS Etiquette question please: Is it better to reply to both the sender and the list or the list only? On 5 January 2020 at 19:18:30, Mats Wichmann (mats at wichmann.us) wrote: On 1/5/20 9:00 AM, Robert Alexander wrote: > Dear friends and tutors, > Also thanks to some of your help I have written my first little python > program. > > It now lives on Github https://github.com/rjalexa/adsl-warn and its purpose > is to monitor my home ADSL speeds, log them and if they degrade send me a > Gmail email with increasing frequency. > > While is does work ok, thinking about it?s overall flow there are things I > do not like. > > The next iteration should always log a line covering all cases from the LAN > not functioning, to Internet not reachable (and of course in these cases I > should log but not attempt to send an email), to the degraded (log + email) > and normal (log only) speeds. quick reaction: look into the logging module to control how, where and what to log. No need to reinvent the wheel. > > I usually run this program as described in the requirements.txt file as > "nohup python -u speedtest.py >> adsl.log &" You shouldn't need to depend on shell syntax to get this stuff to work... the logging module will handle appending, or you can do it yourself (print can take a a file= argument to show where to send the output, and you open that file first in the appropriate mode) _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From mats at wichmann.us Sun Jan 5 13:34:32 2020 From: mats at wichmann.us (Mats Wichmann) Date: Sun, 5 Jan 2020 11:34:32 -0700 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: References: <3b4dcc39-c49f-c13a-1a4a-bf4eec9f5b33@wichmann.us> Message-ID: <32d4b470-c050-eb32-b4de-948e7679a678@wichmann.us> On 1/5/20 11:23 AM, Robert Alexander wrote: > Thank you Mats. Very interesting observation. > > May you kindly expand on why you are suggesting this? > > In my opinion specifying a log name at runtime gives me more flexibility > but I might be not considering some other important reason. Not sure I'm answering the right question here, but... You can still specify the logging location on the command line by accepting an argument, and having a default in the program if it's not on the command line. The logigng module gives you the flexibility to log to different places, even - you can put every reading in the log, and important events (like Internet connection is down) somewhere else - your console, sending by email (I'm not sure that's directly supported, but wouldn't be hard to set up), by assigning different log levels to different types of events. > > Ciao > > PS Etiquette question please: Is it better to reply to both the sender and > the list or the list only? > > > On 5 January 2020 at 19:18:30, Mats Wichmann (mats at wichmann.us) wrote: > > quick reaction: look into the logging module to control how, where and > what to log. No need to reinvent the wheel. From gogonegro at gmail.com Sun Jan 5 13:37:18 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Sun, 5 Jan 2020 19:37:18 +0100 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: <32d4b470-c050-eb32-b4de-948e7679a678@wichmann.us> References: <3b4dcc39-c49f-c13a-1a4a-bf4eec9f5b33@wichmann.us> <32d4b470-c050-eb32-b4de-948e7679a678@wichmann.us> Message-ID: That makes a lot of sense. I already have such situation. My program currently handles an ?exception? and exits if a prerequisite external program is not found. So that would go to STDOUT/ERR and the logs proper to a log via the logging module. Excellent suggestion. Thanks a lot. Robert On 5 January 2020 at 19:34:51, Mats Wichmann (mats at wichmann.us) wrote: On 1/5/20 11:23 AM, Robert Alexander wrote: > Thank you Mats. Very interesting observation. > > May you kindly expand on why you are suggesting this? > > In my opinion specifying a log name at runtime gives me more flexibility > but I might be not considering some other important reason. Not sure I'm answering the right question here, but... You can still specify the logging location on the command line by accepting an argument, and having a default in the program if it's not on the command line. The logigng module gives you the flexibility to log to different places, even - you can put every reading in the log, and important events (like Internet connection is down) somewhere else - your console, sending by email (I'm not sure that's directly supported, but wouldn't be hard to set up), by assigning different log levels to different types of events. > > Ciao > > PS Etiquette question please: Is it better to reply to both the sender and > the list or the list only? > > > On 5 January 2020 at 19:18:30, Mats Wichmann (mats at wichmann.us) wrote: > > quick reaction: look into the logging module to control how, where and > what to log. No need to reinvent the wheel. _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From cs at cskk.id.au Sun Jan 5 18:19:40 2020 From: cs at cskk.id.au (Cameron Simpson) Date: Mon, 6 Jan 2020 10:19:40 +1100 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: References: Message-ID: <20200105231940.GA49876@cskk.homeip.net> On 05Jan2020 19:37, Robert Alexander wrote: >My program currently handles an ?exception? and exits if a prerequisite >external program is not found. So that would go to STDOUT/ERR and the logs >proper to a log via the logging module. The default logger sends to stderr, BTW. So for simple things I just log errors etc via the default logging calls and they come out on stderr as normal for a UNIXy command line. Another nice thing about the logging module is that the format facility lets you configure it to do boilerplate like the timestamp or log level label, for example: '%(asctime)s %(levelname)s %(message)s' However, your setup line: nohup python -u speedtest.py >> adsl.log & expects stuff to come from the standard output. You'd want: nohup python -u speedtest.py >> adsl.log 2>&1 & to use the default logging arrangement. On the subject of starting long running daemons, personally I kick a lot of daemony things off in tmux (like a more modern screen) instead of using nohup. That way I've got a nicely named session I can reconnect to, and I can just interrupt it and restart it differently or whatever. >> PS Etiquette question please: Is it better to reply to both the >> sender and the list or the list only? I tend to reply to the list and the sender by default, with the Reply-To: set to the list address. But just-to-the-list is also normal, and arguably more polite. Cheers, Cameron Simpson From alan.gauld at yahoo.co.uk Sun Jan 5 19:29:21 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Mon, 6 Jan 2020 00:29:21 +0000 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> Message-ID: <1c4a6bad-dfa6-c2d4-d59b-697bf9c4405f@yahoo.co.uk> On 05/01/2020 02:06, DL Neil via Tutor wrote: > One of my favorite comments about comments (and docstrings) was > "mumbling". My recollection is that this was describing comments that > were either of no real use, eg "# adding a to b"; or were purely there > in order to satisfy 'requirements' - I've been known to persuade a > linter to shut-up by writing ''' Docstring. ''' (don't forget the u/case > nor the final punctuation!) BTW I think I'm fooling the linter, but I'm > really only fooling myself... I once worked on a large (very large - 10,000+ files) C project. It was initially written by a US tech company and sent to us. They in turn had employed many short term contractors to meet the project deadline... When we reviewed the code we found lots of files with things like this at the top: /* Scooby dooby dooo..... Where are you.... */ Or /* I need comments to pass the metrics program.... My favourite was "I did it my way..." but another apropos title was the chorus of "The Marvellous Toy" :-) -- 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 From alan.gauld at yahoo.co.uk Sun Jan 5 19:30:12 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Mon, 6 Jan 2020 00:30:12 +0000 Subject: [Tutor] Fwd: Re: How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: Message-ID: <974c5136-d8bc-ba3c-f169-9341b2299482@yahoo.co.uk> On 05/01/2020 05:40, boB Stepp wrote: >>> while True: >>> try: >>> return str_converter(input(msg)) >>> except ValueError: >>> if str_converter == int: >>> print("\nPlease enter a whole number!") >>> elif str_converter == date.fromisoformat: >>> print("\nPlease enter a valid date in the following >>> format yyyy-mm-dd!") >> >> And if its not one of those types? I get a ValueError raised then it >> does nothing. It just returns None. That's not very friendly. > > Huh? Joel said something similar. Maybe I am being dense, but it > *does* do something. If the user types in something that is not one > of the allowed types, he gets a helpful message telling him/her what > the program is expecting, and the program loops back to the original > message requesting input. My bad it does not "return None" in the technical sense. It displays nothing just returns to the prompt. Try writing your own string convertor: def myInt(s): return int(s) Now use myInt as the string convertor and see what happens... It does 'work' but is less user friendly. > But it also does date input. And if in the future I wanted other > sorts of input I need only add new string converters for the new data > types. Yes, but the udr of your module will expect to be able to write their own string convertors and they wont work without modifying the module - thats bad. You would need to have a dict of string convertors and error messages so that the error handler could look up the error message using the string convertor as a key: except ValueError: print conv_msg[string_convertor] And the user can then add their own error message: module.conv_msg[myInt] = "Enter an integer" >> This sounds like a reusable function, but in fact its pretty tied into >> the problem at hand. > > Again, I wonder if I am being dense, but my intent with this function > is to handle a variety of input types. I would have to add new > ValueError possibilities for new data types. Is this what bothers > you? Yes because when I download your "industrial strength" module I discover I either have to contact you every time I need a new string converter or I have to fix up the module myself! Neither is good from a users perspective. > Hmm. I see that in case of ValueError it could look elsewhere for > what sort of user friendly error message to display for a particular > value of str_converter. Is this what you are getting at? Yes, see the dict example above. > presented in the "simple" script. I have quickly looked over D. L. > Neil's comments, and, combined with yours, wonder if I should redesign > the whole shebang from the ground up? Maybe this problem does need an > OO approach. You shouldn't need an OO approach for something this small. But you do need a bit of a rethink in tems of splitting out the functions cleanly. And exactly what is published API and what is just internal utility code. > I grudgingly concede your point. Grudgingly because now I have to > figure out how to accomplish this. ~(:>)) I think exceptions are the way to go. >> ppd = calc_pages_per_day(bp,rp,due) >> >> Maybe its just me but the calc seems implied by the function call. > > But I thought the great preference was for function and method names > to be verbs not nouns? Hmmm, yes. OK I'll give you that one :-) >> Would you really say page if it was 0.5 pages per day? > > Actually, yes. Just as I would say a half page per day. OTOH, I > would say zero pages per day. Maybe I need to dust off a grammar > book... a half page is not the same as 0,5 pages. The a forces it to single. So in your code you need to add an 'a' before the value to make it grammatical. Thats more work, but doable, but what would you say for a value like 0.82? What kind of fraction is that? Are you going to convert it to say a 82 hundredth of a page? Good luck with that... >> I'd only use 'page' if it was exactly 1. >> >> But since its a float you need to use an epsilon value. >> So >> >> e = 0.0001 >> if 1-e < pages_per_day < 1+e: >> word_to_display = 'page' >> else: word_to_display = 'pages' > > Do I really need an epsilon value for this particular case? I am not > seeing where my intentions would fail here. Can you provide an > example? This is because the computer cannot accurately represent floats so you may get a value like 0.999999999 or 1.00000001 when you want to treat it as 1. So you either use an epsilon or you do a float to some minimal number of decimals. > thought if the user wishes to be silly here, why not? Its not about the user, its the floating point division in a digital computer. > I originally in my code did not use mixed quotes. But when I saved > the file my new code formatting tyrant, Black, changed my consistent > quoting style to mixed quotes. Apparently it insists on all double > quotes unless there are embedded quotes within when it converts those > to single quotes. Yukkkk! >> 2) if a function isn't generally reusable make it obvious >> by putting an underscore in front of the name. (or >> make it generally reusable...usually much harder :-) > > This is where I was hoping for some concrete, helpful examples. I do > indeed find this "much harder". Everyone does. Management routinely ask devs to create reusable code, but studies show that writing reusable code is from 3-5 times more expensive than writing single-use code. Some studies say more like 10 times if you factor in maintenance costs too. And there is no easy answer, it takes painful lessons and experience, and you'll still mess it up! >> 3) separate presentation and logic. > > Again, I find this not so easy. This is easier and just takes a little practice to get used to it. Basically every function should either do display or logic. If its doing logic then return values that the caller can use/display. > FEATURE CREEP! Actually sounds like an interesting addition. But I > don't much like eReaders myself. I didn't used to but, on holiday, I save a ton of weight by taking a Kindle. And having got used to them I now occasionally even read tech books on it. Although code formatting often gets messed up. But I'm currently reading a book on curses programming in C and converting it to python - maybe to become a page in my tutor someday - and that's a Kindle book. -- 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 From alan.gauld at yahoo.co.uk Sun Jan 5 19:59:01 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Mon, 6 Jan 2020 00:59:01 +0000 Subject: [Tutor] Fwd: Re: How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <974c5136-d8bc-ba3c-f169-9341b2299482@yahoo.co.uk> References: <974c5136-d8bc-ba3c-f169-9341b2299482@yahoo.co.uk> Message-ID: <4ae7ac28-053a-e0f5-eeaf-2b6bd6072970@yahoo.co.uk> On 06/01/2020 00:30, Alan Gauld via Tutor wrote: > as 1. So you either use an epsilon or you do a float to some minimal > number of decimals. ...do a round.... doh! -- 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 From PyTutor at DancesWithMice.info Sun Jan 5 21:35:31 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Mon, 6 Jan 2020 15:35:31 +1300 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <1c4a6bad-dfa6-c2d4-d59b-697bf9c4405f@yahoo.co.uk> References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> <1c4a6bad-dfa6-c2d4-d59b-697bf9c4405f@yahoo.co.uk> Message-ID: On 6/01/20 1:29 PM, Alan Gauld via Tutor wrote: > On 05/01/2020 02:06, DL Neil via Tutor wrote: > >> One of my favorite comments about comments (and docstrings) was >> "mumbling". My recollection is that this was describing comments that >> were either of no real use, eg "# adding a to b"; or were purely there >> in order to satisfy 'requirements' - I've been known to persuade a >> linter to shut-up by writing ''' Docstring. ''' (don't forget the u/case >> nor the final punctuation!) BTW I think I'm fooling the linter, but I'm >> really only fooling myself... > > I once worked on a large (very large - 10,000+ files) C project. > It was initially written by a US tech company and sent to us. > They in turn had employed many short term contractors to meet > the project deadline... > When we reviewed the code we found lots of files with things > like this at the top: > > /* Scooby dooby dooo..... > > > > Where are you.... */ > > Or > > /* I need comments to pass the metrics program.... > > > > > My favourite was "I did it my way..." > but another apropos title was the chorus of > "The Marvellous Toy" :-) Way back, in the 80s(?), I had a salesman trying to sell me one of those 'measure the performance of your programmers'-type products. Even he admitted that we could expect just such behavior! (I think it also flagged goto-statements as 'red flags'!) Classic example of 'the law of unintended consequences'! If you make an (artificial/boorish/stupid/...) rule, people feel forced to obey the 'letter of the law' but 'miss' (either deliberately or ignorantly) 'the spirit of the law'. My favorite of these was during one of my UN assignments to a country I shall not name (to protect the 'innocent'). These folk had a laissez-faire culture and thus attitude to 9-5 work-hours. A pair of colleagues from India/Sri-Lanka, whose culture is quite different and whose attitude to such poor time-keeping was one of major criticism, if not anger; decided, without discussion amongst the team or with many of our host management, to install time-card machines. They were amazed to read the first monthly-report and find that one department's personnel almost all arrived for work something like 90-minutes before 'time' - and that they all left within minutes (again, usually 'over' time). Casual observation showed that this was not reality. It took them ages to work-out how it was done - which showed another cultural-gap in group-dynamics, and attitudes to 'team-work'! BTW lest anyone think I was criticising any one race/creed/culture, let me say that one of my first meetings in London a short-time later, featured me waiting well beyond the 1000 appointment-time, whilst my 'host' completed reading his morning newspaper... I don't think a time-card would have solved his problem - nor improved his documentation! In partial response to a couple of comments made earlier in this thread: these days we don't need to comment-out chunks of code (per Scooby Doo, above) when we are refactoring/replacing it - we have VCS enabling us to revert to version n-1, or to run a compare-analysis if/when required! For similar reasons, many of the web-articles talking about the various comments we can/should/must add to the beginning of our program(me)s have become anachronistic, eg we used to keep notes of how the code had been changed, release by release (per Release Notes, but on a module/class basis). No longer necessary because those comments should document each VCS-commit! Similarly, the version numbering system comes from delivering a repository's 'latest version' to the 'delivery mechanism'(s). -- Regards =dn From robertvstepp at gmail.com Sun Jan 5 22:25:52 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Sun, 5 Jan 2020 21:25:52 -0600 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> Message-ID: I have been doing much pondering of yesterday's remarks. In particular your and Alan's thoughts have me reflecting deeply. On Sat, Jan 4, 2020 at 8:07 PM DL Neil via Tutor wrote: > > On 5/01/20 12:05 PM, boB Stepp wrote: > If you do not consider OOP to be (too) 'advanced', what may help is > drawing a UML diagram - if UML is not in your portfolio (and to ease > email-presentation), instead make two (equivalent to UML) lists:- Other than some toy, play around with code programs to explore OOP, I have yet to write anything for serious use with that style of code organization (Unless you throw out that everything in Python is an object, etc.). I keep circling around the OOP pool, but have yet to leap in and try to swim. > What is the subject of the computation? A book. > How are you measuring its size? Nr Pages. When I read this in the wee morning hours today, I was startled that it made sense to consider multiple books (Or other sorts of reading materials, per Alan.) even in light of the original simple script. Say I was a student (I always am, just not "officially".). I could see in light of a multiple course load, each with multiple readings to accomplish with each having different due dates, that I (or my mythical user) might want to keep track of all of this and how many pages each day to read in each text. And that thinking leads to persistent storage and upon opening the program updating the pages per day of each text. So this along with Alan' and your comments have me shifting my thinking from procedural code organization to OO organization. So I keep threatening in previous comments I have made on this list to tackle the OOP learning curve. Perhaps now is the timely time? So I have been thinking a lot about this today and find myself stumbling around in a confused daze despite making lists, crude UML-like diagrams, etc. So questions! I think the basic unit of code organization would be a ReadingMaterial class, choosing this name to reflect Alan's thoughts that ultimately there might be other things to read besides paper-based books. To keep with the current confines of the original problem as stated, an object of this class would have the following user-entered attributes: title, total_amount_to_read, total_amount_read, and goal_date. I switched from "pages" to "amount" as my best wording to reflect a potential shift from pages read to percent of item read or lines read or whatever. This means that I will have to throw in the concept of "units" if I do this generalization now. I know the Agile people might say stick with today's problem (pages) and worry about other units when one is forced to. So I may stick with pages after I hash out more important difficulties. So back to "pages" for now. So I am really struggling with "separation of concerns" on getting the four needed attributes of ReadingMaterial from the user's screen to the particular instance of this class that the user wishes to create. First point of confusion: the creation of the ReadingMaterial object. Should it start with (a) my_book = ReadingMaterial() or with (b) my_book = Reading_Material(title [, total_pages_to_read, total_pages_read, goal_date])? The brackets indicate optionally starting with all four attributes from the get-go. In other words in (a) the object would see to initiating the acquisition of user input itself, while the second has some other entity taking care of user input and then passing it into the instantiation process. Which is the proper way to proceed? On the (a) side, ReadingMaterial knows what it needs and it is the natural location for future additional attributes that may need to be acquired someday. This is the direction I am currently leaning. So the initialization of a ReadingMaterial object might look like: class ReadingMaterial: def __init__(self): self.title = _get_input(???) self.total_pages_to_read = _get_input(???) self.total_pages_read = _get_input(???) self.goal_date = _get_input(???) def _get_input(self, ???): return input_manager.get_input(???) Based on my previous code explorations, I need to do two forms of input validation: (1) syntactical, so no ValueErrors result and (2) logical: Things like the number of pages read are less than the length of what is being read, etc. It seems to me that the InputManager (yet to be fleshed out) should do (1) and ReadingMaterial should do (2), especially as it cannot be done until *all* of the input is acquired. So what about (???) ? Only ReadingMaterial knows about the details of each attribute, especially validating the "type" of input. Also, it seems natural that in case of type validation exception being thrown, the resulting message to the user should be customized to the input being asked for. That suggests to me that such error messages should be stored in the ReadingMaterial class. Additionally, the input prompt for each attribute to be acquired needs to be customized appropriately as well. And if I am engaging in "good" OOD thinking then that suggest that (???) should minimally be the input type being requested (str -- not really an issue since input() returns a string, int, date object, etc), the customized input prompt and the customized error message. I presume good practice would for the InputManager to pass the input prompt to a DisplayManager to be printed to the user's screen and that the InputManager would convert the user input to the requested (by ReadingMaterial) type, syntactically validate it, if that fails send the error message to the DisplayManager and await a new effort at valid input. Is this how I should be thinking about this problem? *If* I am on the right track, where does the InputManager get instantiated? DisplayManager? Is this where in the MVC pattern, the program's main() would instantiate a Controller and then the Controller would be responsible for coordinating these? Anyway this is where I am at now staring at a mostly empty split Neovim screen, split between reading_tracker.py (Name change per Alan ~(:>)) ) and test_reading_tracker.py with ============================================================================ #!/usr/bin/env python3 """Pages per day needed to complete a reading on time. [Usage message] [Third-party or custom imports] [A list of any classes, exception, functions, and any other objects exported by the module] class ReadingMaterial """ class ReadingMaterial: """A class to represent reading material such as books.""" pass if __name__ == "__main__": pass ============================================================================ on the left side and ============================================================================ #!/usr/bin/env python3 """Tests for reading_tracker. [Usage message] [Third-party or custom imports] [A list of any classes, exception, functions, and any other objects exported by the module] """ import unittest from reading_tracker import ReadingMaterial if __name__ == "__main__": unittest.main() ============================================================================ on the right side. -- boB From PyTutor at danceswithmice.info Sun Jan 5 22:39:05 2020 From: PyTutor at danceswithmice.info (DL Neil) Date: Mon, 6 Jan 2020 16:39:05 +1300 Subject: [Tutor] Fwd: Re: How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <974c5136-d8bc-ba3c-f169-9341b2299482@yahoo.co.uk> References: <974c5136-d8bc-ba3c-f169-9341b2299482@yahoo.co.uk> Message-ID: On 6/01/20 1:30 PM, Alan Gauld via Tutor wrote: > On 05/01/2020 05:40, boB Stepp wrote: >> presented in the "simple" script. I have quickly looked over D. L. >> Neil's comments, and, combined with yours, wonder if I should redesign >> the whole shebang from the ground up? Maybe this problem does need an >> OO approach. > > You shouldn't need an OO approach for something this small. You're just baiting me, right? Which comes first: the chicken or the egg? the data-objects or the algorithm? (Alan and I will argue this, just for fun...) On a personal note, I usually find that organising the data - the "entities" involved in a problem, is the?my best way of understanding the problem and peeling-back the onion-skin process of moderating one large 'problem' into a number of smaller, and more easily-solved challenges. YMMV! Accordingly, I keep separate the process of gathering input, from the calculation, from the niceties of presentation... As the divided-problems become single tasks, the can be implemented as Python methods/functions without much thought. Surely it is also much easier, at this single-task level, to analyse what might 'go wrong', and to test or to code precautions accordingly? In the case of the input, once the 'problem' of collecting one integer value from the user has been 'solved', it also becomes possible to see how that routine could be re-used to collect the other integer value(s)... (thus addressing the single-use/re-use discussion, below) > But you do need a bit of a rethink in tems of splitting out the > functions cleanly. And exactly what is published API and what > is just internal utility code. +1 >> I grudgingly concede your point. Grudgingly because now I have to >> figure out how to accomplish this. ~(:>)) > > I think exceptions are the way to go. +1 Have a function return ONE data-type/meaning, and only one! Thus if the function is to collect a (valid) integer from user-input, return that integer. If it ALSO is to interpret a null-entry or perhaps a negative-number as a "sentinel value" to cease execution, then code that condition to raise an Exception. (in Python exceptions are not necessarily "errors"!) >> But I thought the great preference was for function and method names >> to be verbs not nouns? > > Hmmm, yes. OK I'll give you that one :-) I wasn't going to raise this unexpected utterance - thinking myself too polite. (oh forget it - who am I trying to convince?)... >>> Would you really say page if it was 0.5 pages per day? >> >> Actually, yes. Just as I would say a half page per day. OTOH, I >> would say zero pages per day. Maybe I need to dust off a grammar >> book... > > a half page is not the same as 0,5 pages. > The a forces it to single. So in your code you need to add > an 'a' before the value to make it grammatical. > Thats more work, but doable, but what would you say > for a value like 0.82? What kind of fraction is that? > Are you going to convert it to say > > a 82 hundredth of a page? > > Good luck with that... We used to spend quite a bit of time and effort worrying about correct grammar. However, these days, such politesse seems more honored in the breach, than the observance! >>> I'd only use 'page' if it was exactly 1. >>> >>> But since its a float you need to use an epsilon value. The points about floating-point's lack of accuracy in using binary to portray decimal values is worth noting. Few bother to teach/learn numerical analysis these days! That said, as someone who often has to 'budget' reading (either to finish, or to limit myself in order to 'do' things), I already 'round' my calculations. Thus, if I have five days before a book is due back to the library, a similar calculation galvanises/motivates me to read (say) at least three chapters per day. Pages/day would be a more precise measure but may not fit with the material! Sections of chapters? Also, if I end-up with only one chapter to read (remainder to integer division!) then that's a 'bonus' or even an additional measure of success - I don't complain about having worked 'too hard' during previous days. What might the added precision of page-fractions add - in the user's mind? >> I originally in my code did not use mixed quotes. But when I saved >> the file my new code formatting tyrant, Black, changed my consistent >> quoting style to mixed quotes. Apparently it insists on all double >> quotes unless there are embedded quotes within when it converts those >> to single quotes. > > Yukkkk! "Tyrant". Tools that are not your friends are what? Things growing in my garden which will not end-up 'in the pot' or on my table are called what? Yes, recommend using 'advisory' linters - you may then choose what to learn, when! and why! >>> 2) if a function isn't generally reusable make it obvious >>> by putting an underscore in front of the name. (or >>> make it generally reusable...usually much harder :-) >> >> This is where I was hoping for some concrete, helpful examples. I do >> indeed find this "much harder". > > Everyone does. Management routinely ask devs to create reusable code, > but studies show that writing reusable code is from 3-5 times more > expensive than writing single-use code. Some studies say more like 10 > times if you factor in maintenance costs too. > > And there is no easy answer, it takes painful lessons and > experience, and you'll still mess it up! YAGNI applies! NB OOP is not (necessarily) "reusable", nor does "reusable" imply OOP! Tested/testable code, over 'it seems to work', *every* time! Conversely to the above, management equally-routinely take stuff which we were initially told would only be used once or for a short period, and re-define those words to mean 'every day' and/or for decades... Elsewhere I threw in my 2c/2.5p ("tuppence ha'penny" in ?old-money) about "tests"... If the code has been written so that its intent and method are clear, then faced with the need, 'single-use' code could be re-worked and generalised into reusable code. Whereas a 'mess' might work for single-use, but is rapidly rated as 'disposable' should the situation 'develop'! Important message: you can't win! (but what made you think you could???) >>> 3) separate presentation and logic. >> Again, I find this not so easy. > This is easier and just takes a little practice to get used to it. > Basically every function should either do display or logic. If its > doing logic then return values that the caller can use/display. +1 See also, "Separation of Concerns" answer = process( validated_input, validated_input, ... ) report( answer ) >> FEATURE CREEP! Actually sounds like an interesting addition. But I >> don't much like eReaders myself. > > I didn't used to but, on holiday, I save a ton of weight by taking a > Kindle. And having got used to them I now occasionally even read > tech books on it. Although code formatting often gets messed up. > But I'm currently reading a book on curses programming in C and > converting it to python - maybe to become a page in my tutor > someday - and that's a Kindle book. I prefer a book. Recent studies have show that, given a choice of text-presentations, the majority of even today's, screen-native, tertiary-students will choose a book over an eBook. I live in a country famed for its bright sunlight* (I can never believe the difference between here and London or Washington, for example). Accordingly, it can be very difficult to read iPads, portable computer screens, smart-phones, etc. However, a Kindle works with reflective light! * and the highest rates of skin-cancers, which just goes to show: life's a beach, and then you... -- Regards =dn From gogonegro at gmail.com Mon Jan 6 06:19:12 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Mon, 6 Jan 2020 12:19:12 +0100 Subject: [Tutor] Please help disentangle my program logic In-Reply-To: <32d4b470-c050-eb32-b4de-948e7679a678@wichmann.us> References: <3b4dcc39-c49f-c13a-1a4a-bf4eec9f5b33@wichmann.us> <32d4b470-c050-eb32-b4de-948e7679a678@wichmann.us> <32d4b470-c050-eb32-b4de-948e7679a678@wichmann.us> Message-ID: As per Mats suggestion I am wrangling with the logging module. Almost there but I don?t understand what I?m doing wrong since I wish the log to go only into a specified log file but my current code spits out TWO different Log lines: A) an unwanted and strangely formatted log on the screen with lines such as INFO:__main__:DL:211.2;UL:29.8;PL:0 WARNING:__main__:DL:125.8;UL:29.6;PL:0 B) The properly formatted log lines in the specified log file. Here the format 2020-01-06 12:07:05;INFO;DL:211.2;UL:29.8;PL:0 2020-01-06 12:08:01;WARNING;DL:125.8;UL:29.6;PL:0 Here?s the code: import logging ?. # setup logging log_file_handler = logging.FileHandler('adsllog.log') log_formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s", "%Y-%m-%d %H:%M:%S") log_file_handler.setFormatter(log_formatter) logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # add file handler to logger logger.addHandler(log_file_handler) ? speedtest_values_string = ( f"DL:{down_speed:3.1f};UL:{up_speed:2.1f};PL:{packet_loss}" ) if down_speed < TD or up_speed < TU or packet_loss > TL: logger.warning(f"{speedtest_values_string}") else: logger.info(f"{speedtest_values_string}?) In the above code TD,TU,TL are numeric thresholds and down_speed, up_speed and packet_loss are my measurements of my ADSL line. Thank you for any clarification. Robert On 5 January 2020 at 19:34:51, Mats Wichmann (mats at wichmann.us) wrote: On 1/5/20 11:23 AM, Robert Alexander wrote: > Thank you Mats. Very interesting observation. > > May you kindly expand on why you are suggesting this? > > In my opinion specifying a log name at runtime gives me more flexibility > but I might be not considering some other important reason. Not sure I'm answering the right question here, but... You can still specify the logging location on the command line by accepting an argument, and having a default in the program if it's not on the command line. The logigng module gives you the flexibility to log to different places, even - you can put every reading in the log, and important events (like Internet connection is down) somewhere else - your console, sending by email (I'm not sure that's directly supported, but wouldn't be hard to set up), by assigning different log levels to different types of events. > > Ciao > > PS Etiquette question please: Is it better to reply to both the sender and > the list or the list only? > > > On 5 January 2020 at 19:18:30, Mats Wichmann (mats at wichmann.us) wrote: > > quick reaction: look into the logging module to control how, where and > what to log. No need to reinvent the wheel. _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From alan.gauld at yahoo.co.uk Mon Jan 6 06:29:57 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Mon, 6 Jan 2020 11:29:57 +0000 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> Message-ID: On 06/01/2020 03:25, boB Stepp wrote: > So I have been thinking a lot about this today and find myself > stumbling around in a confused daze despite making lists, crude > UML-like diagrams, etc. So questions! OK, This switches the emphasis of the thread but lets go... > I think the basic unit of code organization would be a ReadingMaterial > class, choosing this name to reflect Alan's thoughts that ultimately > there might be other things to read besides paper-based books. Good thinking. But... I'd create a very thin abstract class with just the interface - remember think about behaviour first not attributes. Attributes are only there to support behaviour. Then forget about eReaders etc for now and create a subclass that deals with the concrete problem of a paper book. > keep with the current confines of the original problem as stated, an > object of this class would have the following user-entered attributes: > title, total_amount_to_read, total_amount_read, and goal_date. I > switched from "pages" to "amount" as my best wording to reflect a > potential shift from pages read to percent of item read or lines read > or whatever. This means that I will have to throw in the concept of > "units" if I do this generalization now. The units are specific to te subclass. Amount are the generalized concept. My only question is whether the dates are relevant to the book? Or do you need a Reader object? It is the reader who has reading goals. The book will tell you how much to read and current position. But it is not interested in dates. So now we have two objects interacting. And in theory you could have multiple readers reading the same book, and one reader reading multiple books. > I know the Agile people > might say stick with today's problem (pages) and worry about other > units when one is forced to. So I may stick with pages after I hash > out more important difficulties. So back to "pages" for now. Correct approach. Recognize that you may have another type of ReadingMaterial to deal with in the future, but park it for now - its a differnt class. > First point of confusion: the creation of the ReadingMaterial object. > Should it start with (a) my_book = ReadingMaterial() or with (b) > my_book = Reading_Material(title [, total_pages_to_read, > total_pages_read, goal_date])? The brackets indicate optionally > starting with all four attributes from the get-go. In other words in > (a) the object would see to initiating the acquisition of user input > itself, while the second has some other entity taking care of user > input and then passing it into the instantiation process. Separate user interaction from logic. What happens when you want to use this ReadingMaterial object in a GUI? So you have a ReadingMaterialView that can deal with interacting with the user to create the book. In an industrial version of this we probably have a database of readMaterial and the readers select an existing book. The creation of the book is a database(library?) admins job. So I'd definitely opt for a separate data collection function/object and then instantiate the object with the data already gathered. > the proper way to proceed? On the (a) side, ReadingMaterial knows > what it needs and it is the natural location for future additional > attributes that may need to be acquired someday. It knows what it needs but it does that by being created with it. Thats what __init__() - ie. initialization - is for. > direction I am currently leaning. So the initialization of a > ReadingMaterial object might look like: > > class ReadingMaterial: > def __init__(self): > self.title = _get_input(???) > self.total_pages_to_read = _get_input(???) > self.total_pages_read = _get_input(???) > self.goal_date = _get_input(???) Again, how does that work in a GUI? Or on the web? > Based on my previous code explorations, I need to do two forms of > input validation: (1) syntactical, so no ValueErrors result Thats the job of the user interaction mechanism... > logical: Things like the number of pages read are less than the > length of what is being read, etc. That's the job of the book. It should check the logical validity of the data and if necessary raise a ValueError. > InputManager (yet to be fleshed out) should do (1) and ReadingMaterial > should do (2), especially as it cannot be done until *all* of the > input is acquired. So what about (???) ? Only ReadingMaterial knows > about the details of each attribute, especially validating the "type" > of input. The "input manager" knows the types required - that's part of the creation API. So to create the object it must provide the required types. > Also, it seems natural that in case of type validation > exception being thrown, the resulting message to the user should be > customized to the input being asked for. The message can be added to the error when it is raised, so it is type/value specific. > such error messages should be stored in the ReadingMaterial class. They are "stored" wherever you raise the exception. (Again, being truly industrial, you store them in a separate list and use i18n code to extract them in whatever language/locale is in use. But that's way beyond the scope of this exercise!! And outside of the class in any case.) > Additionally, the input prompt for each attribute to be acquired needs > to be customized appropriately as well. And if I am engaging in > "good" OOD thinking then that suggest that (???) should minimally be > the input type being requested (str -- not really an issue since > input() returns a string, int, date object, etc), the customized input > prompt and the customized error message. I presume good practice > would for the InputManager to pass the input prompt to a > DisplayManager to be printed to the user's screen and that the > InputManager would convert the user input to the requested (by > ReadingMaterial) type, syntactically validate it, if that fails send > the error message to the DisplayManager and await a new effort at > valid input. Is this how I should be thinking about this problem? What you are working towards is the classic Model-View-Controller architecture. Google MVC! In essence the model is the logic and core data. The view is what goes n the screen The controller gets input and passes it to the view and/or model. There are a ton of variations on this theme but its all about keeping these elements decoupled. And you are right to be struggling, it is hard, and that's why there is a standard model. Personally I tend to favour the simplified version where you only have a Model and View. The View does all the display/interaction and the Model does all the application stuff. Understand that views are application specific and therefore not usually reusable, whereas models should be reusable. So design the dependencies such that the view depends on the model, not the other way round. That way you can create different views for CLI, GUI and web, with no change to the model. > *If* I am on the right track, where does the InputManager get > instantiated? DisplayManager? Is this where in the MVC pattern, the > program's main() would instantiate a Controller and then the > Controller would be responsible for coordinating these? I am answering your issues as I go and in each case you have already guessed the response. I think thats a good thing! :-) > Anyway this is where I am at now staring at a mostly empty split > Neovim screen, split between reading_tracker.py (Name change per Alan > ~(:>)) ) and test_reading_tracker.py with > > ============================================================================ > #!/usr/bin/env python3 > """Pages per day needed to complete a reading on time. > > [Usage message] > > [Third-party or custom imports] > > [A list of any classes, exception, functions, and any other objects > exported by the module] > class ReadingMaterial > > """ > > class ReadingMaterial: > """A class to represent reading material such as books.""" > > pass > > > if __name__ == "__main__": > pass > ============================================================================ That's a good start. Now start writing some usecases. Forget about code for now. How does the user interact with this application? That should start to give you clues as to what methods the classes need to support. As a start the context diagram consists of the SUD with Reader and ReadingMaterial/PaperBook objects.(Assume the MVC objects are all part of the same thing for use-case purposes) The only(?) Actors are the Admin and User. Users are associated with a reader which is associated with 0..N books Books are set up by Admin. Depending on your vision Readers (or ReadingGoals? or ReadingProjects?) may beset up by admin (if it was a book-club or Language class maybe) or by the users. And a user and admin could be different roles of a single person. It is really important to have it clear in your head how this app will work as a dialog with the user. That is fundamental to understanding what operations it needs to support. And operations translate to methods. And then you decide which object supports which operations. That in turn will tell you which attributes are needed in which class. -- 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 From cs at cskk.id.au Mon Jan 6 17:42:51 2020 From: cs at cskk.id.au (Cameron Simpson) Date: Tue, 7 Jan 2020 09:42:51 +1100 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: Message-ID: <20200106224251.GA74763@cskk.homeip.net> On 06Jan2020 11:29, Alan Gauld wrote: >On 06/01/2020 03:25, boB Stepp wrote: >> I think the basic unit of code organization would be a >> ReadingMaterial >> class, choosing this name to reflect Alan's thoughts that ultimately >> there might be other things to read besides paper-based books. > >Good thinking. But... > >I'd create a very thin abstract class with just the interface > - remember think about behaviour first not attributes. >Attributes are only there to support behaviour. > >Then forget about eReaders etc for now and create a subclass >that deals with the concrete problem of a paper book. Just to this; I often find it easier to write the concrete class (no abstract superclass), and _then_ consider the concrete class and pull out the abstract bits of it. So you start with a working: class SubClass: def __init__(self, total, blah...): self.units = 'pages' self.sofar = 0 self.total = total self.blah = blah ... def portion(self): return self.sofar / self.total and later, when working, split it off into a common superclass with a specific subclass, dividing things up between what is generic and what is specific. class SuperClass: def __init__(self, total, units, blah...): self.units = units self.sofar = 0 self.total = total self.blah = blah ... def portion(self): return self.sofar / self.total class PagesSubClass(SuperClass): def __init__(self, total, blah...): super().__init__(total, 'pages', blah...) Cheers, Cameron Simpson From akleider at sonic.net Mon Jan 6 19:55:43 2020 From: akleider at sonic.net (Alex Kleider) Date: Mon, 06 Jan 2020 16:55:43 -0800 Subject: [Tutor] how best to remove an item from a dict Message-ID: <8e2e92530cda46bcc4376eec6b4f0f3d@sonic.net> Which is best: >>> _ = d.pop(key) or >>> del d[key] ?? >>> import this ... There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. ... Cheers, -- Alex Kleider (sent from my current gizmo) From alan.gauld at yahoo.co.uk Mon Jan 6 20:33:14 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Tue, 7 Jan 2020 01:33:14 +0000 Subject: [Tutor] how best to remove an item from a dict In-Reply-To: <8e2e92530cda46bcc4376eec6b4f0f3d@sonic.net> References: <8e2e92530cda46bcc4376eec6b4f0f3d@sonic.net> Message-ID: On 07/01/2020 00:55, Alex Kleider wrote: > > Which is best: > > >>> _ = d.pop(key) > or > >>> del d[key] > ?? > > >>> import this > .. > There should be one-- and preferably only one --obvious way to do it. > Although that way may not be obvious at first unless you're Dutch. The second one is best, it clearly describes the intended effect. The first one uses a side-effect of the pop/assignment operation, its only when the _ changes value that the dict item goes out of scope and is deleted. A clearly defined operation is always better than a disguised one. Especially from a maintenance point of view, and given maintenance is the most expensive part of programming, that is critical. -- 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 From PyTutor at DancesWithMice.info Mon Jan 6 23:31:06 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Tue, 7 Jan 2020 17:31:06 +1300 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> Message-ID: <345ffab0-b6ed-b1ef-3dd9-50bc0d0af2fd@DancesWithMice.info> On 7/01/20 12:29 AM, Alan Gauld via Tutor wrote: > On 06/01/2020 03:25, boB Stepp wrote: > >> So I have been thinking a lot about this today and find myself >> stumbling around in a confused daze despite making lists, crude >> UML-like diagrams, etc. So questions! > > OK, This switches the emphasis of the thread but lets go... Maybe, but doesn't *some* of the discussion relate to design decisions? >> I think the basic unit of code organization would be a ReadingMaterial >> class, choosing this name to reflect Alan's thoughts that ultimately >> there might be other things to read besides paper-based books. > > Good thinking. But... > > I'd create a very thin abstract class with just the interface > - remember think about behaviour first not attributes. > Attributes are only there to support behaviour. > > Then forget about eReaders etc for now and create a subclass > that deals with the concrete problem of a paper book. > > >> keep with the current confines of the original problem as stated, an >> object of this class would have the following user-entered attributes: >> title, total_amount_to_read, total_amount_read, and goal_date. I >> switched from "pages" to "amount" as my best wording to reflect a >> potential shift from pages read to percent of item read or lines read >> or whatever. This means that I will have to throw in the concept of >> "units" if I do this generalization now. > > The units are specific to te subclass. Amount are the generalized concept. > > My only question is whether the dates are relevant to the book? Or do > you need a Reader object? It is the reader who has reading goals. The > book will tell you how much to read and current position. But it is not > interested in dates. Not necessarily. The reader doesn't know if (s)he is about to drop dead! Alternately, if the book has been borrowed (eg library), then it has a finite availability. > So now we have two objects interacting. And in theory you could have > multiple readers reading the same book, and one reader reading multiple > books. > >> I know the Agile people >> might say stick with today's problem (pages) and worry about other >> units when one is forced to. So I may stick with pages after I hash >> out more important difficulties. So back to "pages" for now. > > Correct approach. Recognize that you may have another type of > ReadingMaterial to deal with in the future, but park it for > now - its a differnt class. +1 Make it work before you make it better! >> First point of confusion: the creation of the ReadingMaterial object. >> Should it start with (a) my_book = ReadingMaterial() or with (b) >> my_book = Reading_Material(title [, total_pages_to_read, >> total_pages_read, goal_date])? The brackets indicate optionally >> starting with all four attributes from the get-go. In other words in >> (a) the object would see to initiating the acquisition of user input >> itself, while the second has some other entity taking care of user >> input and then passing it into the instantiation process. > > Separate user interaction from logic. What happens when you want to use > this ReadingMaterial object in a GUI? So you have a ReadingMaterialView > that can deal with interacting with the user to create the book. In an > industrial version of this we probably have a database of readMaterial > and the readers select an existing book. The creation of the book is a > database(library?) admins job. > > So I'd definitely opt for a separate data collection function/object > and then instantiate the object with the data already gathered. +1, which aligns with earlier mention of MVC Once again, keeping in-mind the future possibility of using a GUI, first write the code to work from the terminal-only. >> the proper way to proceed? On the (a) side, ReadingMaterial knows >> what it needs and it is the natural location for future additional >> attributes that may need to be acquired someday. > > It knows what it needs but it does that by being created with it. Thats > what __init__() - ie. initialization - is for. > >> direction I am currently leaning. So the initialization of a >> ReadingMaterial object might look like: >> >> class ReadingMaterial: >> def __init__(self): >> self.title = _get_input(???) >> self.total_pages_to_read = _get_input(???) >> self.total_pages_read = _get_input(???) >> self.goal_date = _get_input(???) > > Again, how does that work in a GUI? Or on the web? Alternate view: Were the get_input() coded as part of ReadingMaterial, how would we be able to re-use the method when needing to input into some other class, on some other project, some other day, ... > I am answering your issues as I go and in each case you have > already guessed the response. I think thats a good thing! :-) +1 > Now start writing some usecases. Forget about code for now. > How does the user interact with this application? > That should start to give you clues as to what methods > the classes need to support. ... > It is really important to have it clear in your head how > this app will work as a dialog with the user. That is fundamental to > understanding what operations it needs to support. And operations > translate to methods. And then you decide which object supports > which operations. That in turn will tell you which attributes are > needed in which class. +1 -- Regards =dn From akleider at sonic.net Tue Jan 7 02:20:45 2020 From: akleider at sonic.net (Alex Kleider) Date: Mon, 06 Jan 2020 23:20:45 -0800 Subject: [Tutor] how best to remove an item from a dict In-Reply-To: References: <8e2e92530cda46bcc4376eec6b4f0f3d@sonic.net> Message-ID: <4d9d91e4e0f536d79ce67e3095196104@sonic.net> On 2020-01-06 17:33, Alan Gauld via Tutor wrote: > On 07/01/2020 00:55, Alex Kleider wrote: >> >> Which is best: >> >> >>> _ = d.pop(key) >> or >> >>> del d[key] >> ?? > > The second one is best, it clearly describes the intended effect. > > The first one uses a side-effect of the pop/assignment operation, > its only when the _ changes value that the dict item goes out > of scope and is deleted. > > A clearly defined operation is always better than a disguised one. > Especially from a maintenance point of view, and given maintenance > is the most expensive part of programming, that is critical. How obvious it seems now that you've explained it! Thanks, Alan From alan.gauld at yahoo.co.uk Tue Jan 7 05:37:36 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Tue, 7 Jan 2020 10:37:36 +0000 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: <345ffab0-b6ed-b1ef-3dd9-50bc0d0af2fd@DancesWithMice.info> References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <3364d7bd-482f-f538-da1f-90839ccaa493@DancesWithMice.info> <345ffab0-b6ed-b1ef-3dd9-50bc0d0af2fd@DancesWithMice.info> Message-ID: On 07/01/2020 04:31, David L Neil via Tutor wrote: >> My only question is whether the dates are relevant to the book? Or do >> you need a Reader object? It is the reader who has reading goals. The >> book will tell you how much to read and current position. But it is not >> interested in dates. > > Not necessarily. The reader doesn't know if (s)he is about to drop dead! > > Alternately, if the book has been borrowed (eg library), then it has a > finite availability. That's true and raises a moot point. If building a library app its possible to have a LendableItem class from which all loanable objects derive, Or you can create a Loanable mixin that is added to all loanable stock. But it is also possible to have a Loan class associated with a StockItem... All 3 approaches work. My preference is for a separate Loan since it avoids having twin versions of every kind of library item - loanable and reference... A Loan captures the transitive relationship between a borrower and a StockItem. It is responsible for monitoring duration, issuing notifications, raising fines etc. -- 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 From cmcgon at berkeley.edu Tue Jan 7 06:00:41 2020 From: cmcgon at berkeley.edu (Casey McGonigle) Date: Tue, 7 Jan 2020 01:00:41 -1000 Subject: [Tutor] openpyxl module not recognized Message-ID: Hello, I'm working on a project using pandas DataFrames that I'd like to export to excel. I'm using Mac OS Mojave. I understand the way to do this is through the df.to_excel() function, which requires the openpyxl module. As such, I've installed the module (using pip install openpyxl) -- my terminal tells me it was successfully installed and it shows up in my finder. However, when I run the df.to_excel("excel file") function, I get a ModuleNotFoundError: No module named 'openpyxl'. Please advise how to remedy this incongruity -- UC Berkeley Class of 2021 From arj.python at gmail.com Tue Jan 7 07:11:21 2020 From: arj.python at gmail.com (Abdur-Rahmaan Janhangeer) Date: Tue, 7 Jan 2020 16:11:21 +0400 Subject: [Tutor] openpyxl module not recognized In-Reply-To: References: Message-ID: If you used only pip install there's a possibility pip installed it in another python version Retry with python -m pip install ... Then see. On Tue, 7 Jan 2020, 15:04 Casey McGonigle, wrote: > Hello, > > I'm working on a project using pandas DataFrames that I'd like to export to > excel. I'm using Mac OS Mojave. I understand the way to do this is through > the df.to_excel() function, which requires the openpyxl module. > > As such, I've installed the module (using pip install openpyxl) -- my > terminal tells me it was successfully installed and it shows up in my > finder. > > However, when I run the df.to_excel("excel file") function, I get a > ModuleNotFoundError: No module named 'openpyxl'. > > Please advise how to remedy this incongruity > > -- > UC Berkeley > Class of 2021 > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > From deepakdixit0001 at gmail.com Tue Jan 7 09:44:45 2020 From: deepakdixit0001 at gmail.com (Deepak Dixit) Date: Tue, 7 Jan 2020 20:14:45 +0530 Subject: [Tutor] How memory works in Python Message-ID: Hi Team, I was thinking on the memory management in Python but little confused here because when I assign same value of type number and string to different variable, it points to same memory but when I do it for list or tuple it points to different memory locations. Here is the code and the output: # test.py x1 = 10 y1 = 10 x2 = "Hello" y2 = "Hello" x3 = [10] y3 = [10] x4 = (10,) y4 = (10,) print id(x1) == id(y1) print id(x2) == id(y2) print id(x3) == id(y3) print id(x4) == id(y4) deepak at Deepak-PC:~$ python test.py True True False False Can you help me to understand how it works? -- *With Regards,* *Deepak Kumar Dixit* From mats at wichmann.us Tue Jan 7 10:06:46 2020 From: mats at wichmann.us (Mats Wichmann) Date: Tue, 7 Jan 2020 08:06:46 -0700 Subject: [Tutor] How memory works in Python In-Reply-To: References: Message-ID: <3e399a67-d6b0-a719-b479-f7afcf5029dd@wichmann.us> On 1/7/20 7:44 AM, Deepak Dixit wrote: > Hi Team, > > I was thinking on the memory management in Python but little confused here > because when I assign same value of type number and string to different > variable, it points to same memory but when I do it for list or tuple it > points to different memory locations. > > Here is the code and the output: > > # test.py > x1 = 10 > y1 = 10 > > x2 = "Hello" > y2 = "Hello" > > x3 = [10] > y3 = [10] > > x4 = (10,) > y4 = (10,) > > print id(x1) == id(y1) > print id(x2) == id(y2) > print id(x3) == id(y3) > print id(x4) == id(y4) > > deepak at Deepak-PC:~$ python test.py > True > True > False > False > > > Can you help me to understand how it works? quite simply, if works as you expect, but with some optimizations. Turns out small integers are cached in CPython - they reuse the same object. The current range of that is something like up to 256 (I think a small number of negative integers also have this happen). Try that experiment again with bigger numbers. Similarly for strings, small strings are interned, try it with a considerably longer string. You can cause the string interning to happen yourself with the intern function, see this example: >>> y1 = "Hello, this is a much longer string" >>> y2 = "Hello, this is a much longer string" >>> print(id(y1) == id(y2)) False >>> import sys >>> y1 = sys.intern("Hello, this is a much longer string") >>> y2 = sys.intern("Hello, this is a much longer string") >>> print(id(y1) == id(y2)) True >>> From PyTutor at DancesWithMice.info Tue Jan 7 14:04:45 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Wed, 8 Jan 2020 08:04:45 +1300 Subject: [Tutor] How memory works in Python In-Reply-To: References: Message-ID: <304f6b6a-ad2e-cce4-dfd7-3ee94f68b73d@DancesWithMice.info> On 8/01/20 3:44 AM, Deepak Dixit wrote: > Hi Team, > > I was thinking on the memory management in Python but little confused here > because when I assign same value of type number and string to different > variable, it points to same memory but when I do it for list or tuple it > points to different memory locations. > > Here is the code and the output: > > # test.py > x1 = 10 > y1 = 10 > > x2 = "Hello" > y2 = "Hello" > > x3 = [10] > y3 = [10] > > x4 = (10,) > y4 = (10,) > > print id(x1) == id(y1) > print id(x2) == id(y2) > print id(x3) == id(y3) > print id(x4) == id(y4) > > deepak at Deepak-PC:~$ python test.py > True > True > False > False > > > Can you help me to understand how it works? You may find http://pythontutor.com/visualize.html#mode=edit helpful. NB no formal relationship with this list (to my knowledge) It is designed as a learning aid by illustrating storage allocations and usage. -- Regards =dn From cmcgon at berkeley.edu Tue Jan 7 15:31:34 2020 From: cmcgon at berkeley.edu (Casey McGonigle) Date: Tue, 7 Jan 2020 10:31:34 -1000 Subject: [Tutor] openpyxl module not recognized In-Reply-To: References: Message-ID: I've done this -- it gives me the message Requirement already satisfied: openpyxl in /anaconda3/lib/python3.7/site-packages (3.0.2) anything else that could be causing the issue? On Tue, Jan 7, 2020 at 2:11 AM Abdur-Rahmaan Janhangeer < arj.python at gmail.com> wrote: > If you used only pip install there's a possibility pip installed it in > another python version > > Retry with python -m pip install ... > > Then see. > > On Tue, 7 Jan 2020, 15:04 Casey McGonigle, wrote: > >> Hello, >> >> I'm working on a project using pandas DataFrames that I'd like to export >> to >> excel. I'm using Mac OS Mojave. I understand the way to do this is through >> the df.to_excel() function, which requires the openpyxl module. >> >> As such, I've installed the module (using pip install openpyxl) -- my >> terminal tells me it was successfully installed and it shows up in my >> finder. >> >> However, when I run the df.to_excel("excel file") function, I get a >> ModuleNotFoundError: No module named 'openpyxl'. >> >> Please advise how to remedy this incongruity >> >> -- >> UC Berkeley >> Class of 2021 >> _______________________________________________ >> Tutor maillist - Tutor at python.org >> To unsubscribe or change subscription options: >> https://mail.python.org/mailman/listinfo/tutor >> > -- UC Berkeley Class of 2021 From alan.gauld at yahoo.co.uk Tue Jan 7 20:34:17 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Wed, 8 Jan 2020 01:34:17 +0000 Subject: [Tutor] openpyxl module not recognized In-Reply-To: References: Message-ID: On 07/01/2020 20:31, Casey McGonigle wrote: > I've done this -- it gives me the message > > Requirement already satisfied: openpyxl in > /anaconda3/lib/python3.7/site-packages (3.0.2) ... >>> However, when I run the df.to_excel("excel file") function, I get a >>> ModuleNotFoundError: No module named 'openpyxl'. >>> Please send the full command line used to launch the program plus the full error trace you received, not just the last lines. -- 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 From sarfraaz at gmail.com Wed Jan 8 00:36:12 2020 From: sarfraaz at gmail.com (Sarfraaz Ahmed) Date: Wed, 8 Jan 2020 11:06:12 +0530 Subject: [Tutor] openpyxl module not recognized In-Reply-To: References: Message-ID: Hello Casey, Am taking a wild guess and assuming that you are executing your code from some sort of an editor or some such and may be that editor is not picking the right version of Python. If that is really the case, then I found a comment on stackoverflow that talks about solving it. The relevant comment for your scenario : "check your python version first, then refer to this answer : https://stackoverflow.com/questions/23161604/how-to-set-which-version-of-python-sublime-text-uses " The original question : https://stackoverflow.com/questions/44223621/no-module-named-openpyxl-python-3-6-osx Hope that helps. On Tue, Jan 7, 2020 at 4:35 PM Casey McGonigle wrote: > Hello, > > I'm working on a project using pandas DataFrames that I'd like to export to > excel. I'm using Mac OS Mojave. I understand the way to do this is through > the df.to_excel() function, which requires the openpyxl module. > > As such, I've installed the module (using pip install openpyxl) -- my > terminal tells me it was successfully installed and it shows up in my > finder. > > However, when I run the df.to_excel("excel file") function, I get a > ModuleNotFoundError: No module named 'openpyxl'. > > Please advise how to remedy this incongruity > > -- > UC Berkeley > Class of 2021 > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > -- Thanks -- Sarfraaz Ahmed From cal.97g at gmail.com Wed Jan 8 05:28:20 2020 From: cal.97g at gmail.com (Cal97g .) Date: Wed, 8 Jan 2020 10:28:20 +0000 Subject: [Tutor] How memory works in Python In-Reply-To: References: Message-ID: What is happening is that python sees you have some small variables which are identical. As these variables are immutable python knows they cannot be changed. Instead of creating a new memory location for each variable, python references the same memory location for both items. This is why your the id for (x1, y1) + (x2, y2) is identical. Many Thanks Callam Delaney On Tue, 7 Jan 2020 at 14:45, Deepak Dixit wrote: > Hi Team, > > I was thinking on the memory management in Python but little confused here > because when I assign same value of type number and string to different > variable, it points to same memory but when I do it for list or tuple it > points to different memory locations. > > Here is the code and the output: > > # test.py > x1 = 10 > y1 = 10 > > x2 = "Hello" > y2 = "Hello" > > x3 = [10] > y3 = [10] > > x4 = (10,) > y4 = (10,) > > print id(x1) == id(y1) > print id(x2) == id(y2) > print id(x3) == id(y3) > print id(x4) == id(y4) > > deepak at Deepak-PC:~$ python test.py > True > True > False > False > > > Can you help me to understand how it works? > -- > > > *With Regards,* > *Deepak Kumar Dixit* > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > From deepakdixit0001 at gmail.com Wed Jan 8 05:49:06 2020 From: deepakdixit0001 at gmail.com (Deepak Dixit) Date: Wed, 8 Jan 2020 16:19:06 +0530 Subject: [Tutor] How memory works in Python In-Reply-To: References: Message-ID: As you can see in the code, we are getting different location for tuple which is also immutable. So it should not be dependent on type of variable. Anyway, Thanks for your help. On Wed, Jan 8, 2020, 3:58 PM Cal97g . wrote: > What is happening is that python sees you have some small variables which > are identical. As these variables are immutable python knows they cannot be > changed. > > Instead of creating a new memory location for each variable, python > references the same memory location for both items. > > This is why your the id for (x1, y1) + (x2, y2) is identical. > > Many Thanks > > Callam Delaney > > > On Tue, 7 Jan 2020 at 14:45, Deepak Dixit > wrote: > >> Hi Team, >> >> I was thinking on the memory management in Python but little confused here >> because when I assign same value of type number and string to different >> variable, it points to same memory but when I do it for list or tuple it >> points to different memory locations. >> >> Here is the code and the output: >> >> # test.py >> x1 = 10 >> y1 = 10 >> >> x2 = "Hello" >> y2 = "Hello" >> >> x3 = [10] >> y3 = [10] >> >> x4 = (10,) >> y4 = (10,) >> >> print id(x1) == id(y1) >> print id(x2) == id(y2) >> print id(x3) == id(y3) >> print id(x4) == id(y4) >> >> deepak at Deepak-PC:~$ python test.py >> True >> True >> False >> False >> >> >> Can you help me to understand how it works? >> -- >> >> >> *With Regards,* >> *Deepak Kumar Dixit* >> _______________________________________________ >> Tutor maillist - Tutor at python.org >> To unsubscribe or change subscription options: >> https://mail.python.org/mailman/listinfo/tutor >> > From niharika1883 at gmail.com Wed Jan 8 07:56:16 2020 From: niharika1883 at gmail.com (Niharika Jakhar) Date: Wed, 8 Jan 2020 13:56:16 +0100 Subject: [Tutor] Query regarding file conversion - DOT files Message-ID: Hello I am a masters student, and beginner in python programming. I seek help and guidance from the tutors here. Thanks in advance. Problem: Convert DOT files to PDF. I came across pydot for this and tried following: #####code#### import os import pydot (graph,) = pydot.graph_from_dot_file('hello.dot') graph.write_png('hellow.png') Error: assert process.returncode == 0, process.returncode AssertionError: 1 Python version: 3.7.4 Development environment: Spyder Best regards Niharika From gogonegro at gmail.com Wed Jan 8 12:28:02 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Wed, 8 Jan 2020 09:28:02 -0800 Subject: [Tutor] Query regarding file conversion - DOT files In-Reply-To: References: Message-ID: Should?nt the line be graph = pydot.graph_from_dot_file('hello.dot?) ? HTH Robert On 8 January 2020 at 18:25:30, Niharika Jakhar (niharika1883 at gmail.com) wrote: Hello I am a masters student, and beginner in python programming. I seek help and guidance from the tutors here. Thanks in advance. Problem: Convert DOT files to PDF. I came across pydot for this and tried following: #####code#### import os import pydot (graph,) = pydot.graph_from_dot_file('hello.dot') graph.write_png('hellow.png') Error: assert process.returncode == 0, process.returncode AssertionError: 1 Python version: 3.7.4 Development environment: Spyder Best regards Niharika _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From alan.gauld at yahoo.co.uk Wed Jan 8 12:42:15 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Wed, 8 Jan 2020 17:42:15 +0000 Subject: [Tutor] Query regarding file conversion - DOT files In-Reply-To: References: Message-ID: <042e0a76-199f-f95e-27cf-530e93f956e4@yahoo.co.uk> On 08/01/2020 12:56, Niharika Jakhar wrote: > I came across pydot for this and tried following: > #####code#### > import os > import pydot > (graph,) = pydot.graph_from_dot_file('hello.dot') > graph.write_png('hellow.png') > > Error: > assert process.returncode == 0, process.returncode > > AssertionError: 1 Please, always post the full error trace, do not summarize or shorten it. They contain a lot of useful detail and without it we are only making guesses. Also, for specialist libraries that are not part of the python core, it is always worth contacting the package maintainer or support forum. They will have more experience than us. -- 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 From mats at wichmann.us Wed Jan 8 12:52:34 2020 From: mats at wichmann.us (Mats Wichmann) Date: Wed, 8 Jan 2020 10:52:34 -0700 Subject: [Tutor] Query regarding file conversion - DOT files In-Reply-To: <042e0a76-199f-f95e-27cf-530e93f956e4@yahoo.co.uk> References: <042e0a76-199f-f95e-27cf-530e93f956e4@yahoo.co.uk> Message-ID: <3c28d846-9e94-c3dd-3928-2e3f8e16a379@wichmann.us> On 1/8/20 10:42 AM, Alan Gauld via Tutor wrote: > On 08/01/2020 12:56, Niharika Jakhar wrote: > >> I came across pydot for this and tried following: >> #####code#### >> import os >> import pydot >> (graph,) = pydot.graph_from_dot_file('hello.dot') >> graph.write_png('hellow.png') >> >> Error: >> assert process.returncode == 0, process.returncode >> >> AssertionError: 1 > > Please, always post the full error trace, do not summarize > or shorten it. They contain a lot of useful detail and > without it we are only making guesses. what he said :) Is graphviz installed? I believe pydot and its descendants (like pydotplus) require an installation of graphviz, which does not come in when you install pydot*. if the failure comes from the graph.write_png call - we can't tell because the proper error trace is missing - this may well be the issue, it's either not installed, or not found. From niharika1883 at gmail.com Wed Jan 8 12:38:12 2020 From: niharika1883 at gmail.com (Niharika Jakhar) Date: Wed, 8 Jan 2020 18:38:12 +0100 Subject: [Tutor] Query regarding file conversion - DOT files In-Reply-To: References: Message-ID: No, it doesn't work for me. Error: graph.write_png('hellow.png') AttributeError: 'list' object has no attribute 'write_png' On Wed, Jan 8, 2020 at 6:28 PM Robert Alexander wrote: > Should?nt the line be > > graph = pydot.graph_from_dot_file('hello.dot?) > > ? > > HTH > Robert > > > On 8 January 2020 at 18:25:30, Niharika Jakhar (niharika1883 at gmail.com) > wrote: > > Hello > > I am a masters student, and beginner in python programming. I seek help > and > guidance from the tutors here. Thanks in advance. > > Problem: Convert DOT files to PDF. > > I came across pydot for this and tried following: > #####code#### > import os > import pydot > (graph,) = pydot.graph_from_dot_file('hello.dot') > graph.write_png('hellow.png') > > Error: > assert process.returncode == 0, process.returncode > > AssertionError: 1 > > > > Python version: 3.7.4 > Development environment: Spyder > > Best regards > Niharika > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > > From alan.gauld at yahoo.co.uk Wed Jan 8 16:25:30 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Wed, 8 Jan 2020 21:25:30 +0000 Subject: [Tutor] Query regarding file conversion - DOT files In-Reply-To: References: Message-ID: On 08/01/2020 17:38, Niharika Jakhar wrote: > No, it doesn't work for me. > Error: > graph.write_png('hellow.png') > > AttributeError: 'list' object has no attribute 'write_png' Please post the full error trace, not just the last line. Also note that Robert suggested changing the tuple on the left side, not the function call: >> graph = pydot.graph_from_dot_file('hello.dot?) -- 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 From mats at wichmann.us Wed Jan 8 17:16:36 2020 From: mats at wichmann.us (Mats Wichmann) Date: Wed, 8 Jan 2020 15:16:36 -0700 Subject: [Tutor] openpyxl module not recognized In-Reply-To: References: Message-ID: <2a128815-68cc-347a-3396-7cdde0711169@wichmann.us> On 1/7/20 1:31 PM, Casey McGonigle wrote: > I've done this -- it gives me the message > > Requirement already satisfied: openpyxl in > /anaconda3/lib/python3.7/site-packages (3.0.2) > > anything else that could be causing the issue? >>> I'm working on a project using pandas DataFrames that I'd like to export >>> to >>> excel. I'm using Mac OS Mojave. I understand the way to do this is through >>> the df.to_excel() function, which requires the openpyxl module. >>> >>> As such, I've installed the module (using pip install openpyxl) -- my >>> terminal tells me it was successfully installed and it shows up in my >>> finder. >>> >>> However, when I run the df.to_excel("excel file") function, I get a >>> ModuleNotFoundError: No module named 'openpyxl'. The good news is we know exactly what's wrong. That message always means a path problem, one of two possibilities: - the module is not installed. that is, there is no path at all on the system that would work to import it. - or, the module is installed, but the python instance you're using doesn't know the path to it. You don't want a history lesson, but there was a time when if you had python, everything was attached to that Python, and it was a little simpler. These days, odds are pretty good you have more than one, and ones you have may or may not have several distinct instances (virtualenvs). The message at the top indicates you have a separate distribution via Anaconda, so if you've installed a regular Python.org python, or are running Linux or Mac (which come with a system-provided version), in which case you have at least two. I'm not an Anaconda user, but I believe Conda is quite aggressive in setting up virtualenvs. For any given invocation of Python, here's how you see where it knows to look: >>> import sys >>> print(sys.path) One persistent problem we're seeing a lot of these days is that instructions for things say to do pip install this-wonderful-package If you have several Pythons installed, how can you know which Python pip is going to install for? Therefore, it's usually worth modifying that instruction they have given you and invoke it this way: python -m pip install this-wonderful-package then whatever is installed matches the python you invoked that command with. However... if you're using Anaconda, it has its own installation system and doesn't install using pip, but you also indicated you used pip to install it, so at this point I'm pretty confused. Conda should be able to get things right - but then you have to invoke the same Python as it does. This is without even getting into the complication of virtualenvs. So the challenge is to get things to match. For whichever Python you used that gave you the failure, try this: python -m pip list and see if it lists openpyxl or invoke it interactively and try the import: >>> import openpyxl Hope this gets you at least a little closer to understanding, if not solving. >>> UC Berkeley >>> Class of 2021 Cheers! -- mats, UC Berkeley Class of Long Ago (well, okay, 1981) From zebra05 at gmail.com Mon Jan 13 08:33:43 2020 From: zebra05 at gmail.com (S D) Date: Mon, 13 Jan 2020 15:33:43 +0200 Subject: [Tutor] Imports (Py 3) Message-ID: I have a folder `abc_app`, which contains a module `utils`. In the `abc_app` folder, I also have a subfolder `tests` which contains a module `test_api` How do I import a function from the `utils` module from the `test_api` module? Kind regards, - SD From mats at wichmann.us Mon Jan 13 09:49:24 2020 From: mats at wichmann.us (Mats Wichmann) Date: Mon, 13 Jan 2020 07:49:24 -0700 Subject: [Tutor] Imports (Py 3) In-Reply-To: References: Message-ID: <351d2a55-c3da-dbcf-40d5-03d91654f6a3@wichmann.us> On 1/13/20 6:33 AM, S D wrote: > I have a folder `abc_app`, which contains a module `utils`. In the > `abc_app` folder, I also have a subfolder `tests` which contains a module > `test_api` > > How do I import a function from the `utils` module from the `test_api` > module? "relative imports". which can be vaguely confusing, it's worth reading a tutorial on them to be sure you understand. if "foo" is in "utils", what you described ought to look like: from ..utils import foo (unless I managed to confuse myself!) From akleider at sonic.net Mon Jan 13 16:59:42 2020 From: akleider at sonic.net (Alex Kleider) Date: Mon, 13 Jan 2020 13:59:42 -0800 Subject: [Tutor] Imports (Py 3) In-Reply-To: <351d2a55-c3da-dbcf-40d5-03d91654f6a3@wichmann.us> References: <351d2a55-c3da-dbcf-40d5-03d91654f6a3@wichmann.us> Message-ID: <9ac1a64c4284268735c372d345914553@sonic.net> On 2020-01-13 06:49, Mats Wichmann wrote: > On 1/13/20 6:33 AM, S D wrote: >> I have a folder `abc_app`, which contains a module `utils`. In the >> `abc_app` folder, I also have a subfolder `tests` which contains a >> module >> `test_api` >> >> How do I import a function from the `utils` module from the `test_api` >> module? > > "relative imports". which can be vaguely confusing, it's worth > reading a tutorial on them to be sure you understand. > > if "foo" is in "utils", what you described ought to look like: > > from ..utils import foo > > (unless I managed to confuse myself!) > This is a problem with which I also have struggled and was never able to get the (..) syntax of relative imports to work so I solved it another way placing the following code at the top of the file containing the tests: path2add = os.path.expandvars("${CLUB}/Utils") sys.path.append(path2add) If the first line confuses you just ignore it and assign the full path name to and including your 'utils.py' file to the path2add variable. From nandishn17 at gmail.com Thu Jan 16 08:00:33 2020 From: nandishn17 at gmail.com (Nandish N) Date: Thu, 16 Jan 2020 18:30:33 +0530 Subject: [Tutor] Fetch CPU and GPU values from python Message-ID: Hi, I want to fetch system cpu,process cpu and gpu values from task manager, on exploring I got to know about psutil.cpu_percent() and GPUtil and here is the code used: import os import time import psutil from GPUtil import GPUtil string=xyz timer=124 listOfProcessNames=[] pInfoDict={} while timer != 1: for x in range(3): print(psutil.cpu_percent(1)) #print("GPU:",GPUtil.showUtilization()) psutil.process_iter() for proc in psutil.process_iter(): processName = proc.name() processID = proc.pid if processName.find("xyz") != -1: #print(proc) print(processName) print(processID) cpu=proc.cpu_percent() print(cpu) pInfoDict = proc.as_dict(attrs=('name', 'cpu_percent')) # Append dict of process detail in list listOfProcessNames.append(pInfoDict) #print(listOfProcessNames) os.system('wmic cpu get loadpercentage') timer=timer-1 time.sleep(5) What is the output of psutil.cpu_percent() is it system cpu values? And does proc.cpu_percent() give process cpu value? Is there any function to get values for Data-In/Data-Out, Memeory info? How to call shell script from python? I kindly request you to help me in getting the above values mentioned, Thanks in advance. Regards, Nandish. From jf_byrnes at comcast.net Thu Jan 16 14:20:05 2020 From: jf_byrnes at comcast.net (Jim) Date: Thu, 16 Jan 2020 13:20:05 -0600 Subject: [Tutor] OT - gmane Message-ID: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Is anyone else having trouble connecting to gmane? I know that tutor is pretty sporadic on it but until today I have been able to connect and read other groups. Thanks, Jim From mats at wichmann.us Thu Jan 16 14:25:06 2020 From: mats at wichmann.us (Mats Wichmann) Date: Thu, 16 Jan 2020 12:25:06 -0700 Subject: [Tutor] OT - gmane In-Reply-To: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Message-ID: On 1/16/20 12:20 PM, Jim wrote: > Is anyone else having trouble connecting to gmane? > > I know that tutor is pretty sporadic on it but until today I have been > able to connect and read other groups. > > Thanks,? Jim > _______________________________________________ > Tutor maillist? -? Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor a quick check shows everything down... https://notopening.com/site/gmane.org From zachary.ware+pytut at gmail.com Thu Jan 16 14:38:28 2020 From: zachary.ware+pytut at gmail.com (Zachary Ware) Date: Thu, 16 Jan 2020 13:38:28 -0600 Subject: [Tutor] OT - gmane In-Reply-To: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Message-ID: On Thu, Jan 16, 2020 at 1:23 PM Jim wrote: > Is anyone else having trouble connecting to gmane? > > I know that tutor is pretty sporadic on it but until today I have been > able to connect and read other groups. Gmane has moved: https://lars.ingebrigtsen.no/2020/01/15/news-gmane-org-is-now-news-gmane-io/ -- Zach From alan.gauld at yahoo.co.uk Thu Jan 16 15:23:44 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 16 Jan 2020 20:23:44 +0000 Subject: [Tutor] OT - gmane In-Reply-To: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Message-ID: <445924ab-3a7e-2008-0cf5-02054a3f8ffb@yahoo.co.uk> On 16/01/2020 19:20, Jim wrote: > Is anyone else having trouble connecting to gmane? > > I know that tutor is pretty sporadic on it but until today I have been > able to connect and read other groups. Tutor has not worked on gmane since the great unsubscription event a few months ago. Gmane works by subscribing to the list and getting a feed. It was unsubscribed along with everyone else. But gmane is not maintained so nobody is there to resubscribe... I note that gmane has a new home. It might now be possible to get somebody to reinstate the tutor link. I'll see if I can find someone to do so... -- 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 From jf_byrnes at comcast.net Thu Jan 16 15:50:33 2020 From: jf_byrnes at comcast.net (Jim) Date: Thu, 16 Jan 2020 14:50:33 -0600 Subject: [Tutor] OT - gmane In-Reply-To: <445924ab-3a7e-2008-0cf5-02054a3f8ffb@yahoo.co.uk> References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> <445924ab-3a7e-2008-0cf5-02054a3f8ffb@yahoo.co.uk> Message-ID: <2ca3730f-b106-0b67-e427-3295747546d7@comcast.net> On 1/16/20 2:23 PM, Alan Gauld via Tutor wrote: > On 16/01/2020 19:20, Jim wrote: >> Is anyone else having trouble connecting to gmane? >> >> I know that tutor is pretty sporadic on it but until today I have been >> able to connect and read other groups. > > Tutor has not worked on gmane since the great unsubscription > event a few months ago. > > Gmane works by subscribing to the list and getting a feed. > It was unsubscribed along with everyone else. But gmane > is not maintained so nobody is there to resubscribe... > > I note that gmane has a new home. It might now be possible > to get somebody to reinstate the tutor link. I'll see if > I can find someone to do so... > Thanks to everyone for letting me know it's not just me. I never unsubscribed when the problems started, so I occasionally actually get a tutor message off of gmane. Jim From alan.gauld at yahoo.co.uk Thu Jan 16 16:10:35 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 16 Jan 2020 21:10:35 +0000 Subject: [Tutor] OT - gmane In-Reply-To: References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Message-ID: On 16/01/2020 19:38, Zachary Ware wrote: > Gmane has moved: > https://lars.ingebrigtsen.no/2020/01/15/news-gmane-org-is-now-news-gmane-io/ > Has anyone succeeded in getting the nntp feed to work on the new servers? Not just the tutor list - anything at all? I failed miserably. -- 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 From robertvstepp at gmail.com Thu Jan 16 23:13:18 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Thu, 16 Jan 2020 22:13:18 -0600 Subject: [Tutor] Fetch CPU and GPU values from python In-Reply-To: References: Message-ID: On Thu, Jan 16, 2020 at 7:25 AM Nandish N wrote: > And does proc.cpu_percent() give process cpu value? > Is there any function to get values for Data-In/Data-Out, Memeory info? Searching for "psutil docs" I found: https://psutil.readthedocs.io/en/latest/#system-related-functions > How to call shell script from python? Perhaps the subprocess module in the Python 3 standard library might be helpful? https://docs.python.org/3/library/subprocess.html -- boB From jf_byrnes at comcast.net Fri Jan 17 12:08:46 2020 From: jf_byrnes at comcast.net (Jim) Date: Fri, 17 Jan 2020 11:08:46 -0600 Subject: [Tutor] OT - gmane In-Reply-To: References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Message-ID: On 1/16/20 3:10 PM, Alan Gauld via Tutor wrote: > On 16/01/2020 19:38, Zachary Ware wrote: > >> Gmane has moved: >> https://lars.ingebrigtsen.no/2020/01/15/news-gmane-org-is-now-news-gmane-io/ >> > > Has anyone succeeded in getting the nntp feed to work > on the new servers? > > Not just the tutor list - anything at all? > > I failed miserably. > Alan, I just managed to download all the messages from the newsgroups I had subscribed to using the old gmane. This includes Tutor and even one I tried to subscribe to on the old gmane as a test. I use Thunderbird on Linux Mint. All I did was go to the server settings and change news.gmane.org to news.gmane.io. I am sending you this message using tutor at python.org. I will send you a test message from new.gmane.io after I send this. Regards, Jim From jf_byrnes at comcast.net Fri Jan 17 12:10:25 2020 From: jf_byrnes at comcast.net (Jim) Date: Fri, 17 Jan 2020 11:10:25 -0600 Subject: [Tutor] OT - gmane In-Reply-To: References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Message-ID: On 1/16/20 3:10 PM, Alan Gauld via Tutor wrote: > On 16/01/2020 19:38, Zachary Ware wrote: > >> Gmane has moved: >> https://lars.ingebrigtsen.no/2020/01/15/news-gmane-org-is-now-news-gmane-io/ >> > > Has anyone succeeded in getting the nntp feed to work > on the new servers? > > Not just the tutor list - anything at all? > > I failed miserably. > Ok, here I am replying to a message on the new news.gmane.io Jim From alan.gauld at yahoo.co.uk Fri Jan 17 18:35:09 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Fri, 17 Jan 2020 23:35:09 +0000 Subject: [Tutor] OT - gmane In-Reply-To: References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> Message-ID: <4d484f92-6bbe-227c-425f-739a9b2d62b7@yahoo.co.uk> On 17/01/2020 17:10, Jim wrote: > On 1/16/20 3:10 PM, Alan Gauld via Tutor wrote: >> On 16/01/2020 19:38, Zachary Ware wrote: >> >>> Gmane has moved: >>> https://lars.ingebrigtsen.no/2020/01/15/news-gmane-org-is-now-news-gmane-io/ >>> >> >> Has anyone succeeded in getting the nntp feed to work >> on the new servers? >> >> Not just the tutor list - anything at all? >> >> I failed miserably. >> > > Ok, here I am replying to a message on the new news.gmane.io > Thanks, I got both of them. Ok I'll try again... -- 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 From alan.gauld at yahoo.co.uk Fri Jan 17 18:45:26 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Fri, 17 Jan 2020 23:45:26 +0000 Subject: [Tutor] OT - gmane In-Reply-To: <4d484f92-6bbe-227c-425f-739a9b2d62b7@yahoo.co.uk> References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> <4d484f92-6bbe-227c-425f-739a9b2d62b7@yahoo.co.uk> Message-ID: On 17/01/2020 23:35, Alan Gauld via Tutor wrote: >>> Has anyone succeeded in getting the nntp feed to work >>> on the new servers? >>> >>> Not just the tutor list - anything at all? >>> >>> I failed miserably. >> >> Ok, here I am replying to a message on the new news.gmane.io > > Thanks, I got both of them. > > Ok I'll try again... Yep, all working now. I must just have been a tad early yesterday... This is very good news. :-) -- 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 From jf_byrnes at comcast.net Fri Jan 17 20:04:26 2020 From: jf_byrnes at comcast.net (Jim) Date: Fri, 17 Jan 2020 19:04:26 -0600 Subject: [Tutor] OT - gmane In-Reply-To: References: <65d78a9d-d7ad-c4c4-7947-7bbfbe93444e@comcast.net> <4d484f92-6bbe-227c-425f-739a9b2d62b7@yahoo.co.uk> Message-ID: On 1/17/20 5:45 PM, Alan Gauld via Tutor wrote: > On 17/01/2020 23:35, Alan Gauld via Tutor wrote: > >>>> Has anyone succeeded in getting the nntp feed to work >>>> on the new servers? >>>> >>>> Not just the tutor list - anything at all? >>>> >>>> I failed miserably. >>> >>> Ok, here I am replying to a message on the new news.gmane.io >> >> Thanks, I got both of them. >> >> Ok I'll try again... > > > Yep, all working now. > I must just have been a tad early yesterday... > > This is very good news. :-) I agree. Jim From caudilho at cock.li Mon Jan 20 14:50:24 2020 From: caudilho at cock.li (Alessandro Caudilho) Date: Mon, 20 Jan 2020 22:50:24 +0300 Subject: [Tutor] Variable and a raw string Message-ID: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> Hello all my code below: ============================ from datetime import datetime state = 'colorado/us' with open('/home/user/' + state + '.txt', 'a') as f: time = datetime.today().strftime('%Y-%m-%d %H:%M:%S') f.write(time) f.close() ============================ And I get FileNotFoundError: [Errno 2] No such file or directory: "/home/user/colorado/us'.txt" So how can I create a variable and assign the raw string to it? Are there other solutions for this problem? -- Alessandro Caudilho From joel.goldstick at gmail.com Mon Jan 20 15:21:08 2020 From: joel.goldstick at gmail.com (Joel Goldstick) Date: Mon, 20 Jan 2020 15:21:08 -0500 Subject: [Tutor] Variable and a raw string In-Reply-To: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> References: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> Message-ID: On Mon, Jan 20, 2020 at 3:15 PM Alessandro Caudilho wrote: > > Hello all > > my code below: > > ============================ > from datetime import datetime > > state = 'colorado/us' > the following line is causing your problem. 'state' is 'colorado/us so you are trying to open /home/user/colorado/us.txt You could change the / to _ and it should work > with open('/home/user/' + state + '.txt', 'a') as f: > time = datetime.today().strftime('%Y-%m-%d %H:%M:%S') > f.write(time) > f.close() > ============================ > > And I get FileNotFoundError: [Errno 2] No such file or directory: "/home/user/colorado/us'.txt" > > So how can I create a variable and assign the raw string to it? Are there other solutions for this problem? > > -- > Alessandro Caudilho > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor -- Joel Goldstick http://joelgoldstick.com/blog http://cc-baseballstats.info/stats/birthdays From mats at wichmann.us Mon Jan 20 15:43:15 2020 From: mats at wichmann.us (Mats Wichmann) Date: Mon, 20 Jan 2020 13:43:15 -0700 Subject: [Tutor] Variable and a raw string In-Reply-To: References: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> Message-ID: <900d745c-fcf2-d9ba-ec3e-bd9895fae8f7@wichmann.us> On 1/20/20 1:21 PM, Joel Goldstick wrote: > On Mon, Jan 20, 2020 at 3:15 PM Alessandro Caudilho wrote: >> >> Hello all >> >> my code below: >> >> ============================ >> from datetime import datetime >> >> state = 'colorado/us' >> > the following line is causing your problem. 'state' is 'colorado/us > so you are trying to open > /home/user/colorado/us.txt > > You could change the / to _ and it should work >> with open('/home/user/' + state + '.txt', 'a') as f: >> time = datetime.today().strftime('%Y-%m-%d %H:%M:%S') >> f.write(time) >> f.close() >> ============================ >> >> And I get FileNotFoundError: [Errno 2] No such file or directory: "/home/user/colorado/us'.txt" >> >> So how can I create a variable and assign the raw string to it? Are there other solutions for this problem? In particular, it's causing a problem because you're asking to create a file in a directory /home/user/colorado that doesn't exist. If you follow Joel's suggestion and change state to 'colorado_us', then you're trying to create the file 'colorado_us.text' in /home/user, which will work (as long as /home/user exists). You can also add logic to create necessary directories if you want to organize your data by directories... From PyTutor at DancesWithMice.info Mon Jan 20 16:15:16 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Tue, 21 Jan 2020 10:15:16 +1300 Subject: [Tutor] Variable and a raw string In-Reply-To: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> References: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> Message-ID: <1042c43d-f0c1-a6ee-8e82-7a3d4b237416@DancesWithMice.info> On 21/01/20 8:50 AM, Alessandro Caudilho wrote: > Hello all > > my code below: > > ============================ > from datetime import datetime > > state = 'colorado/us' > > with open('/home/user/' + state + '.txt', 'a') as f: > time = datetime.today().strftime('%Y-%m-%d %H:%M:%S') > f.write(time) > f.close() > ============================ > > And I get FileNotFoundError: [Errno 2] No such file or directory: "/home/user/colorado/us'.txt" Did you re-type the code or did you copy-paste it from the editor into this email msg??? (the mixture of quotation-marks/double-quotes and apostrophe/single-quote in the err.msg, indicates that the above code is NOT the original) 1 if the code is being run (by "user") from his/her home-directory (ie /home/user) then all that may be omitted from the open() 2 if your intent is to separate each US State into its own directory, then would "...US/Colorado..." make for a better stepwise-refinement? 3 otherwise if the "us" part is merely a label, perhaps to distinguish from some "colorado" in another country, then please see previous responses. 4 Please review open-append in https://docs.python.org/3/library/functions.html?highlight=open#open (see also "absolute" and "relative", per (1) above). What happens if one opens a file in a directory which does not (yet) exist? How does this differ from attempting to open a file which does not exist (read) or does not yet exist (append)? (experiment in the Python REPL) 5 given that the file is being processed in-context (with), the close() is unnecessary, because it is provided auto-magically as part of the context-manager (https://docs.python.org/3/reference/compound_stmts.html#the-with-statement) -- Regards =dn From Richard at Damon-family.org Mon Jan 20 17:21:39 2020 From: Richard at Damon-family.org (Richard Damon) Date: Mon, 20 Jan 2020 17:21:39 -0500 Subject: [Tutor] Variable and a raw string In-Reply-To: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> References: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> Message-ID: <8DE18B16-69D4-482A-B40E-05B8382DE869@Damon-family.org> Does the directory /home/user/colorado exist? (And is writable?) The function fopen can create the file, but it won?t create the directory. > On Jan 20, 2020, at 3:15 PM, Alessandro Caudilho wrote: > > ?Hello all > > my code below: > > ============================ > from datetime import datetime > > state = 'colorado/us' > > with open('/home/user/' + state + '.txt', 'a') as f: > time = datetime.today().strftime('%Y-%m-%d %H:%M:%S') > f.write(time) > f.close() > ============================ > > And I get FileNotFoundError: [Errno 2] No such file or directory: "/home/user/colorado/us'.txt" > > So how can I create a variable and assign the raw string to it? Are there other solutions for this problem? > > -- > Alessandro Caudilho > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor From alan.gauld at yahoo.co.uk Mon Jan 20 19:08:31 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Tue, 21 Jan 2020 00:08:31 +0000 Subject: [Tutor] Variable and a raw string In-Reply-To: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> References: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> Message-ID: On 20/01/2020 19:50, Alessandro Caudilho wrote: > state = 'colorado/us' > > with open('/home/user/' + state + '.txt', 'a') as f: > > And I get FileNotFoundError: [Errno 2] No such file or directory: "/home/user/colorado/us'.txt" Notice the error has a spurious ' within it - between /us and .txt. That quote is not in your code which suggests that there must be some code you are not showing us? Eliminate that quote and it should work - assuming you have access to the directory and that it exists. Also note that you don't need a close() when you use the with structure to open the file. close() is done automatically by Python. -- 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 From PyTutor at DancesWithMice.info Mon Jan 20 23:44:29 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Tue, 21 Jan 2020 17:44:29 +1300 Subject: [Tutor] Variable and a raw string In-Reply-To: <20200121063014.3b86ffa0f909aef381111650@cock.li> References: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> <1042c43d-f0c1-a6ee-8e82-7a3d4b237416@DancesWithMice.info> <20200121063014.3b86ffa0f909aef381111650@cock.li> Message-ID: <1509a9ed-4d54-d618-de24-bae27207528c@DancesWithMice.info> On 21/01/20 4:30 PM, Alessandro Caudilho wrote: > On Tue, 21 Jan 2020 10:15:16 +1300 > David L Neil via Tutor wrote: >> Did you re-type the code or did you copy-paste it from the editor into >> this email msg??? >> (the mixture of quotation-marks/double-quotes and >> apostrophe/single-quote in the err.msg, indicates that the above code is >> NOT the original) >> >> 1 if the code is being run (by "user") from his/her home-directory (ie >> /home/user) then all that may be omitted from the open() >> >> 2 if your intent is to separate each US State into its own directory, >> then would "...US/Colorado..." make for a better stepwise-refinement? >> >> 3 otherwise if the "us" part is merely a label, perhaps to distinguish >> from some "colorado" in another country, then please see previous responses. >> >> 4 Please review open-append in >> https://docs.python.org/3/library/functions.html?highlight=open#open >> (see also "absolute" and "relative", per (1) above). What happens if one >> opens a file in a directory which does not (yet) exist? How does this >> differ from attempting to open a file which does not exist (read) or >> does not yet exist (append)? (experiment in the Python REPL) > > Thanks David! I copy-pasted the code from my editor. > > This code is a part of the one that uses weather API. When I call, it returns me number of bulk values with current weather forecasts. One of values is a ). I know that I can`t create file because of '/' in 'colorado/us'. But maybe is there some way to avoid removing or replacing slash in 'state'? > > My fault - I should give more information on this in my first message No problem - we live and learn. We try to be a friendly group but we are here to help learners/learning. (also, please check email headers to ensure answers go to the list so that others might learn/contribute suggestions) As you will see, others also suggest that the err.msg does not smell right... Not sure what is meant by "One of values is a )." (above). Is the right-parenthesis a response from the API? Also (sorry to keep grumbling), DuckDuckGo returned at least three different hits for "weather API" from the US alone. Which one? Why not also supply the query you are using and the data-returned (sub-set, as appropriate), so that we can see what you're facing... Sorry, but I don't think it can be done: the problem with the (above) specification is that a slash (/) has particular meaning within a Posix filename (even on MS-Windows which uses back-slashes within its native rules). I don't think that the slash can be "escaped". (but better minds than mine may correct/elucidate...) Three thoughts: 1 if the "/us" component does not add-value to your application ...cut it out => "out damn spot"*! >>> state = 'colorado/us' >>> state_only = state[ :-3 ] 'colorado' - assuming that the '/us' suffix is entirely consistent across (your) data-set! 2 if you must keep the "us" within the directoryNM, then change ("translate") the slash to an underline (or similar). 3 go through and manually bulk-create the 50+ directories, which will obviate the exception. 4 (who said I could count?) create a dictionary with the API's labels as "keys", eg 'colorado/us', and your own preference (and legal directoryNM) as "values", eg 'colorado' or 'CO'. Use that as a translation-table to construct legal paths from the API labels. * Shakespeare reference (in a bid to appear learned???) -- Regards =dn From caudilho at cock.li Mon Jan 20 22:30:14 2020 From: caudilho at cock.li (Alessandro Caudilho) Date: Tue, 21 Jan 2020 06:30:14 +0300 Subject: [Tutor] Variable and a raw string In-Reply-To: <1042c43d-f0c1-a6ee-8e82-7a3d4b237416@DancesWithMice.info> References: <20200120225024.32f26c1cdb9a759b4ad8177a@cock.li> <1042c43d-f0c1-a6ee-8e82-7a3d4b237416@DancesWithMice.info> Message-ID: <20200121063014.3b86ffa0f909aef381111650@cock.li> On Tue, 21 Jan 2020 10:15:16 +1300 David L Neil via Tutor wrote: > Did you re-type the code or did you copy-paste it from the editor into > this email msg??? > (the mixture of quotation-marks/double-quotes and > apostrophe/single-quote in the err.msg, indicates that the above code is > NOT the original) > > 1 if the code is being run (by "user") from his/her home-directory (ie > /home/user) then all that may be omitted from the open() > > 2 if your intent is to separate each US State into its own directory, > then would "...US/Colorado..." make for a better stepwise-refinement? > > 3 otherwise if the "us" part is merely a label, perhaps to distinguish > from some "colorado" in another country, then please see previous responses. > > 4 Please review open-append in > https://docs.python.org/3/library/functions.html?highlight=open#open > (see also "absolute" and "relative", per (1) above). What happens if one > opens a file in a directory which does not (yet) exist? How does this > differ from attempting to open a file which does not exist (read) or > does not yet exist (append)? (experiment in the Python REPL) Thanks David! I copy-pasted the code from my editor. This code is a part of the one that uses weather API. When I call, it returns me number of bulk values with current weather forecasts. One of values is a state (e.g. 'colorado/us'). I know that I can`t create file because of '/' in 'colorado/us'. But maybe is there some way to avoid removing or replacing slash in 'state'? My fault - I should give more information on this in my first message -- Alessandro Caudilho From aaliyahrebeccawood at gmail.com Thu Jan 23 14:00:00 2020 From: aaliyahrebeccawood at gmail.com (Aaliyah Wood) Date: Thu, 23 Jan 2020 12:00:00 -0700 Subject: [Tutor] Commands Message-ID: I recently started learning Python two days ago using different lessons online so my question may sound very uneducated. Python is not recognizing any commands (True, False, Print, Int.) I know that these commands turn a different color when they're recognized (True and False turn orange) How do I fix this? Thank you! Aaliyah From david at graniteweb.com Thu Jan 23 18:56:24 2020 From: david at graniteweb.com (David Rock) Date: Thu, 23 Jan 2020 17:56:24 -0600 Subject: [Tutor] Commands In-Reply-To: References: Message-ID: <79A4C283-18FD-45FD-82B4-73D307EB6FA8@graniteweb.com> > On Jan 23, 2020, at 13:00, Aaliyah Wood wrote: > > I recently started learning Python two days ago using different lessons > online so my question may sound very uneducated. Python is not recognizing > any commands (True, False, Print, Int.) I know that these commands turn a > different color when they're recognized (True and False turn orange) How do > I fix this? Welcome, Aaliyah. What you are describing is called syntax highlighting, and is a function of the text editor you are using. Python itself is not responsible for changing colors of text in the file. When you say ?Python is not recognizing any commands,? what is the context where you are seeing this? What text editor are you using? What version of Python are you using? What Operating system are you using? The more information you can give us about what you are doing and what you expect to see, the better we can help. ? David Rock david at graniteweb.com From alan.gauld at yahoo.co.uk Thu Jan 23 19:11:19 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Fri, 24 Jan 2020 00:11:19 +0000 Subject: [Tutor] Commands In-Reply-To: References: Message-ID: On 23/01/2020 19:00, Aaliyah Wood wrote: > I recently started learning Python two days ago using different lessons > online so my question may sound very uneducated. Python is not recognizing > any commands (True, False, Print, Int.) One thing about programming is that you need to be very precise in your language. A command in Python is something very specific. None of the above words are Python commands. Also spelling, punctuation and spacing are all incredibly important too. True and False are Python values. They are also "reserved words" meaning you can't use them for variable names. Print is just a name(*) to Python. It is very different to print(lower case) which is a *function* in Python 3 and a *command* in Python 2 - two very different things! Int likewise is just a name. int(lower case) however is a built-in type. It is also a conversion function used to create integers from (valid)strings(another type) and floats(yet another type). (*)------------------- Names in python are just labels that you can attach to objects. They usually appear as variables or functions. For example: foo = 66 creates a name foo and gives it a value of 66 def Print(): pass creates name a Print and gives it a value of a function which does nothing(pass). Print won't get a special colour in IDLE but it will be remembered and offered as an option if you try typing Pri. (see below) ------------------------ > I know that these commands turn a > different color when they're recognized (True and False turn orange) reserved words turn orange, so True, False, None, if, while, class, def etc all turn orange - assuming you are using IDLE. The actual colour will vary depending on the tool you use. > How do I fix this? Mainly by typing the correct word and in the correct place. But if using IDLE (and some other tools) you do get a little extra help in that you can type a tab and Python will offer up a choice of valid options. If you have mistyped there will be no options(or perhaps a very limited set) You can select the one you want and IDLE will fill it in for you - a bit like smart typing on your phone. Does that help? -- 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 From david at graniteweb.com Thu Jan 23 21:27:40 2020 From: david at graniteweb.com (David Rock) Date: Thu, 23 Jan 2020 20:27:40 -0600 Subject: [Tutor] Commands In-Reply-To: References: <79A4C283-18FD-45FD-82B4-73D307EB6FA8@graniteweb.com> Message-ID: > On Jan 23, 2020, at 20:04, Aaliyah Wood wrote: > > Hi David, > > Thank you for getting back to me so quickly, my version of Python is 3.8.1 for Windows. Essentially what is happening is Python is not recognizing and commands like True, False, print. I attached a couple pictures to explain better. The screenshot is a lesson I am watching on YouTube, this is an example of what my program is supposed to look like. The photo of my laptop is what the program looks like on my computer. I?m just confused as to why I cannot run a program and why it doesn?t look right. It was working two days ago when I began learning and now it isn?t. If you need any more explanation just let me know! > > Thank you again, > Aaliyah > > > Ok, that helps a lot. One thing to note, make sure when you reply, you reply to the tutor mailing list, not the individual. That way, everyone can help, and not just one person. In this case, it?s actually good you did, because the mailing list would have dropped the images, so we would not have seen what they look like. I can tell from the pictures that you are using PyCharm, which is part of the issue. PyCharm is a great IDE for Python, but it has its own learning curve. What?s happened is you created a generic text file named ?App?, rather than an ?App.py? python file. That?s why PyCharm isn?t coloring things for you; it does not know it?s supposed to be a Python file. If you do File->New Project and create a new, blank project, then Right-click on the project name and pick New->Python File That should get you a file that PyCharm knows is a Python File and should behave as you expect. I?d also look around for tutorials specifically on using PyCharm. That will go a long way to helping you deal with the quirks of working in that specific IDE. For something simpler, you could also try using IDLE, the built-in editor that comes with Python installs. It will also give you syntax highlighting, without the overhead of also learning an IDE at the same time. ? David From PyTutor at DancesWithMice.info Thu Jan 23 23:32:46 2020 From: PyTutor at DancesWithMice.info (DL Neil) Date: Fri, 24 Jan 2020 17:32:46 +1300 Subject: [Tutor] Friday Finking: Enum by gum (cross-posted from Python-list) Message-ID: [@Alan: should you feel cross-posting inappropriate, please delete...] When, why, and how do you employ Python/PSL enums? TLDR? Fair enough! Much of this text recounts a voyage of discovery. More specific questions appear at the bottom... Last century, when I learned to program (in-between working part-time at the dinosaur farm) neither FORTRAN II/IV nor COBOL-68 offered enums. Interestingly, whilst most of our team-members learned about them (ComSc) or had been trained to use them in xyz-language, few remember any mention of them in Python courses/books/tutorials - nor could anyone recall (local) usage. This fascinated me (small things amuse small minds) because I had been called-in to settle a 'disagreement' where users thought they should enter data 'one way' into an interface/API, whereas the design had directed it otherwise. Was it a user issue, a design problem, an inflexible interface, poor documentation, weak design, ...? This in-turn led me to consider: can we both illustrate the interface (not just in docs) AND ease the user-side of the interface AND/OR improve the error-checking*/feedback? (and do it cheaply~$0, and take no time at all, and leap tall-buildings at a single...) * perhaps by controlling what control-data (cf statistics/source-data) may be input. Accordingly, as 'they' say, there is a "first time for everything", and I starting playing with enums... The PSL manual is somewhat sparse. The examples are weak and almost solely introspective: if I have an enum, I can ask it what it is. Should that sum-up the total use of enums? By-and-large, most web-refs seem weak and pointless repetitions of the PSL docs (perhaps only the parts that the author actually understood?); reciting uses such as defining the suits in a pack/deck of cards, but not describing how the enum might actually be used in (real) code. Negative motivations resulted from the first part of Mike 'the Mouse' Driscoll's comment: "Whilst I don?t think the enum module is really necessary for Python, it is a neat tool...", but interest was recovered with the closing observation. "Neat" for what exactly? With the stated-intention that enums would be back-ported/re-factored into the PSL, the PEP(s) dance-around the idea that existing code may run off integer parameters which are understood to hold particular meanings, eg 0 => hourly, 1 => daily, 2 => weekly, etc In the normal course of Python there is nothing to stop one from setting such an parameter to 10, which has no actual meaning within the API (~my user-issue, above). Nor is the user (or the original coder) prevented from using the parameter for a purpose other than its temporal intention! Enter the enum: applying semantic meaning: >>> Frequency = enum.Enum( "Frequency", "HOURLY DAILY WEEKLY" ) Here, whilst there is an int value, it can't be used as such: >>> Frequency.HOURLY >>> Frequency.HOURLY + 2 Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'Frequency' and 'int' Unless (are we "all adults here"?): >>> Frequency.HOURLY.value + 2 3 OK, so it can be used, mis-used, and abused... Plus, be aware: >>> 1 in Frequency __main__:1: DeprecationWarning: using non-Enums in containment checks will raise TypeError in Python 3.8 False However, ignoring the value completely, setting >>> argument = Frequency.DAILY and then posting argument into the API, the parameter assumes semantic-meaning: >>> if parameter is Frequency.DAILY: ... # multiply by 24 to use hours as common metric So, in my thinking about interfaces: if the first code-line is on the calling-side of an API and the latter inside our 'black-box', that code-snippet seemed to fit my spec and illustrate an actual and useful purpose for enums. (OK, I might be slow, but I'll get there...) Agreed? Thus, alongside the API we need to publish the enum, and 'advertise' it as a "helper". A good choice of name (in the users' view(s)) may also avoid mis-communications and other errors... Do you agree? The earlier PEP talked of "enumerated values hiding as much as possible about their implementation" which also makes sense in this application, because I don't care about any underlying value for Frequency.DAILY, only that it be checked/compared - and that if someone enters 0, 1, or something of their own devising (or even Fr:"QUOTIDIEN" (== En:"DAILY")) it is not (in this case) regarded as 'equal' or otherwise equivalent! This point is picked-up in PEP 435: "an enumeration ensures that such values are distinct from any others including, importantly, values within other enumerations". Hence (in the snippet, above) the "is", ie looking at "identity" rather than value! Questions: Have I made proper sense of this? (please don't laugh too much) Is the above 'interfacing' an appropriate use of enum-s; or is it really 'overkill' or posturing? Do you use enum-s very often? What other 'pattern' of application do you employ? Python v3.6+ brings Flag-enums into the game. These seem better for interface use, as described. Do you prefer them to the 'plain-vanilla' enum, and for what reason? WebRefs: By gum: https://www.phrases.org.uk/meanings/82225.html Mike Driscoll: https://www.blog.pythonlibrary.org/2018/03/20/python-3-an-intro-to-enumerations/ PSL enums: https://docs.python.org/3.7/library/enum.html PEP 354 (superseded): https://www.python.org/dev/peps/pep-0354/ PEP 435 (accepted): https://www.python.org/dev/peps/pep-0435/ -- Regards, =dn PS: life has been 'exciting' here. Some thoughtless person cut-through our local fiber back-haul, and even since the service was restored, there have been throughput problems, connection issues, ... Apologies if I haven't caught-up with you amongst the massive backlog - it's not you, it's me! -- https://mail.python.org/mailman/listinfo/python-list From suresh.gm at gmail.com Fri Jan 24 08:12:36 2020 From: suresh.gm at gmail.com (Panchanathan Suresh) Date: Fri, 24 Jan 2020 05:12:36 -0800 Subject: [Tutor] How to remove the comma only at the end? Message-ID: Hi everyone, How to remove the last comma , from the variable members? I tried members[:-1], members.rstrip(","), but no luck. It is always returning: Mike, Karen, Jake, Tasha, I want it to return Mike, Karen, Jake, Tasha (no comma after Tasha) Below is my code: ***** def group_list(group, users): members = "" members1 = "" for x in range(0,len(users)): #print(users[x]) members = members + users[x] + ", " return members[:-1] print(group_list("Marketing", ["Mike", "Karen", "Jake", "Tasha"])) # Should be "Marketing: Mike, Karen, Jake, Tasha" ***** Thanks so much, Suresh From marc.tompkins at gmail.com Fri Jan 24 12:43:24 2020 From: marc.tompkins at gmail.com (Marc Tompkins) Date: Fri, 24 Jan 2020 09:43:24 -0800 Subject: [Tutor] How to remove the comma only at the end? In-Reply-To: References: Message-ID: On Fri, Jan 24, 2020 at 9:21 AM Panchanathan Suresh wrote: > Hi everyone, > > How to remove the last comma , from the variable members? > I tried members[:-1], members.rstrip(","), but no luck. It is > always returning: > Mike, Karen, Jake, Tasha, > Use the string.join() method: >>> marketing = ["Mike", "Karen", "Jake", "Tasha"] >>> ", ".join(marketing) 'Mike, Karen, Jake, Tasha' Essentially, this says "Take the members of the list and glue them together into a string, using this string (a comma and a space in this instance) as glue". From alan.gauld at yahoo.co.uk Fri Jan 24 12:45:53 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Fri, 24 Jan 2020 17:45:53 +0000 Subject: [Tutor] How to remove the comma only at the end? In-Reply-To: References: Message-ID: On 24/01/2020 13:12, Panchanathan Suresh wrote: > How to remove the last comma , from the variable members? > I tried members[:-1], That will remove the last character from a string but.... > for x in range(0,len(users)): > members = members + users[x] + ", " You are adding a comma and a space at the end, so your code will remove the space but not the comma. However, you are probably better using the rstrip() method of the string return members.rstrip(', ') Or maybe you are better not using a string at all? Would a dictionary be a better fit and then only convert to a string on output? Especially if you plan on having multiple departments. If you really want a string you could use join: def group_list(group, users): members = group +": " return members + ','.join(users) Which only inserts the comma between the inner members. -- 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 From akleider at sonic.net Fri Jan 24 12:50:06 2020 From: akleider at sonic.net (Alex Kleider) Date: Fri, 24 Jan 2020 09:50:06 -0800 Subject: [Tutor] How to remove the comma only at the end? In-Reply-To: References: Message-ID: <28ab54254ae0a86b316efc2b69977b96@sonic.net> On 2020-01-24 05:12, Panchanathan Suresh wrote: > Hi everyone, > > How to remove the last comma , from the variable members? > I tried members[:-1], members.rstrip(","), but no luck. It is > always returning: > Mike, Karen, Jake, Tasha, > > I want it to return Mike, Karen, Jake, Tasha (no comma after Tasha) > > Below is my code: > > ***** > def group_list(group, users): > members = "" > members1 = "" > for x in range(0,len(users)): > #print(users[x]) > members = members + users[x] + ", " > return members[:-1] > > > print(group_list("Marketing", ["Mike", "Karen", "Jake", "Tasha"])) # > Should > be "Marketing: Mike, Karen, Jake, Tasha" > You might find the str join method useful; more specifically: >>> ', '.join(["Mike", "Karen", "Jake", "Tasha"]) From mats at wichmann.us Fri Jan 24 13:12:12 2020 From: mats at wichmann.us (Mats Wichmann) Date: Fri, 24 Jan 2020 11:12:12 -0700 Subject: [Tutor] How to remove the comma only at the end? In-Reply-To: References: Message-ID: <80865e83-99b9-b0f6-0dbe-f6fa722e43cc@wichmann.us> On 1/24/20 6:12 AM, Panchanathan Suresh wrote: > Hi everyone, > > How to remove the last comma , from the variable members? > I tried members[:-1], members.rstrip(","), but no luck. It is > always returning: > Mike, Karen, Jake, Tasha, > > I want it to return Mike, Karen, Jake, Tasha (no comma after Tasha) > > Below is my code: > > ***** > def group_list(group, users): > members = "" > members1 = "" > for x in range(0,len(users)): > #print(users[x]) > members = members + users[x] + ", " > return members[:-1] > > > print(group_list("Marketing", ["Mike", "Karen", "Jake", "Tasha"])) # Should > be "Marketing: Mike, Karen, Jake, Tasha" I'm going to jump in with my own comment here - you should cringe almost any time you see "for x in range(len(foo)):" If users is something you can iterate over (as a list, it is), then iterate over it. Don't artifcially generate an item count, use range to generate a NEW thing you can iterate over, and then index into the original list. You can write this more expressively as: for user in users: members = members + user + ", " and yes, if you're solving the problem this way, you wanted: return members[:-2] # because you put in two chars at the end! that's line is not terribly expressive (-2 is "magic", avoid "magic"), so how about if you define: sep = ", " then you can return sep.join(users) or members = members + user + sep ... return members[:-len(sep)] From cs at cskk.id.au Fri Jan 24 14:34:07 2020 From: cs at cskk.id.au (Cameron Simpson) Date: Sat, 25 Jan 2020 06:34:07 +1100 Subject: [Tutor] How to remove the comma only at the end? In-Reply-To: References: Message-ID: <20200124193407.GA78968@cskk.homeip.net> On 24Jan2020 05:12, Panchanathan Suresh wrote: >How to remove the last comma , from the variable members? >I tried members[:-1], members.rstrip(","), but no luck. It is >always returning: >Mike, Karen, Jake, Tasha, Note that your code is adding ", ", so there is a comma and a space. Using "members[:-1]" only skips the space. -2 would help. Likewise, rstrip strips trailing commas, but there are no trailing commas, only a trailing space. If you: print(repr(members)) as a debugging statement, this would become more evident. Personally, I prefer not to strip the trailing stuff, but to only add the separator on the second and following members. Eg: if members: members += ', ' members += users[x] That way there is never a trailing separator. However, I recommend what others have suggested: use str.join. Incrementally extending string is ok for small strings, but as the lis of users gets bigger the cost gets significantly larger because the members string gets copied at each append, leading to O(n^2) behaviour in the long term. Cheers, Cameron Simpson From breamoreboy at gmail.com Fri Jan 24 12:40:19 2020 From: breamoreboy at gmail.com (Mark Lawrence) Date: Fri, 24 Jan 2020 17:40:19 +0000 Subject: [Tutor] How to remove the comma only at the end? In-Reply-To: References: Message-ID: On 24/01/2020 13:12, Panchanathan Suresh wrote: > Hi everyone, > > How to remove the last comma , from the variable members? > I tried members[:-1], members.rstrip(","), but no luck. It is > always returning: > Mike, Karen, Jake, Tasha, > > I want it to return Mike, Karen, Jake, Tasha (no comma after Tasha) > > Below is my code: > > ***** > def group_list(group, users): > members = "" > members1 = "" Your for loop is poor Python, you should use:- for user in users: > for x in range(0,len(users)): > #print(users[x]) > members = members + users[x] + ", " > return members[:-1] But you simply don't need the above, just the string join method https://docs.python.org/3/library/stdtypes.html#str.join hence using the interactive prompt:- >>> print("Marketing:", ', '.join(["Mike", "Karen", "Jake", "Tasha"])) Marketing: Mike, Karen, Jake, Tasha > > > print(group_list("Marketing", ["Mike", "Karen", "Jake", "Tasha"])) # Should > be "Marketing: Mike, Karen, Jake, Tasha" > > ***** > > Thanks so much, > Suresh -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence From suresh.gm at gmail.com Fri Jan 24 13:03:29 2020 From: suresh.gm at gmail.com (Panchanathan Suresh) Date: Fri, 24 Jan 2020 10:03:29 -0800 Subject: [Tutor] How to remove the comma only at the end? In-Reply-To: References: <28ab54254ae0a86b316efc2b69977b96@sonic.net> Message-ID: Thanks a lot Mark and Alex, really appreciate it. I thought about this more and then this worked return members[:-2] (-2 because there is an extra space after , in members = members + users[x] + ", ") I was all along trying -1 and was unsuccessful. Thanks again. On Fri, Jan 24, 2020 at 10:02 AM Panchanathan Suresh wrote: > Thanks a lot Mark and Alex, really appreciate it. > > I thought about this more and then this worked return members[:-2] > (-2 because there is an extra space after , in members = members + > users[x] + ", ") > > On Fri, Jan 24, 2020 at 9:50 AM Alex Kleider wrote: > >> On 2020-01-24 05:12, Panchanathan Suresh wrote: >> > Hi everyone, >> > >> > How to remove the last comma , from the variable members? >> > I tried members[:-1], members.rstrip(","), but no luck. It is >> > always returning: >> > Mike, Karen, Jake, Tasha, >> > >> > I want it to return Mike, Karen, Jake, Tasha (no comma after Tasha) >> > >> > Below is my code: >> > >> > ***** >> > def group_list(group, users): >> > members = "" >> > members1 = "" >> > for x in range(0,len(users)): >> > #print(users[x]) >> > members = members + users[x] + ", " >> > return members[:-1] >> > >> > >> > print(group_list("Marketing", ["Mike", "Karen", "Jake", "Tasha"])) # >> > Should >> > be "Marketing: Mike, Karen, Jake, Tasha" >> > >> >> You might find the str join method useful; >> more specifically: >> >>> ', '.join(["Mike", "Karen", "Jake", "Tasha"]) >> > From suresh.gm at gmail.com Sat Jan 25 12:33:57 2020 From: suresh.gm at gmail.com (Panchanathan Suresh) Date: Sat, 25 Jan 2020 09:33:57 -0800 Subject: [Tutor] Tutor Digest, Vol 191, Issue 32 In-Reply-To: References: Message-ID: Brilliant suggestions from everyone. Thanks a ton! On Sat, Jan 25, 2020 at 9:01 AM wrote: > Send Tutor mailing list submissions to > tutor at python.org > > To subscribe or unsubscribe via the World Wide Web, visit > https://mail.python.org/mailman/listinfo/tutor > or, via email, send a message with subject or body 'help' to > tutor-request at python.org > > You can reach the person managing the list at > tutor-owner at python.org > > When replying, please edit your Subject line so it is more specific > than "Re: Contents of Tutor digest..." > Today's Topics: > > 1. Re: How to remove the comma only at the end? (Panchanathan Suresh) > > > > ---------- Forwarded message ---------- > From: Panchanathan Suresh > To: tutor at python.org > Cc: > Bcc: > Date: Fri, 24 Jan 2020 10:03:29 -0800 > Subject: Re: [Tutor] How to remove the comma only at the end? > Thanks a lot Mark and Alex, really appreciate it. > > I thought about this more and then this worked return members[:-2] > (-2 because there is an extra space after , in members = members + users[x] > + ", ") > > I was all along trying -1 and was unsuccessful. > > Thanks again. > > On Fri, Jan 24, 2020 at 10:02 AM Panchanathan Suresh > wrote: > > > Thanks a lot Mark and Alex, really appreciate it. > > > > I thought about this more and then this worked return members[:-2] > > (-2 because there is an extra space after , in members = members + > > users[x] + ", ") > > > > On Fri, Jan 24, 2020 at 9:50 AM Alex Kleider wrote: > > > >> On 2020-01-24 05:12, Panchanathan Suresh wrote: > >> > Hi everyone, > >> > > >> > How to remove the last comma , from the variable members? > >> > I tried members[:-1], members.rstrip(","), but no luck. It is > >> > always returning: > >> > Mike, Karen, Jake, Tasha, > >> > > >> > I want it to return Mike, Karen, Jake, Tasha (no comma after Tasha) > >> > > >> > Below is my code: > >> > > >> > ***** > >> > def group_list(group, users): > >> > members = "" > >> > members1 = "" > >> > for x in range(0,len(users)): > >> > #print(users[x]) > >> > members = members + users[x] + ", " > >> > return members[:-1] > >> > > >> > > >> > print(group_list("Marketing", ["Mike", "Karen", "Jake", "Tasha"])) # > >> > Should > >> > be "Marketing: Mike, Karen, Jake, Tasha" > >> > > >> > >> You might find the str join method useful; > >> more specifically: > >> >>> ', '.join(["Mike", "Karen", "Jake", "Tasha"]) > >> > > > > _______________________________________________ > Tutor maillist - Tutor at python.org > https://mail.python.org/mailman/listinfo/tutor > From zebra05 at gmail.com Mon Jan 27 06:51:58 2020 From: zebra05 at gmail.com (S D) Date: Mon, 27 Jan 2020 13:51:58 +0200 Subject: [Tutor] Getting first item in dictionary Message-ID: I have a dictionary which contains one item (?current_location?, which is a nested dict) and I would like to access that nested dict. However, I cannot use the key as the code will break if a different key is passed, e.g. ?different_location". How can I access the first item in a dictionary without using a key? The dict looks like this: ``` {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, 'request_location': 'Current location'}} ``` Kind regards, - SD From mats at wichmann.us Mon Jan 27 10:07:09 2020 From: mats at wichmann.us (Mats Wichmann) Date: Mon, 27 Jan 2020 08:07:09 -0700 Subject: [Tutor] Getting first item in dictionary In-Reply-To: References: Message-ID: <773c138a-b323-ee0d-426d-22a3d468c983@wichmann.us> On 1/27/20 4:51 AM, S D wrote: > I have a dictionary which contains one item (?current_location?, which is a > nested dict) and I would like to access that nested dict. However, I cannot > use the key as the code will break if a different key is passed, e.g. > ?different_location". > > How can I access the first item in a dictionary without using a key? The > dict looks like this: > > ``` > {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': > 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, > 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, > 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, > 'request_location': 'Current location'}} > ``` Two simple ways: >>> d = {'key': 'value'} 1. use the dict's get method, which returns None if not found: >>> v = d.get('key') >>> print(v) value >>> v = d.get('foo') >>> print(v) None >>> 2. use a try/except: >>> for k in ('key', 'foo'): ... try: ... print(d[k]) ... except KeyError: ... print("No such key:", k) ... value No such key: foo >>> From joel.goldstick at gmail.com Mon Jan 27 10:14:34 2020 From: joel.goldstick at gmail.com (Joel Goldstick) Date: Mon, 27 Jan 2020 10:14:34 -0500 Subject: [Tutor] Getting first item in dictionary In-Reply-To: <773c138a-b323-ee0d-426d-22a3d468c983@wichmann.us> References: <773c138a-b323-ee0d-426d-22a3d468c983@wichmann.us> Message-ID: On Mon, Jan 27, 2020 at 10:07 AM Mats Wichmann wrote: > > On 1/27/20 4:51 AM, S D wrote: > > I have a dictionary which contains one item (?current_location?, which is a > > nested dict) and I would like to access that nested dict. However, I cannot > > use the key as the code will break if a different key is passed, e.g. > > ?different_location". > > > > How can I access the first item in a dictionary without using a key? The > > dict looks like this: > > > > ``` > > {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': > > 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, > > 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, > > 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, > > 'request_location': 'Current location'}} > > ``` > > > Two simple ways: > > >>> d = {'key': 'value'} > > 1. use the dict's get method, which returns None if not found: > > >>> v = d.get('key') > >>> print(v) > value > >>> v = d.get('foo') > >>> print(v) > None > >>> > > 2. use a try/except: > > >>> for k in ('key', 'foo'): > ... try: > ... print(d[k]) > ... except KeyError: > ... print("No such key:", k) > ... > value > No such key: foo > >>> > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor Try d.values() Python 3.6.9 (default, Nov 7 2019, 10:44:02) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> d = {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': ... 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, ... 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, ... 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, ... 'request_location': 'Current location'}} >>> d {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, 'request_location': 'Current location'}} >>> d.values() dict_values([{'date': '2020-01-27T10:28:24.148Z', 'type_icon': 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, 'request_location': 'Current location'}]) -- Joel Goldstick http://joelgoldstick.com/blog http://cc-baseballstats.info/stats/birthdays From alan.gauld at yahoo.co.uk Mon Jan 27 13:16:55 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Mon, 27 Jan 2020 18:16:55 +0000 Subject: [Tutor] Getting first item in dictionary In-Reply-To: References: Message-ID: On 27/01/2020 11:51, S D wrote: > I have a dictionary which contains one item (?current_location?, which is a > nested dict) and I would like to access that nested dict. However, I cannot > use the key as the code will break if a different key is passed, e.g. > ?different_location". That doesn't make much sense. If you cannot use a key to access the data why did you put it in a dictionary which is a structure accessed by keys? When you say the code will break if you use a wrong key surely all you should get is a Keyerror? And you can avoid that by using dict.get(key) which returns None if the key is not found. (Although using try/except to catch the error would be better in this scenario because you should want to know why you have a wrong key and prevent it! > How can I access the first item in a dictionary without using a key? If that's what you really want you should be using a list instead of a dictionary. But you can kind of get that by using dict.values() which gives you back a list of values (actually a fancy type of structure that acts like a list) > dict looks like this: > > ``` > {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': > 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, > 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, > 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, > 'request_location': 'Current location'}} > ``` Have you considered creating a class? With that amount of nested dicts etc I'd have thought a class would be a better fit. Then maybe a list of instances. -- 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 From PyTutor at DancesWithMice.info Mon Jan 27 16:10:47 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Tue, 28 Jan 2020 10:10:47 +1300 Subject: [Tutor] Getting first item in dictionary In-Reply-To: References: Message-ID: <95dad860-2e16-02b7-466d-02498faced5e@DancesWithMice.info> On 28/01/20 7:16 AM, Alan Gauld via Tutor wrote: > On 27/01/2020 11:51, S D wrote: >> I have a dictionary which contains one item (?current_location?, which is a >> nested dict) and I would like to access that nested dict. However, I cannot >> use the key as the code will break if a different key is passed, e.g. >> ?different_location". > > That doesn't make much sense. If you cannot use a key to > access the data why did you put it in a dictionary which > is a structure accessed by keys? +1 (however, if we're going to abuse data structures, please see below) > When you say the code will break if you use a wrong key > surely all you should get is a Keyerror? And you can avoid > that by using dict.get(key) which returns None if the key > is not found. (Although using try/except to catch the error > would be better in this scenario because you should want > to know why you have a wrong key and prevent it! > >> How can I access the first item in a dictionary without using a key? > > If that's what you really want you should be using a list > instead of a dictionary. > > But you can kind of get that by using dict.values() which > gives you back a list of values (actually a fancy type > of structure that acts like a list) +1 see below... >> dict looks like this: >> >> ``` >> {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': >> 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, >> 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, >> 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, >> 'request_location': 'Current location'}} >> ``` > > Have you considered creating a class? > With that amount of nested dicts etc I'd have thought > a class would be a better fit. Then maybe a list of instances. +1 python3 Python 3.7.5 (default, Dec 15 2019, 17:54:26) [GCC 9.2.1 20190827 (Red Hat 9.2.1-1)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> d = {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': ... 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, ... 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, ... 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, ... 'request_location': 'Current location'}} >>> list( d.values() )[ 0 ] {'date': '2020-01-27T10:28:24.148Z', 'type_icon': 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, 'request_location': 'Current location'} >>> # alternately >>> d[ list( d.keys() )[ 0 ] ] {'date': '2020-01-27T10:28:24.148Z', 'type_icon': 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, 'request_location': 'Current location'} Why do you have a dict without knowing its key(s)? If calling an API realises this data, surely the call includes sufficient data to be, or to assemble, the key? -- Regards =dn From roel at roelschroeven.net Mon Jan 27 14:30:09 2020 From: roel at roelschroeven.net (Roel Schroeven) Date: Mon, 27 Jan 2020 20:30:09 +0100 Subject: [Tutor] Getting first item in dictionary In-Reply-To: References: Message-ID: S D schreef op 27/01/2020 om 12:51: > I have a dictionary which contains one item (?current_location?, which is a > nested dict) and I would like to access that nested dict. However, I cannot > use the key as the code will break if a different key is passed, e.g. > ?different_location". > > How can I access the first item in a dictionary without using a key? The > dict looks like this: > > ``` > {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'type_icon': > 'partly-cloudy-day', 'description': 'Mostly Cloudy', 'temperature': 68.28, > 'wind': {'speed': 10.48, 'bearing': 178, 'gust': 12.47}, 'rain_prob': 0.02, > 'latitude': '-33.927407', 'longitude': '18.415747', 'request_id': 31364, > 'request_location': 'Current location'}} > ``` If you are really sure that there is only one item in the dictionary, you can simply retrieve the first value without even looking at the key: from pprint import pprint loc_dict = {'current_location': {'date': '2020-01-27T10:28:24.148Z', 'description': 'Mostly Cloudy', 'latitude': '-33.927407', 'longitude': '18.415747', 'rain_prob': 0.02, 'request_id': 31364, 'request_location': 'Current location', 'temperature': 68.28, 'type_icon': 'partly-cloudy-day', 'wind': {'bearing': 178, 'gust': 12.47, 'speed': 10.48}}} first_value = list(loc_dict.values())[0] pprint(first_value) Outputs: {'date': '2020-01-27T10:28:24.148Z', 'description': 'Mostly Cloudy', 'latitude': '-33.927407', 'longitude': '18.415747', 'rain_prob': 0.02, 'request_id': 31364, 'request_location': 'Current location', 'temperature': 68.28, 'type_icon': 'partly-cloudy-day', 'wind': {'bearing': 178, 'gust': 12.47, 'speed': 10.48}} That should extract the one item from a dictionary, if there is only one. And I think it should extract the first item even if there are multiple items, but that "first item" might not be what you expect depending on your expectations and the version of Python you use. -- "Honest criticism is hard to take, particularly from a relative, a friend, an acquaintance, or a stranger." -- Franklin P. Jones Roel Schroeven From PyTutor at DancesWithMice.info Mon Jan 27 19:22:53 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Tue, 28 Jan 2020 13:22:53 +1300 Subject: [Tutor] Getting first item in dictionary In-Reply-To: References: Message-ID: <83ab2af7-6273-714f-6b00-fa4421b76069@DancesWithMice.info> On 28/01/20 8:30 AM, Roel Schroeven wrote: > S D schreef op 27/01/2020 om 12:51: >> I have a dictionary which contains one item (?current_location?, which >> is a >> nested dict) and I would like to access that nested dict. However, I >> cannot >> use the key as the code will break if a different key is passed, e.g. >> ?different_location". >> >> How can I access the first item in a dictionary without using a key? The >> dict looks like this: ... > That should extract the one item from a dictionary, if there is only > one. And I think it should extract the first item even if there are > multiple items, but that "first item" might not be what you expect > depending on your expectations and the version of Python you use. v3.7+ "the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec." https://docs.python.org/3/whatsnew/3.7.html -- Regards =dn From Yuanyuan.A.Olsen at HealthPartners.Com Mon Jan 27 20:10:58 2020 From: Yuanyuan.A.Olsen at HealthPartners.Com (Olsen, Avalow Y) Date: Tue, 28 Jan 2020 01:10:58 +0000 Subject: [Tutor] Jupyternotebook set up Message-ID: <25bf7bb0fd8f4f719541bdf4b716fd3c@REGEXCH05.HealthPartners.int> Hi All, I am using Jupyter Notebook for Python programing through Anaconda. Anaconda Navigator version = 1.9.7 Jupyter Notebook version= 6.0.2 When running the following code, presumably there are 6 entries with the value "Not available" in the frequency. Since the output is a long list, the "Not available" value is omitted in the outcome pane by .. as shown below. cars.horsepower.value_counts() 150.0 22 90.0 19 110.0 19 100.0 16 88.0 14 .. 230.0 1 107.0 1 158.0 1 137.0 1 167.0 1 Name: horsepower, Length: 85, dtype: int64 Here is the proof that "Not available " exists in the horsepower variable cars.horsepower.value_counts().loc["Not available "] 6 There are 6 entries with the value "Not available" My question is how to setup Jupyter Notebook in order to let the whole list of values be shown like below? 150.0 22 90.0 19 110.0 19 100.0 16 88.0 14 105.0 12 95.0 10 85.0 9 70.0 8 145.0 7 Not available 6 84.0 5 .. 230.0 1 107.0 1 158.0 1 137.0 1 167.0 1 Thank you in advance! Ava ________________________________ This e-mail and any files transmitted with it are confidential and are intended solely for the use of the individual or entity to whom they are addressed. If you are not the intended recipient or the individual responsible for delivering the e-mail to the intended recipient, please be advised that you have received this e-mail in error and that any use, dissemination, forwarding, printing, or copying of this e-mail is strictly prohibited. If you have received this communication in error, please return it to the sender immediately and delete the original message and any copy of it from your computer system. If you have any questions concerning this message, please contact the sender. Disclaimer R001.0 From garylarose at outlook.com Mon Jan 27 21:40:43 2020 From: garylarose at outlook.com (Gary LaRose) Date: Tue, 28 Jan 2020 02:40:43 +0000 Subject: [Tutor] Jupyternotebook set up In-Reply-To: <25bf7bb0fd8f4f719541bdf4b716fd3c@REGEXCH05.HealthPartners.int> References: <25bf7bb0fd8f4f719541bdf4b716fd3c@REGEXCH05.HealthPartners.int> Message-ID: It appears you are printing value counts from a pandas dataframe column (series) Check this stackoverflow post https://stackoverflow.com/questions/19124601/pretty-print-an-entire-pandas-series-dataframe#30691921 Try option_context ( from above post ) with pd.option_context('display.max_rows', None): # more options can be specified also print(cars.horsepower.value_counts()) Or try pd.option_context as above with: print(cars[?horsepower?].value_counts()) On Jan 27, 2020, at 8:11 PM, Olsen, Avalow Y wrote: ?Hi All, I am using Jupyter Notebook for Python programing through Anaconda. Anaconda Navigator version = 1.9.7 Jupyter Notebook version= 6.0.2 When running the following code, presumably there are 6 entries with the value "Not available" in the frequency. Since the output is a long list, the "Not available" value is omitted in the outcome pane by .. as shown below. cars.horsepower.value_counts() 150.0 22 90.0 19 110.0 19 100.0 16 88.0 14 .. 230.0 1 107.0 1 158.0 1 137.0 1 167.0 1 Name: horsepower, Length: 85, dtype: int64 Here is the proof that "Not available " exists in the horsepower variable cars.horsepower.value_counts().loc["Not available "] 6 There are 6 entries with the value "Not available" My question is how to setup Jupyter Notebook in order to let the whole list of values be shown like below? 150.0 22 90.0 19 110.0 19 100.0 16 88.0 14 105.0 12 95.0 10 85.0 9 70.0 8 145.0 7 Not available 6 84.0 5 .. 230.0 1 107.0 1 158.0 1 137.0 1 167.0 1 Thank you in advance! Ava ________________________________ This e-mail and any files transmitted with it are confidential and are intended solely for the use of the individual or entity to whom they are addressed. If you are not the intended recipient or the individual responsible for delivering the e-mail to the intended recipient, please be advised that you have received this e-mail in error and that any use, dissemination, forwarding, printing, or copying of this e-mail is strictly prohibited. If you have received this communication in error, please return it to the sender immediately and delete the original message and any copy of it from your computer system. If you have any questions concerning this message, please contact the sender. Disclaimer R001.0 _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.python.org%2Fmailman%2Flistinfo%2Ftutor&data=02%7C01%7C%7C87478238f7294eaaf99208d7a38efe51%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637157706850698976&sdata=05RKoT8ZvgE%2B07PPbFSjlytANDEydgBYt6qPmLIRpGw%3D&reserved=0 From Yuanyuan.A.Olsen at HealthPartners.Com Mon Jan 27 23:01:47 2020 From: Yuanyuan.A.Olsen at HealthPartners.Com (Olsen, Avalow Y) Date: Tue, 28 Jan 2020 04:01:47 +0000 Subject: [Tutor] Jupyternotebook set up In-Reply-To: References: <25bf7bb0fd8f4f719541bdf4b716fd3c@REGEXCH05.HealthPartners.int> Message-ID: Thanks, Gary! You are right. Pandas has its own built-in options for display. The following code works: pd.options.display.max_rows = None pd.options.display.min_rows = None # this line is a must. Otherwise it would still only show head and tail. Really appreciate it! Thank you! Ava From: Gary LaRose [mailto:garylarose at outlook.com] Sent: Monday, January 27, 2020 8:41 PM To: Olsen, Avalow Y Cc: tutor at python.org Subject: [EXTERNAL]Re: [Tutor] Jupyternotebook set up External Email: Don't click links or attachments unless you trust the email. It appears you are printing value counts from a pandas dataframe column (series) Check this stackoverflow post https://stackoverflow.com/questions/19124601/pretty-print-an-entire-pandas-series-dataframe#30691921 Try option_context ( from above post ) with pd.option_context('display.max_rows', None): # more options can be specified also print(cars.horsepower.value_counts()) Or try pd.option_context as above with: print(cars[?horsepower?].value_counts()) On Jan 27, 2020, at 8:11 PM, Olsen, Avalow Y > wrote: ?Hi All, I am using Jupyter Notebook for Python programing through Anaconda. Anaconda Navigator version = 1.9.7 Jupyter Notebook version= 6.0.2 When running the following code, presumably there are 6 entries with the value "Not available" in the frequency. Since the output is a long list, the "Not available" value is omitted in the outcome pane by .. as shown below. cars.horsepower.value_counts() 150.0 22 90.0 19 110.0 19 100.0 16 88.0 14 .. 230.0 1 107.0 1 158.0 1 137.0 1 167.0 1 Name: horsepower, Length: 85, dtype: int64 Here is the proof that "Not available " exists in the horsepower variable cars.horsepower.value_counts().loc["Not available "] 6 There are 6 entries with the value "Not available" My question is how to setup Jupyter Notebook in order to let the whole list of values be shown like below? 150.0 22 90.0 19 110.0 19 100.0 16 88.0 14 105.0 12 95.0 10 85.0 9 70.0 8 145.0 7 Not available 6 84.0 5 .. 230.0 1 107.0 1 158.0 1 137.0 1 167.0 1 Thank you in advance! Ava ________________________________ This e-mail and any files transmitted with it are confidential and are intended solely for the use of the individual or entity to whom they are addressed. If you are not the intended recipient or the individual responsible for delivering the e-mail to the intended recipient, please be advised that you have received this e-mail in error and that any use, dissemination, forwarding, printing, or copying of this e-mail is strictly prohibited. If you have received this communication in error, please return it to the sender immediately and delete the original message and any copy of it from your computer system. If you have any questions concerning this message, please contact the sender. Disclaimer R001.0 _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.python.org%2Fmailman%2Flistinfo%2Ftutor&data=02%7C01%7C%7C87478238f7294eaaf99208d7a38efe51%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637157706850698976&sdata=05RKoT8ZvgE%2B07PPbFSjlytANDEydgBYt6qPmLIRpGw%3D&reserved=0 From mrkarimbo at gmail.com Tue Jan 28 07:37:21 2020 From: mrkarimbo at gmail.com (Karim Beidas) Date: Tue, 28 Jan 2020 15:37:21 +0300 Subject: [Tutor] Runtime Error Message-ID: <5e302b03.1c69fb81.b27ab.8113@mx.google.com> Can you please help me fix this? C:\Users\MSIi\Desktop\Fortnite Py Bot\Fortnite Py Bot\Fortnite PY bot\fortnitepy-bot-master>py fortnite.py Traceback (most recent call last): File "fortnite.py", line 28, in import fortnitepy File "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\__init__.py", line 48, in get_event_loop() File "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\client.py", line 116, in get_event_loop raise RuntimeError('asyncio.ProactorEventLoop is not supported') RuntimeError: asyncio.ProactorEventLoop is not supported C:\Users\MSIi\Desktop\Fortnite Py Bot\Fortnite Py Bot\Fortnite PY bot\fortnitepy-bot-master>cmd /k C:\Users\MSIi\Desktop\Fortnite Py Bot\Fortnite Py Bot\Fortnite PY bot\fortnitepy-bot-master> This is what it says when I try start the start bot file. And why does it say RuntimeError: asyncio.ProactorEventLoop is not supported Please respond as quick as possible. Thank you. Sent from Mail for Windows 10 From alan.gauld at yahoo.co.uk Tue Jan 28 11:14:19 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Tue, 28 Jan 2020 16:14:19 +0000 Subject: [Tutor] Runtime Error In-Reply-To: <5e302b03.1c69fb81.b27ab.8113@mx.google.com> References: <5e302b03.1c69fb81.b27ab.8113@mx.google.com> Message-ID: On 28/01/2020 12:37, Karim Beidas wrote: > File "fortnite.py", line 28, in > import fortnitepy > File "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\__init__.py", line 48, in > get_event_loop() > File "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\client.py", line 116, in get_event_loop > raise RuntimeError('asyncio.ProactorEventLoop is not supported') My immediate suspicion is that there is a missing module. You may need to install another package to make it work. But I'm not familiar with the fortnitepy module, so you would need to check the documentation there for any dependencies. But that's my guess. -- 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 From marc.tompkins at gmail.com Tue Jan 28 11:34:23 2020 From: marc.tompkins at gmail.com (Marc Tompkins) Date: Tue, 28 Jan 2020 08:34:23 -0800 Subject: [Tutor] Runtime Error In-Reply-To: References: <5e302b03.1c69fb81.b27ab.8113@mx.google.com> Message-ID: On Tue, Jan 28, 2020 at 8:15 AM Alan Gauld via Tutor wrote: > On 28/01/2020 12:37, Karim Beidas wrote: > > > File "fortnite.py", line 28, in > > import fortnitepy > > File > "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\__init__.py", > line 48, in > > get_event_loop() > > File > "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\client.py", > line 116, in get_event_loop > > raise RuntimeError('asyncio.ProactorEventLoop is not supported') > > My immediate suspicion is that there is a missing module. > You may need to install another package to make it work. > > But I'm not familiar with the fortnitepy module, so you > would need to check the documentation there for any dependencies. > But that's my guess. > > Looking at the documentation for ProactorEventLoop, there are so many instances of "is not supported" and "is only supported if" that I wonder - is it just that the OP's Windows configuration is subtly different from whoever wrote the fortnitepy module? https://docs.python.org/3.6/library/asyncio-eventloops.html#asyncio.ProactorEventLoop From mats at wichmann.us Tue Jan 28 11:45:56 2020 From: mats at wichmann.us (Mats Wichmann) Date: Tue, 28 Jan 2020 09:45:56 -0700 Subject: [Tutor] Runtime Error In-Reply-To: References: <5e302b03.1c69fb81.b27ab.8113@mx.google.com> Message-ID: On 1/28/20 9:34 AM, Marc Tompkins wrote: > On Tue, Jan 28, 2020 at 8:15 AM Alan Gauld via Tutor > wrote: > >> On 28/01/2020 12:37, Karim Beidas wrote: >> >>> File "fortnite.py", line 28, in >>> import fortnitepy >>> File >> "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\__init__.py", >> line 48, in >>> get_event_loop() >>> File >> "C:\Users\MSIi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\fortnitepy\client.py", >> line 116, in get_event_loop >>> raise RuntimeError('asyncio.ProactorEventLoop is not supported') >> >> My immediate suspicion is that there is a missing module. >> You may need to install another package to make it work. Like the others, never heard of this. But searching a bit gets me this page: https://github.com/xMistt/fortnitepy-bot Which says, with lots of shouting: Install Python 3.6 (suggested, any 3.x version should work, APART FROM 3.8 DO NOT USE 3.8 OR ELSE YOU'LL GET A LOT OF ERRORS.) And the traceback above clearly shows Python 3.8 is in the picture. From alan.gauld at btinternet.com Tue Jan 28 21:46:21 2020 From: alan.gauld at btinternet.com (Alan Gauld) Date: Wed, 29 Jan 2020 02:46:21 -0000 Subject: [Tutor] How to refactor a simple, straightforward script into a "proper" program? In-Reply-To: References: <45f1a074-d8bc-ec13-9757-4686ee60462d@DancesWithMice.info> <928a7db3-50a7-9bca-7ce3-01323222b237@yahoo.co.uk> Message-ID: On 05/01/2020 05:40, boB Stepp wrote: >>> while True: >>> try: >>> return str_converter(input(msg)) >>> except ValueError: >>> if str_converter == int: >>> print("\nPlease enter a whole number!") >>> elif str_converter == date.fromisoformat: >>> print("\nPlease enter a valid date in the following >>> format yyyy-mm-dd!") >> >> And if its not one of those types? I get a ValueError raised then it >> does nothing. It just returns None. That's not very friendly. > > Huh? Joel said something similar. Maybe I am being dense, but it > *does* do something. If the user types in something that is not one > of the allowed types, he gets a helpful message telling him/her what > the program is expecting, and the program loops back to the original > message requesting input. My bad it does not "return None" in the technical sense. It displays nothing just returns to the prompt. Try writing your own string convertor: def myInt(s): return int(s) Now use myInt as the string convertor and see what happens... It does 'work' but is less user friendly. > But it also does date input. And if in the future I wanted other > sorts of input I need only add new string converters for the new data > types. Yes, but the udr of your module will expect to be able to write their own string convertors and they wont work without modifying the module - thats bad. You would need to have a dict of string convertors and error messages so that the error handler could look up the error message using the string convertor as a key: except ValueError: print conv_msg[string_convertor] And the user can then add their own error message: module.conv_msg[myInt] = "Enter an integer" >> This sounds like a reusable function, but in fact its pretty tied into >> the problem at hand. > > Again, I wonder if I am being dense, but my intent with this function > is to handle a variety of input types. I would have to add new > ValueError possibilities for new data types. Is this what bothers > you? Yes because when I download your "industrial strength" module I discover I either have to contact you every time I need a new string converter or I have to fix up the module myself! Neither is good from a users perspective. > Hmm. I see that in case of ValueError it could look elsewhere for > what sort of user friendly error message to display for a particular > value of str_converter. Is this what you are getting at? Yes, see the dict example above. > presented in the "simple" script. I have quickly looked over D. L. > Neil's comments, and, combined with yours, wonder if I should redesign > the whole shebang from the ground up? Maybe this problem does need an > OO approach. You shouldn't need an OO approach for something this small. But you do need a bit of a rethink in tems of splitting out the functions cleanly. And exactly what is published API and what is just internal utility code. > I grudgingly concede your point. Grudgingly because now I have to > figure out how to accomplish this. ~(:>)) I think exceptions are the way to go. >> ppd = calc_pages_per_day(bp,rp,due) >> >> Maybe its just me but the calc seems implied by the function call. > > But I thought the great preference was for function and method names > to be verbs not nouns? Hmmm, yes. OK I'll give you that one :-) >> Would you really say page if it was 0.5 pages per day? > > Actually, yes. Just as I would say a half page per day. OTOH, I > would say zero pages per day. Maybe I need to dust off a grammar > book... a half page is not the same as 0,5 pages. The a forces it to single. So in your code you need to add an 'a' before the value to make it grammatical. Thats more work, but doable, but what would you say for a value like 0.82? What kind of fraction is that? Are you going to convert it to say a 82 hundredth of a page? Good luck with that... >> I'd only use 'page' if it was exactly 1. >> >> But since its a float you need to use an epsilon value. >> So >> >> e = 0.0001 >> if 1-e < pages_per_day < 1+e: >> word_to_display = 'page' >> else: word_to_display = 'pages' > > Do I really need an epsilon value for this particular case? I am not > seeing where my intentions would fail here. Can you provide an > example? This is because the computer cannot accurately represent floats so you may get a value like 0.999999999 or 1.00000001 when you want to treat it as 1. So you either use an epsilon or you do a float to some minimal number of decimals. > thought if the user wishes to be silly here, why not? Its not about the user, its the floating point division in a digital computer. > I originally in my code did not use mixed quotes. But when I saved > the file my new code formatting tyrant, Black, changed my consistent > quoting style to mixed quotes. Apparently it insists on all double > quotes unless there are embedded quotes within when it converts those > to single quotes. Yukkkk! >> 2) if a function isn't generally reusable make it obvious >> by putting an underscore in front of the name. (or >> make it generally reusable...usually much harder :-) > > This is where I was hoping for some concrete, helpful examples. I do > indeed find this "much harder". Everyone does. Management routinely ask devs to create reusable code, but studies show that writing reusable code is from 3-5 times more expensive than writing single-use code. Some studies say more like 10 times if you factor in maintenance costs too. And there is no easy answer, it takes painful lessons and experience, and you'll still mess it up! >> 3) separate presentation and logic. > > Again, I find this not so easy. This is easier and just takes a little practice to get used to it. Basically every function should either do display or logic. If its doing logic then return values that the caller can use/display. > FEATURE CREEP! Actually sounds like an interesting addition. But I > don't much like eReaders myself. I didn't used to but, on holiday, I save a ton of weight by taking a Kindle. And having got used to them I now occasionally even read tech books on it. Although code formatting often gets messed up. But I'm currently reading a book on curses programming in C and converting it to python - maybe to become a page in my tutor someday - and that's a Kindle book. -- 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 From alan.gauld at btinternet.com Tue Jan 28 22:30:11 2020 From: alan.gauld at btinternet.com (Alan Gauld) Date: Wed, 29 Jan 2020 03:30:11 -0000 Subject: [Tutor] stalker-m3u.py In-Reply-To: References: Message-ID: <05a307ee-d835-48ef-9ff0-95a062b89956@btinternet.com> On 23/12/2019 09:27, Aleksa wrote: > Error: Please upgrade your API plan to use filters or paging. > > NOT WORK..... What doesn't work? When you upgraded your API plan to use filters or paging did your code break? Did you get a different error message? Did the wrong data come back? And is it the same using filters as it is with paging? We have no idea what we are looking for. Personally I don't even know what the library does, nor what paging and filters entails. But even if I did I couldn't help without more details. > Is there another way I suspect you'd need to ask the package/API provider. This is not a standard part of Python so the likelihood of anyone on this list using it is quite small. > def grab_file (IP,PORT,FILE): > print ("[*] Testing: "+IP+" on Port: "+PORT+"[*]\n") > try: Your code has been mangled by the email system, you need to post in plain text to preserve formatting. -- 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 From bob at ralexander.it Wed Jan 29 11:37:23 2020 From: bob at ralexander.it (Robert Alexander) Date: Wed, 29 Jan 2020 17:37:23 +0100 Subject: [Tutor] Remove some known text around text I need to keep Message-ID: Dear friends, I have 200 text files in which a doctor has ?tagged? parts of the text as symptom, sign or therapy (in Italian SINTOMO, SEGNO, TERAPIA) as follows: [SEGNO: edemi declivi >> a sinistra] [SINTOMO: non lamenta dispnea] [SINTOMO: paziente sintomatica per dispnea moderata] [SEGNO: Non edemi] [SEGNO: calo di 2 kg (55,8 kg)] [TERAPIA: ha ridotto la terapia diuretica] [TERAPIA: Lieve riduzione della terapia diuretica] [TERAPIA: Lieve riduzione della terapia diuretica] and so forth. These lines can span over a single text line and I need to remove just the tags and rewrite the original text without them. To give you an example on the same lines as above what I need to write back in the new ?clean? files are like the following: edemi declivi >> a sinistra] non lamenta dispnea] paziente sintomatica per dispnea moderata] Non edemi] calo di 2 kg (55,8 kg)] ha ridotto la terapia diuretica] Lieve riduzione della terapia diuretica] ?Lieve riduzione della terapia diuretica] if there was a newline in the text I should respect it and rewrite it out. I am not 100% sure about thehandling of the space between the tags (eg [SEGNO:) and the first word within it. What would you recommend? Regular expressions? Have a tutorial or document I can study? Thank you very much. From alan.gauld at yahoo.co.uk Wed Jan 29 13:46:32 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Wed, 29 Jan 2020 18:46:32 +0000 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: On 29/01/2020 16:37, Robert Alexander wrote: ] > [SINTOMO: paziente sintomatica per dispnea moderata] > [SEGNO: Non edemi] > [SEGNO: calo di 2 kg (55,8 kg)] > [TERAPIA: ha ridotto la terapia diuretica] > the same lines as above what I need to write back in the new ?clean? files are like the following: > paziente sintomatica per dispnea moderata] > Non edemi] > calo di 2 kg (55,8 kg)] > ha ridotto la terapia diuretica] Look at the partition method of strings: partition(...) S.partition(sep) -> (head, sep, tail) Search for the separator sep in S, and return the part before it, the separator itself, and the part after it. If the separator is not found, return S and two empty strings. Example: >>> "FRED: Heris a partition: that looks like s".partition(':') ('FRED', ':', ' Heris a partition: that looks like s') >>> It should work with embedded \n too. -- 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 From gogonegro at gmail.com Wed Jan 29 13:51:13 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Wed, 29 Jan 2020 10:51:13 -0800 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: Thanks Alan, Good to learn :) I?ll look into it even though at a later reflection I might do this without python with just an awk command killing all [TAG: and ] chars from the files :) Take care, Robert On 29 January 2020 at 19:46:55, Alan Gauld via Tutor (tutor at python.org) wrote: On 29/01/2020 16:37, Robert Alexander wrote: ] > [SINTOMO: paziente sintomatica per dispnea moderata] > [SEGNO: Non edemi] > [SEGNO: calo di 2 kg (55,8 kg)] > [TERAPIA: ha ridotto la terapia diuretica] > the same lines as above what I need to write back in the new ?clean? files are like the following: > paziente sintomatica per dispnea moderata] > Non edemi] > calo di 2 kg (55,8 kg)] > ha ridotto la terapia diuretica] Look at the partition method of strings: partition(...) S.partition(sep) -> (head, sep, tail) Search for the separator sep in S, and return the part before it, the separator itself, and the part after it. If the separator is not found, return S and two empty strings. Example: >>> "FRED: Heris a partition: that looks like s".partition(':') ('FRED', ':', ' Heris a partition: that looks like s') >>> It should work with embedded \n too. -- 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 at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From joel.goldstick at gmail.com Wed Jan 29 16:09:41 2020 From: joel.goldstick at gmail.com (Joel Goldstick) Date: Wed, 29 Jan 2020 16:09:41 -0500 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: On Wed, Jan 29, 2020 at 1:51 PM Robert Alexander wrote: > > Thanks Alan, > Good to learn :) > > I?ll look into it even though at a later reflection I might do this without > python with just an awk command killing all [TAG: and ] chars from the > files :) > > Take care, > Robert > > > On 29 January 2020 at 19:46:55, Alan Gauld via Tutor (tutor at python.org) > wrote: > > On 29/01/2020 16:37, Robert Alexander wrote: > ] > > [SINTOMO: paziente sintomatica per dispnea moderata] > > [SEGNO: Non edemi] > > [SEGNO: calo di 2 kg (55,8 kg)] > > [TERAPIA: ha ridotto la terapia diuretica] > > > the same lines as above what I need to write back in the new ?clean? > files are like the following: > > > paziente sintomatica per dispnea moderata] > > Non edemi] > > calo di 2 kg (55,8 kg)] > > ha ridotto la terapia diuretica] > > Look at the partition method of strings: > > partition(...) > S.partition(sep) -> (head, sep, tail) > > Search for the separator sep in S, and return the part before it, > the separator itself, and the part after it. If the separator is not > found, return S and two empty strings. > > > Example: > > >>> "FRED: Heris a partition: that looks like s".partition(':') > ('FRED', ':', ' Heris a partition: that looks like s') > >>> > > It should work with embedded \n too. > > > -- > 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 at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor I didn't know about str.partition, but in your case str.split(":") will return a list with the part you don't want first, followed by the part you want >>> s = "[SEGNO: edemi declivi >> a sinistra]" >>> s.split(":") ['[SEGNO', ' edemi declivi >> a sinistra]'] >>> -- Joel Goldstick http://joelgoldstick.com/blog http://cc-baseballstats.info/stats/birthdays From PyTutor at danceswithmice.info Wed Jan 29 17:05:04 2020 From: PyTutor at danceswithmice.info (DL Neil) Date: Thu, 30 Jan 2020 11:05:04 +1300 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: >>> [SINTOMO: paziente sintomatica per dispnea moderata] >>> [SEGNO: Non edemi] >>> [SEGNO: calo di 2 kg (55,8 kg)] >>> [TERAPIA: ha ridotto la terapia diuretica] >> >>> the same lines as above what I need to write back in the new ?clean? >> files are like the following: >> >>> paziente sintomatica per dispnea moderata] >>> Non edemi] >>> calo di 2 kg (55,8 kg)] >>> ha ridotto la terapia diuretica] >> >> Look at the partition method of strings: >> >> partition(...) >> S.partition(sep) -> (head, sep, tail) ... > I didn't know about str.partition, but in your case str.split(":") > will return a list with the part you don't want first, followed by the > part you want > >>>> s = "[SEGNO: edemi declivi >> a sinistra]" >>>> s.split(":") > ['[SEGNO', ' edemi declivi >> a sinistra]'] Some call it "pedantry", others "attention to detail" but don't forget the closing square-bracket! Depending upon the read-mechanism (whether the end-of-line character(s) are included - NB OpSys variations then apply!), a string-slice to remove the last character(s), possibly even before the above, will do-the-trick! -- Regards =dn From cs at cskk.id.au Wed Jan 29 17:19:44 2020 From: cs at cskk.id.au (Cameron Simpson) Date: Thu, 30 Jan 2020 09:19:44 +1100 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: <20200129221944.GA55881@cskk.homeip.net> On 29Jan2020 10:51, Robert Alexander wrote: >Thanks Alan, >Good to learn :) > >I?ll look into it even though at a later reflection I might do this without >python with just an awk command killing all [TAG: and ] chars from the >files :) sed will be even smaller. Cheers, Cameron Simpson From alan.gauld at yahoo.co.uk Wed Jan 29 18:28:32 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Wed, 29 Jan 2020 23:28:32 +0000 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: On 29/01/2020 21:09, Joel Goldstick wrote: > I didn't know about str.partition, but in your case str.split(":") > will return a list with the part you don't want first, followed by the > part you want The problem with split is that if the string has a colon in it split will split into 3 parts, not 2. You can specify the maxsplits but that?s extra complexity. Partition only splits on the first occurrence. split also loses the separator - although not an isssue here - but partition retains the whole string. -- 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 From alan.gauld at yahoo.co.uk Wed Jan 29 18:30:38 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Wed, 29 Jan 2020 23:30:38 +0000 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: On 29/01/2020 22:05, DL Neil via Tutor wrote: >>>> [SINTOMO: paziente sintomatica per dispnea moderata] >>>> [SEGNO: Non edemi] >>>> [SEGNO: calo di 2 kg (55,8 kg)] >>>> [TERAPIA: ha ridotto la terapia diuretica] >>> >>>> the same lines as above what I need to write back in the new ?clean? >>> files are like the following: >>> >>>> paziente sintomatica per dispnea moderata] >>>> Non edemi] >>>> calo di 2 kg (55,8 kg)] >>>> ha ridotto la terapia diuretica] > Some call it "pedantry", others "attention to detail" but don't forget > the closing square-bracket! Although, looking at the OPs original post, he retained the closing bracket... But I think he did intend to remove them... So either a slice, or better, rstrip('] ') will deal with those. -- 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 From PyTutor at DancesWithMice.info Wed Jan 29 19:35:12 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Thu, 30 Jan 2020 13:35:12 +1300 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: References: Message-ID: <157e9bfb-a48a-20c8-cd2b-3a0532a8a092@DancesWithMice.info> On 30/01/20 12:30 PM, Alan Gauld via Tutor wrote: > On 29/01/2020 22:05, DL Neil via Tutor wrote: >>>>> [SINTOMO: paziente sintomatica per dispnea moderata] >>>>> [SEGNO: Non edemi] >>>>> [SEGNO: calo di 2 kg (55,8 kg)] >>>>> [TERAPIA: ha ridotto la terapia diuretica] >>>> >>>>> the same lines as above what I need to write back in the new ?clean? >>>> files are like the following: >>>> >>>>> paziente sintomatica per dispnea moderata] >>>>> Non edemi] >>>>> calo di 2 kg (55,8 kg)] >>>>> ha ridotto la terapia diuretica] > >> Some call it "pedantry", others "attention to detail" but don't forget >> the closing square-bracket! > > Although, looking at the OPs original post, he retained the closing > bracket... > But I think he did intend to remove them... > So either a slice, or better, rstrip('] ') will deal with those. Hah! You're correct - I read the word "tags". Further, the idea of using RegEx seemed to support the idea of finding "contents" rather than trimming a prefix... In Italian, do parentheses, brackets, braces, etc, not come in pairs? -- Regards =dn From gogonegro at gmail.com Thu Jan 30 02:06:20 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Thu, 30 Jan 2020 08:06:20 +0100 Subject: [Tutor] Remove some known text around text I need to keep In-Reply-To: <157e9bfb-a48a-20c8-cd2b-3a0532a8a092@DancesWithMice.info> References: <157e9bfb-a48a-20c8-cd2b-3a0532a8a092@DancesWithMice.info> Message-ID: Dear tutors, thanks a lot for the input. It taught me a few new tricks :) The OP just forgot to eliminate the closing square bracket in his example. May he suffer consequences for this oversight! and yes we usually tend to use parenthesis, whatever their shape, always in pairs, also in this country ;) A good day to all of you. Robert On 30 January 2020 at 01:35:41, David L Neil via Tutor (tutor at python.org) wrote: On 30/01/20 12:30 PM, Alan Gauld via Tutor wrote: > On 29/01/2020 22:05, DL Neil via Tutor wrote: >>>>> [SINTOMO: paziente sintomatica per dispnea moderata] >>>>> [SEGNO: Non edemi] >>>>> [SEGNO: calo di 2 kg (55,8 kg)] >>>>> [TERAPIA: ha ridotto la terapia diuretica] >>>> >>>>> the same lines as above what I need to write back in the new ?clean? >>>> files are like the following: >>>> >>>>> paziente sintomatica per dispnea moderata] >>>>> Non edemi] >>>>> calo di 2 kg (55,8 kg)] >>>>> ha ridotto la terapia diuretica] > >> Some call it "pedantry", others "attention to detail" but don't forget >> the closing square-bracket! > > Although, looking at the OPs original post, he retained the closing > bracket... > But I think he did intend to remove them... > So either a slice, or better, rstrip('] ') will deal with those. Hah! You're correct - I read the word "tags". Further, the idea of using RegEx seemed to support the idea of finding "contents" rather than trimming a prefix... In Italian, do parentheses, brackets, braces, etc, not come in pairs? -- Regards =dn _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From gogonegro at gmail.com Thu Jan 30 02:29:40 2020 From: gogonegro at gmail.com (Robert Alexander) Date: Thu, 30 Jan 2020 08:29:40 +0100 Subject: [Tutor] Namespaces in Python Message-ID: Not 100% sure this is appropriate, if not please correct me and won?t do it in the future but I thought some people on this list might like to know about a very well written Medium article on namespaces in Python: https://medium.com/better-programming/namespacing-with-python-79574d125564 From frahmanf89 at gmail.com Thu Jan 30 12:22:19 2020 From: frahmanf89 at gmail.com (faslur rahman) Date: Thu, 30 Jan 2020 20:22:19 +0300 Subject: [Tutor] Regarding learning python Message-ID: I am faslur rahman from Sri Lanka, last six months i was trying to learn python programming by my self, But unfortunately i did not found any good resource to learn about python. Even i try from python website, there I got some books to refer and this email ID also. can you please advise me where to learn python full course?. Thank you From suresh.gm at gmail.com Thu Jan 30 15:23:47 2020 From: suresh.gm at gmail.com (Panchanathan Suresh) Date: Thu, 30 Jan 2020 12:23:47 -0800 Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys and values of Dictionary1) Message-ID: Hi Everyone, I spent a lot of time on this, trying out many things, but I am unable to create a new dictionary with the users as keys and a list of their groups as values. Input Dictionary is {"local": ["admin", "userA"],"public": ["admin", "userB"],"administrator": ["admin"] } Expected Output Dictionary is {"userA": ["admin","public"], "userB": ["admin"], "administrator": ["admin"]} --- How to create an unique group for a particular user? --- And then how to add this user key and the user's value (which will be the list of groups the user belongs to) ***** def groups_per_user(group_dictionary): user_groups = {} groups = [] # Go through group_dictionary for group,user in group_dictionary.items(): # Now go through the users in the group for user1 in user: groups.append(group) print(user1,groups) return(user_groups) print(groups_per_user({"local": ["admin", "userA"],"public": ["admin", "userB"],"administrator": ["admin"] })) ***** Thanks so much From alan.gauld at yahoo.co.uk Thu Jan 30 16:32:58 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 30 Jan 2020 21:32:58 +0000 Subject: [Tutor] Regarding learning python In-Reply-To: References: Message-ID: On 30/01/2020 17:22, faslur rahman wrote: > I am faslur rahman from Sri Lanka, last six months i was trying to learn > python programming by my self, But unfortunately i did not found any good > resource to learn about python. It would help if you can be more specific about what was wrong with the resources you tried. Also what is your background? Can you program in other languages, is it just python you ate struggling with? Or are you new to programming itself, in which case there are a lot of concepts to learn as well as how they are done in python. > Even i try from python website, there I got > some books to refer and this email ID also. The official tutorial is very good if you can already program in another language. But it is not so good for total beginners. Thats where the non programmers page comes into play... > can you please advise me where to learn python full course?. Well, obviously I can't resist the opportunity to push my tutorial - see my signature below... :-) And if you get stuck (in any tutorial, not just mine) ask specific questions here. Send the code that doesn't work, any errors you get, tell us what you expected to happen and what did happen./ Then we can help/explain. Also tell us whether you are using Python 2 or 3. I strongly recommend using Python 3 and making sure your chosen tutorial also uses it. -- 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 From mats at wichmann.us Thu Jan 30 16:47:15 2020 From: mats at wichmann.us (Mats Wichmann) Date: Thu, 30 Jan 2020 14:47:15 -0700 Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys and values of Dictionary1) In-Reply-To: References: Message-ID: On 1/30/20 1:23 PM, Panchanathan Suresh wrote: > Hi Everyone, > > I spent a lot of time on this, trying out many things, but I am unable to > create a new dictionary with the users as keys and a list of their groups > as values. > > Input Dictionary is {"local": ["admin", "userA"],"public": ["admin", > "userB"],"administrator": ["admin"] } > Expected Output Dictionary is {"userA": ["admin","public"], "userB": > ["admin"], "administrator": ["admin"]} > > --- How to create an unique group for a particular user? > --- And then how to add this user key and the user's value (which will be > the list of groups the user belongs to) > > ***** > def groups_per_user(group_dictionary): > user_groups = {} > groups = [] > # Go through group_dictionary > for group,user in group_dictionary.items(): > # Now go through the users in the group > for user1 in user: > groups.append(group) > print(user1,groups) > > return(user_groups) > > print(groups_per_user({"local": ["admin", "userA"],"public": ["admin", > "userB"],"administrator": ["admin"] })) > > ***** Hopefully this isn't a homework problem :) You're not adding anything to your new dictionary. What you want to do is use the user as a key, and a list of groups as the value; the trick is that you may need to add several times to a user's entry, so you need to handle both the case of "user not yet in dictionary" and the case of "user has a grouplist, add to that list". As a quick and dirty hack, like this (I changed a name a bit for clarity): for group, userlist in group_dictionary.items(): # Now go through the users in the group for user in userlist: try: user_groups[user].append(group) except KeyError: user_groups[user] = [group] From PyTutor at DancesWithMice.info Thu Jan 30 17:51:38 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Fri, 31 Jan 2020 11:51:38 +1300 Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys and values of Dictionary1) In-Reply-To: References: Message-ID: Greetings Suresh! What is your level of Python skill? ... skills with data structures? On 31/01/20 9:23 AM, Panchanathan Suresh wrote: > Hi Everyone, > > I spent a lot of time on this, trying out many things, but I am unable to > create a new dictionary with the users as keys and a list of their groups > as values. > > Input Dictionary is {"local": ["admin", "userA"],"public": ["admin", > "userB"],"administrator": ["admin"] } > Expected Output Dictionary is {"userA": ["admin","public"], "userB": > ["admin"], "administrator": ["admin"]} > > --- How to create an unique group for a particular user? > --- And then how to add this user key and the user's value (which will be > the list of groups the user belongs to) > > ***** > def groups_per_user(group_dictionary): > user_groups = {} > groups = [] > # Go through group_dictionary > for group,user in group_dictionary.items(): > # Now go through the users in the group > for user1 in user: > groups.append(group) > print(user1,groups) > > return(user_groups) > > print(groups_per_user({"local": ["admin", "userA"],"public": ["admin", > "userB"],"administrator": ["admin"] })) > > ***** This idea might be a bit more than you really want/are ready to try: The problem with inheriting from dict and list in Python https://treyhunner.com/2019/04/why-you-shouldnt-inherit-from-list-and-dict-in-python/ Although the article addresses a completely different set of interests, the example used is class TwoWayDict( ... ) which implements a dict having not only entries in the form key:value, but adds the complementary?inverted value:key. He calls it "bi-directional": "When a key-value pair is added, the key maps to the value but the value also maps to the key.". This is not *exactly* the spec in this post, in that Trey is using a single dict, but there is absolutely no reason why the article's solution(s) couldn't be extended so that the TwoWayDict class actually manages two dicts - one key:value and the other value:key. This would then allow extension to the 1:m relationship*: forward_dict ~ key1:value1,value2,value3 inverted_dict ~ value1:key1,key2 value2:key2,key5 ... The philosophical difference in this approach lies in the *assembling* of a data structure that it is prepared for future use/'ready for action'; rather than using the algorithmic 'translation' currently envisaged, in-between assembling the original dict, and being able to make use of its inversion! ie the complexity is in the 'writing' to the dict (or 'loading' it), rather than in the 'reading' (or getting items from it)! OTOH understanding and coding the above will require some reasonably-advanced Python knowledge and skills - hence the opening questions! * apologies if this term is unfamiliar: "one to many relationship", refers to the idea that (here) one key may yield multiple values, cf "one to one", eg your ID/SocialSecurityNR which can only apply to you (and vice-versa). NB PSL's collections.UserDict stores its (single dict) keys and values in a dict called "data" (IIRC). Thus adding a parallel, but reversed, dict, eg "inverse" should be quite smooth to implement. (YMMV ? famous last words) -- Regards =dn From breamoreboy at gmail.com Thu Jan 30 18:52:37 2020 From: breamoreboy at gmail.com (Mark Lawrence) Date: Thu, 30 Jan 2020 23:52:37 +0000 Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys and values of Dictionary1) In-Reply-To: References: Message-ID: On 30/01/2020 20:23, Panchanathan Suresh wrote: > Hi Everyone, > > I spent a lot of time on this, trying out many things, but I am unable to > create a new dictionary with the users as keys and a list of their groups > as values. > > Input Dictionary is {"local": ["admin", "userA"],"public": ["admin", > "userB"],"administrator": ["admin"] } > Expected Output Dictionary is {"userA": ["admin","public"], "userB": > ["admin"], "administrator": ["admin"]} > I have no idea what you're trying to achieve as your input key "local" doesn't appear in the output, your "public" key does and the "administrator": ["admin"] key/value pair appears in both. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence From dewshrs at gmail.com Thu Jan 30 16:31:46 2020 From: dewshrs at gmail.com (Dewan Shrestha) Date: Thu, 30 Jan 2020 15:31:46 -0600 Subject: [Tutor] Regarding learning python In-Reply-To: References: Message-ID: You can try coursera On Thu, Jan 30, 2020 at 3:23 PM faslur rahman wrote: > I am faslur rahman from Sri Lanka, last six months i was trying to learn > python programming by my self, But unfortunately i did not found any good > resource to learn about python. Even i try from python website, there I got > some books to refer and this email ID also. > can you please advise me where to learn python full course?. > > Thank you > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > From suresh.gm at gmail.com Thu Jan 30 18:10:59 2020 From: suresh.gm at gmail.com (Panchanathan Suresh) Date: Thu, 30 Jan 2020 15:10:59 -0800 Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys and values of Dictionary1) In-Reply-To: References: Message-ID: Thanks a lot Mats and David! I am not yet that familiar with data structures. This statement indeed works: user_groups[user].append(group) But how can we use 'append' keyword for a dictionary? Is not append used only for appending to a List? Is it because Group is a already a List in the original dictionary? This is a bit confusing... Can we append to the list of dictionary values for a given key, using Append command? On Thu, Jan 30, 2020 at 2:52 PM wrote: > Send Tutor mailing list submissions to > tutor at python.org > > To subscribe or unsubscribe via the World Wide Web, visit > https://mail.python.org/mailman/listinfo/tutor > or, via email, send a message with subject or body 'help' to > tutor-request at python.org > > You can reach the person managing the list at > tutor-owner at python.org > > When replying, please edit your Subject line so it is more specific > than "Re: Contents of Tutor digest..." > Today's Topics: > > 1. Regarding learning python (faslur rahman) > 2. Input is Dictionary1, Output is Dictionary2 (using keys and > values of Dictionary1) (Panchanathan Suresh) > 3. Re: Regarding learning python (Alan Gauld) > 4. Re: Input is Dictionary1, Output is Dictionary2 (using keys > and values of Dictionary1) (Mats Wichmann) > 5. Re: Input is Dictionary1, Output is Dictionary2 (using keys > and values of Dictionary1) (David L Neil) > > > > ---------- Forwarded message ---------- > From: faslur rahman > To: tutor at python.org > Cc: > Bcc: > Date: Thu, 30 Jan 2020 20:22:19 +0300 > Subject: [Tutor] Regarding learning python > I am faslur rahman from Sri Lanka, last six months i was trying to learn > python programming by my self, But unfortunately i did not found any good > resource to learn about python. Even i try from python website, there I got > some books to refer and this email ID also. > can you please advise me where to learn python full course?. > > Thank you > > > > > ---------- Forwarded message ---------- > From: Panchanathan Suresh > To: tutor at python.org > Cc: > Bcc: > Date: Thu, 30 Jan 2020 12:23:47 -0800 > Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys > and values of Dictionary1) > Hi Everyone, > > I spent a lot of time on this, trying out many things, but I am unable to > create a new dictionary with the users as keys and a list of their groups > as values. > > Input Dictionary is {"local": ["admin", "userA"],"public": ["admin", > "userB"],"administrator": ["admin"] } > Expected Output Dictionary is {"userA": ["admin","public"], "userB": > ["admin"], "administrator": ["admin"]} > > --- How to create an unique group for a particular user? > --- And then how to add this user key and the user's value (which will be > the list of groups the user belongs to) > > ***** > def groups_per_user(group_dictionary): > user_groups = {} > groups = [] > # Go through group_dictionary > for group,user in group_dictionary.items(): > # Now go through the users in the group > for user1 in user: > groups.append(group) > print(user1,groups) > > return(user_groups) > > print(groups_per_user({"local": ["admin", "userA"],"public": ["admin", > "userB"],"administrator": ["admin"] })) > > ***** > > Thanks so much > > > > > ---------- Forwarded message ---------- > From: Alan Gauld > To: tutor at python.org > Cc: > Bcc: > Date: Thu, 30 Jan 2020 21:32:58 +0000 > Subject: Re: [Tutor] Regarding learning python > On 30/01/2020 17:22, faslur rahman wrote: > > I am faslur rahman from Sri Lanka, last six months i was trying to learn > > python programming by my self, But unfortunately i did not found any good > > resource to learn about python. > > It would help if you can be more specific about what was > wrong with the resources you tried. > > Also what is your background? Can you program in other languages, > is it just python you ate struggling with? Or are you new to > programming itself, in which case there are a lot of concepts > to learn as well as how they are done in python. > > > Even i try from python website, there I got > > some books to refer and this email ID also. > > The official tutorial is very good if you can already program > in another language. But it is not so good for total beginners. > Thats where the non programmers page comes into play... > > > can you please advise me where to learn python full course?. > > Well, obviously I can't resist the opportunity to push my > tutorial - see my signature below... :-) > > And if you get stuck (in any tutorial, not just mine) ask > specific questions here. Send the code that doesn't work, > any errors you get, tell us what you expected to happen > and what did happen./ Then we can help/explain. > > Also tell us whether you are using Python 2 or 3. > I strongly recommend using Python 3 and making sure > your chosen tutorial also uses it. > > -- > 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 > > > > > > > ---------- Forwarded message ---------- > From: Mats Wichmann > To: tutor at python.org > Cc: > Bcc: > Date: Thu, 30 Jan 2020 14:47:15 -0700 > Subject: Re: [Tutor] Input is Dictionary1, Output is Dictionary2 (using > keys and values of Dictionary1) > On 1/30/20 1:23 PM, Panchanathan Suresh wrote: > > Hi Everyone, > > > > I spent a lot of time on this, trying out many things, but I am unable to > > create a new dictionary with the users as keys and a list of their groups > > as values. > > > > Input Dictionary is {"local": ["admin", "userA"],"public": ["admin", > > "userB"],"administrator": ["admin"] } > > Expected Output Dictionary is {"userA": ["admin","public"], "userB": > > ["admin"], "administrator": ["admin"]} > > > > --- How to create an unique group for a particular user? > > --- And then how to add this user key and the user's value (which will be > > the list of groups the user belongs to) > > > > ***** > > def groups_per_user(group_dictionary): > > user_groups = {} > > groups = [] > > # Go through group_dictionary > > for group,user in group_dictionary.items(): > > # Now go through the users in the group > > for user1 in user: > > groups.append(group) > > print(user1,groups) > > > > return(user_groups) > > > > print(groups_per_user({"local": ["admin", "userA"],"public": ["admin", > > "userB"],"administrator": ["admin"] })) > > > > ***** > > Hopefully this isn't a homework problem :) > > You're not adding anything to your new dictionary. What you want to do > is use the user as a key, and a list of groups as the value; the trick > is that you may need to add several times to a user's entry, so you need > to handle both the case of "user not yet in dictionary" and the case of > "user has a grouplist, add to that list". As a quick and dirty hack, > like this (I changed a name a bit for clarity): > > for group, userlist in group_dictionary.items(): > # Now go through the users in the group > for user in userlist: > try: > user_groups[user].append(group) > except KeyError: > user_groups[user] = [group] > > > > > > ---------- Forwarded message ---------- > From: David L Neil > To: tutor at python.org > Cc: > Bcc: > Date: Fri, 31 Jan 2020 11:51:38 +1300 > Subject: Re: [Tutor] Input is Dictionary1, Output is Dictionary2 (using > keys and values of Dictionary1) > Greetings Suresh! > What is your level of Python skill? > ... skills with data structures? > > > On 31/01/20 9:23 AM, Panchanathan Suresh wrote: > > Hi Everyone, > > > > I spent a lot of time on this, trying out many things, but I am unable to > > create a new dictionary with the users as keys and a list of their groups > > as values. > > > > Input Dictionary is {"local": ["admin", "userA"],"public": ["admin", > > "userB"],"administrator": ["admin"] } > > Expected Output Dictionary is {"userA": ["admin","public"], "userB": > > ["admin"], "administrator": ["admin"]} > > > > --- How to create an unique group for a particular user? > > --- And then how to add this user key and the user's value (which will be > > the list of groups the user belongs to) > > > > ***** > > def groups_per_user(group_dictionary): > > user_groups = {} > > groups = [] > > # Go through group_dictionary > > for group,user in group_dictionary.items(): > > # Now go through the users in the group > > for user1 in user: > > groups.append(group) > > print(user1,groups) > > > > return(user_groups) > > > > print(groups_per_user({"local": ["admin", "userA"],"public": ["admin", > > "userB"],"administrator": ["admin"] })) > > > > ***** > > > This idea might be a bit more than you really want/are ready to try: > > The problem with inheriting from dict and list in Python > > https://treyhunner.com/2019/04/why-you-shouldnt-inherit-from-list-and-dict-in-python/ > > Although the article addresses a completely different set of interests, > the example used is class TwoWayDict( ... ) which implements a dict > having not only entries in the form key:value, but adds the > complementary?inverted value:key. > > He calls it "bi-directional": "When a key-value pair is added, the key > maps to the value but the value also maps to the key.". > > This is not *exactly* the spec in this post, in that Trey is using a > single dict, but there is absolutely no reason why the article's > solution(s) couldn't be extended so that the TwoWayDict class actually > manages two dicts - one key:value and the other value:key. > > This would then allow extension to the 1:m relationship*: > forward_dict ~ key1:value1,value2,value3 > inverted_dict ~ value1:key1,key2 > value2:key2,key5 > ... > > The philosophical difference in this approach lies in the *assembling* > of a data structure that it is prepared for future use/'ready for > action'; rather than using the algorithmic 'translation' currently > envisaged, in-between assembling the original dict, and being able to > make use of its inversion! > ie the complexity is in the 'writing' to the dict (or 'loading' it), > rather than in the 'reading' (or getting items from it)! > > OTOH understanding and coding the above will require some > reasonably-advanced Python knowledge and skills - hence the opening > questions! > > > * apologies if this term is unfamiliar: "one to many relationship", > refers to the idea that (here) one key may yield multiple values, cf > "one to one", eg your ID/SocialSecurityNR which can only apply to you > (and vice-versa). > > NB PSL's collections.UserDict stores its (single dict) keys and values > in a dict called "data" (IIRC). Thus adding a parallel, but reversed, > dict, eg "inverse" should be quite smooth to implement. > (YMMV ? famous last words) > -- > Regards =dn > > _______________________________________________ > Tutor maillist - Tutor at python.org > https://mail.python.org/mailman/listinfo/tutor > From alan.gauld at yahoo.co.uk Thu Jan 30 19:37:10 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Fri, 31 Jan 2020 00:37:10 +0000 Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys and values of Dictionary1) In-Reply-To: References: Message-ID: On 30/01/2020 23:10, Panchanathan Suresh wrote: > But how can we use 'append' keyword for a dictionary? Is not append used > only for appending to a List? > Is it because Group is a already a List in the original dictionary? This is > a bit confusing... > > Can we append to the list of dictionary values for a given key, using > Append command? Yes, because the list inside the dictionary is still a list, no different to any other list. so you use the dictionary key to get a reference to the list and then call the lists append method. Note that append is not a keyword it is a method of the list object. A keyword is something reserved by the language, like 'for', 'while', 'def' etc. The method or function names can be overridden. for example, you could, if you wished, create your own print() function def print(s):pass that would hide the normal print. Then you couldn't print anything(*)! Not very useful, but possible(in Python3) because print is just a function name not a keyword. (*)Actually you can by referencing the builtin function via the __builtins__ module. But you get the idea, I hope. -- 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 From mats at wichmann.us Thu Jan 30 20:55:14 2020 From: mats at wichmann.us (Mats Wichmann) Date: Thu, 30 Jan 2020 18:55:14 -0700 Subject: [Tutor] Regarding learning python In-Reply-To: References: Message-ID: <4ee936d4-27dc-75dc-53f1-0d8ac0b429c5@wichmann.us> On 1/30/20 2:32 PM, Alan Gauld via Tutor wrote: > The official tutorial is very good if you can already program > in another language. But it is not so good for total beginners. > Thats where the non programmers page comes into play... The problem with any recommendation any one of us could give is that everyone learns differently. What works for some doesn't work for others. Specifically, a vast number of really good Python programmers have learned the language from the "official" tutorial, but I find parts of it terrible now I look back at it. For example, the chapter on classes gets rolling by contrasting Python's approach to Smalltalk and Modula-3. As if that would be of any actual use to almost anyone these days. That sets a bad tone right off the bat - Modula-3? So What. I really don't like pointing people at it any longer (Modula-3 not being the main reason, of course). I'd point to Alan's material over it, to be sure :) From PyTutor at DancesWithMice.info Thu Jan 30 21:24:03 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Fri, 31 Jan 2020 15:24:03 +1300 Subject: [Tutor] Input is Dictionary1, Output is Dictionary2 (using keys and values of Dictionary1) In-Reply-To: References: Message-ID: <747843ae-4197-3daa-6364-37a78c6b8baf@DancesWithMice.info> On 31/01/20 12:10 PM, Panchanathan Suresh wrote: > Thanks a lot Mats and David! I am not yet that familiar with data > structures. In which case the earlier suggestion of creating a 'clever class' was inappropriate for such a skill-level. Don't worry though, you'll get there... > This statement indeed works: user_groups[user].append(group) great! > But how can we use 'append' keyword for a dictionary? Is not append used > only for appending to a List? > Is it because Group is a already a List in the original dictionary? This is > a bit confusing... More-so for us because the code has (presumably) changed since last shown to us! Please copy-paste the (relevant part of the) code from your editor/the REPL into future email msgs (plus any err.msgs, if necessary). > Can we append to the list of dictionary values for a given key, using > Append command? Please review Built-in Types (https://docs.python.org/3/library/stdtypes.html) You will find that a list is a sequence type, whereas a dict is a mapping type. What does this mean? Lists/sequences are intended to be accessed serially, ie one element at a time, one after the other. So, if you have a play-list of tunes/songs, the intent is to listen to one after the other. Whereas dicts/maps are direct-access structures. In this case, if each song has a name or an ID, the individual song may be accessed, using its name as a "key", without (first) accessing every other (previous) element. Note also that a list is comprised of elements which are individual values (objects), eg >>> l = [ 1, "b", 3.14159 ] >>> len( l ) 3 but a dict's elements come in pairs - there must be a "key" and a "value", eg >>> d = { "question":Suresh", "answer":"dn" } >>> len( d ) 2 So, to access an individual element, because the structures are different, the access mechanisms must necessarily also differ: >>> l[ 1 ] 'b' - the list is a "sequence" therefore we "address" the individual element by its "index", its position within the sequence (an integer, starting from zero). >>> d[ "question" ] 'Suresh' - the dict is a "mapping" thus we identify the individual element using its key - the key "maps" directly to a particular element with no reference to any other elements 'before' or 'after' it in the dict. Right, let's talk about "append"! The word means quite literally 'add to the end'. End of what? If we were talking about a shopping list, then 'the end' is the last entry - so we write the new item at 'the bottom' of the shopping list (and don't forget to take the shopping list with you to the store!). So l.append( ... ) has meaning! What about a dictionary? Let's say you have a (book) dictionary, but it's getting out-of-date with modern jargon or may not have the complicated, fancy, words we use in computer jargon. Say you want to add a new word to that dictionary. Would you write it on the last page? No, better to add it 'in the right place'. So, if the new word was "meta-class", you would want to locate it somewhere after "meat" and before "moat". Thus d.append() has NO meaning - at the end? Instead try: d[ "meta-class" ] = "Class inheriting from type" We are now back to the earlier term: "direct access". In addition to "retrieving" data directly/by key, we are able to "store" data directly as well! Good news! We don't have to work-out where in the dict Python stores things, as long as we know the keys we want to use! So, there are some "methods" which can be used with both lists and dicts, but there are others which can only be used with lists and not dicts, and yet more which can be used with dicts but not lists. Accordingly, it follows that lists have a particular purpose and dicts have another. (the earlier web.ref lists methods for each, and more) Just the same as a Green-billed Coucal and a Kiwi are both birds (not quite data structures). Both have wings and thus a flap 'method'. So, you'd think they could both fly. Not true! Different purposes, different lives, different islands! NB the descriptions (above) are just that. The contents of lists can be much more complex - and often are; plus the rules about what we can use as "keys" for a dict, are significantly more involved (for example). Have you spotted Alan's tutorial, or looked at Python courses on coursera.org or edx.org? (all free-access!) They might help you to learn under-pinnings like data structures, as well as Python itself... -- Regards =dn From PyTutor at DancesWithMice.info Thu Jan 30 21:25:45 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Fri, 31 Jan 2020 15:25:45 +1300 Subject: [Tutor] Regarding learning python In-Reply-To: References: Message-ID: On 31/01/20 10:31 AM, Dewan Shrestha wrote: > You can try coursera Did you? Which series of courses? Please provide feedback... -- Regards =dn From PyTutor at DancesWithMice.info Thu Jan 30 22:01:54 2020 From: PyTutor at DancesWithMice.info (David L Neil) Date: Fri, 31 Jan 2020 16:01:54 +1300 Subject: [Tutor] Regarding learning python In-Reply-To: <4ee936d4-27dc-75dc-53f1-0d8ac0b429c5@wichmann.us> References: <4ee936d4-27dc-75dc-53f1-0d8ac0b429c5@wichmann.us> Message-ID: <5073ad0c-7286-6038-5fb4-faf560739b19@DancesWithMice.info> On 31/01/20 2:55 PM, Mats Wichmann wrote: > On 1/30/20 2:32 PM, Alan Gauld via Tutor wrote: > >> The official tutorial is very good if you can already program >> in another language. But it is not so good for total beginners. >> Thats where the non programmers page comes into play... > > The problem with any recommendation any one of us could give is that > everyone learns differently. What works for some doesn't work for > others. Specifically, a vast number of really good Python programmers > have learned the language from the "official" tutorial, but I find parts > of it terrible now I look back at it. For example, the chapter on > classes gets rolling by contrasting Python's approach to Smalltalk and > Modula-3. As if that would be of any actual use to almost anyone these > days. That sets a bad tone right off the bat - Modula-3? So What. I > really don't like pointing people at it any longer (Modula-3 not being > the main reason, of course). I'd point to Alan's material over it, to > be sure :) OK, so will you now tell us what was the (large) present Alan sent you for Christmas? (joke) You are absolutely correct. However, coders, and indeed systems programmers, rarely make the best tutors/user documentation authors - and after coding some update to Python (or a PSL library), how much time would be 'left over' for writing docs anyway? (thinking of a number of places where I've worked...) There is a Python project team dedicated to improving 'our documentation'. (who would no doubt appreciate any and all 'open source' contributions!) Yes, it is intensely annoying to find design (only) rationale, eg we wanted this because SmallTalk had it, jtest, ... Such requires expansion in user docs - why did SmallTalk have it? So, say that! What advantages does it confer? etc, etc. This goes 'double' when such blather (largely more to establish the author's 'credentials' than to enable the reader) appears in some article or tutorial, purporting to "explain". That said, I take the feed from PlanetPython. The number of 'updates' which are announced, but which don't explain what the library actually does, is a frequent source of dismay (and motivation to hit the Del(ete) key!) Imagine approaching a business-owner to justify porting feature-xyz from SmallTalk to Python? The process is flawed - and likely the project floored! The business-boys do have advice to offer though: to quickly grab attention, they talk about an "elevator pitch". (https://www.mindtools.com/pages/article/elevator-pitch.htm) Sadly, writing 'grabbing' docs, ANN(nouncements), training courses, etc, is an under-stated skill (and one which too many of our 'authors', do not actually possess/exhibit). -- Regards =dn From robertvstepp at gmail.com Thu Jan 30 22:56:39 2020 From: robertvstepp at gmail.com (boB Stepp) Date: Thu, 30 Jan 2020 21:56:39 -0600 Subject: [Tutor] Corrupt file(s) likelihood and prevention? Message-ID: I just finished reading a thread on the main list that got a little testy. The part that is piquing my interest was some discussion of data files perhaps getting corrupted. I have wondered about this off and on. First what is the likelihood nowadays of a file becoming corrupted, and, second, what steps should one take to prevent/minimize this from happening? I notice that one of the applications I work with at my job apparently uses checksums to detect when one of its files gets modified from outside of the application. -- boB From cs at cskk.id.au Fri Jan 31 00:01:34 2020 From: cs at cskk.id.au (Cameron Simpson) Date: Fri, 31 Jan 2020 16:01:34 +1100 Subject: [Tutor] Corrupt file(s) likelihood and prevention? In-Reply-To: References: Message-ID: <20200131050134.GA24378@cskk.homeip.net> On 30Jan2020 21:56, boB Stepp wrote: >I just finished reading a thread on the main list that got a little >testy. The part that is piquing my interest was some discussion of >data files perhaps getting corrupted. I have wondered about this off >and on. First what is the likelihood nowadays of a file becoming >corrupted, and, second, what steps should one take to prevent/minimize >this from happening? I can't speak to specific likelihoods, but these days things should be pretty stable. Hard drives have long mean time to failure, memory is often ECC (eg fixing single bit errors and noticing mulitbit errors), and data sizes are large enough that if failures were common nothing would work. (Of course that goes both ways.) Some filesystems checksum their blocks (ZFS in particular sticks in my mind for this, and it is in its way a flexible RAID, meaning a bad block gets fixed from a good one as discovered). >I notice that one of the applications I work >with at my job apparently uses checksums to detect when one of its >files gets modified from outside of the application. That can often be worth doing. It depends on the app. If a file is modified outside the app then many things it may be assuming in its internal state may need discarding or checking (conceptually: flush cached knowledge). Leaving aside system failures, there's also malice. In another life we used to checksum the OS files on a regular basis looking for changed files. Cheers, Cameron Simpson From alan.gauld at yahoo.co.uk Fri Jan 31 07:15:40 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Fri, 31 Jan 2020 12:15:40 +0000 Subject: [Tutor] Corrupt file(s) likelihood and prevention? In-Reply-To: References: Message-ID: On 31/01/2020 03:56, boB Stepp wrote: > I just finished reading a thread on the main list that got a little > testy. The part that is piquing my interest was some discussion of > data files perhaps getting corrupted. I have wondered about this off > and on. First what is the likelihood nowadays of a file becoming > corrupted, The likelihood is 100% - if you wait long enough! Corruption is caused by several things including the physical deterioration of the media upon which it is stored. It can be due to sun spot activity, local radiation sources, high magnetic fields and heat related stress on the platters. In addition there is corruption due to software misoperation. Buffers filling or overflowing, power outages at the wrong moment(either to the computer itself or to a card due to vibration) The bottom line is that corruption is inevitable eventually. But the average life of a file on a single device is not infinite, and most files get moved around (another potential cause of corruption of course!) Every time you copy or move a file to a new location you effectively start the clock from zero. And most files time out in their value too. So you get to a point where nobody cares. But if you do care, take backups. Corruption is real and not going to go away any time soon. Modern storage devices and technologies are vastly more reliable than they were 50 years ago, or even 20 years ago. But nothing is foolproof. > and, second, what steps should one take to prevent/minimize > this from happening? Reliable disks, RAID configured, use checksums to detect it. And backup, then backup your backups. And test that your retrieval strategy works. (It is astonishing how many times I've found sites where they took regular backups but never actually tried restoring anything until it was too late. Then discovered their retrieval mechanism was broken...) > I notice that one of the applications I work > with at my job apparently uses checksums to detect when one of its > files gets modified from outside of the application. A wise precaution for anything that needs to be checked, either for modification by external systems or where any corruption is critical. Remember that corruption is not always (not often!) fatal to a file. If its just a single byte getting twiddled then it might just change a value - Your bank balance switches from $1000 to $9000 or vice versa, say! Checksums are about the only reliable way to detect those kinds of corruption. But in the real world don't stress too much. It is a rare occurrence and I've only had it cause real problems a dozen or so times in 40 years. But when computers hold millions of files, many of which are millions or even billions of bytes, some corruption is bound to occur eventually. -- 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 From PyTutor at danceswithmice.info Fri Jan 31 18:20:11 2020 From: PyTutor at danceswithmice.info (DL Neil) Date: Sat, 1 Feb 2020 12:20:11 +1300 Subject: [Tutor] Corrupt file(s) likelihood and prevention? In-Reply-To: References: Message-ID: On 31/01/20 4:56 PM, boB Stepp wrote: > I just finished reading a thread on the main list that got a little > testy. The part that is piquing my interest was some discussion of > data files perhaps getting corrupted. I have wondered about this off > and on. First what is the likelihood nowadays of a file becoming > corrupted, and, second, what steps should one take to prevent/minimize > this from happening? I notice that one of the applications I work > with at my job apparently uses checksums to detect when one of its > files gets modified from outside of the application. Hi boB, (I haven't looked at it again, since posting yesterday) Yes the thread did seem to become argumentative - unnecessarily (IMHO) because the 'right answer' only exists in the OP's mind, not any of 'us'/ours. However...) To put this question into the context of the original post, the issue was not so much the likelihood of a file becoming corrupt, per-se; it was a question of whether the mechanism of (one of) the proposed 'solutions' exposed the user to an higher, or perhaps less-obvious, risk of corruption. One idea was to keep a dynamic list within a (disk) file. As the program ran, this list would be extended and/or altered to record progress/success. - when a file is opened (+a or +w) it must be closed properly. - the OP specified being able to interrupt operation using Ctrl+c. Put those two together and the risk becomes 'obvious' (to those with the experience/expertise to see it). Some mitigation was offered. As an alternative, using an RDBMS was suggested. This was disputed. The other alternative (me causing 'trouble' again), was to query the need to use such a blunt instrument (Ctrl+c) to 'exit'. So, if I've interpreted your interest correctly, the pertinent part of that (perhaps less apparent in your question) is: what can we do about the risk of corruption, when files which may have been updated, have not been/cannot be, closed properly? "Here be dragons!" (and by that I don't mean that Alan is about to take you on a hike around Snowdonia/Wales...) -- Regards =dn From adam_slow at hotmail.com Fri Jan 31 13:30:48 2020 From: adam_slow at hotmail.com (Adam Slowley) Date: Fri, 31 Jan 2020 18:30:48 +0000 Subject: [Tutor] Printing Bold Text Message-ID: So I?m a beginner, learning Python for the first time so this might be a dumb question but here goes. I?m trying to print a text in bold. For example: def main(): name=input("Enter your name:") print("You entered for name a value of",name) I want whatever the user inputs as his/her name to be printed in Bold From adam_slow at hotmail.com Fri Jan 31 19:04:42 2020 From: adam_slow at hotmail.com (Adam Slowley) Date: Sat, 1 Feb 2020 00:04:42 +0000 Subject: [Tutor] Corrupt file(s) likelihood and prevention? In-Reply-To: References: , Message-ID: What is this ? I never asked about corrupt files ________________________________ From: Tutor on behalf of DL Neil via Tutor Sent: Friday, January 31, 2020 6:20:11 PM To: tutor Subject: Re: [Tutor] Corrupt file(s) likelihood and prevention? On 31/01/20 4:56 PM, boB Stepp wrote: > I just finished reading a thread on the main list that got a little > testy. The part that is piquing my interest was some discussion of > data files perhaps getting corrupted. I have wondered about this off > and on. First what is the likelihood nowadays of a file becoming > corrupted, and, second, what steps should one take to prevent/minimize > this from happening? I notice that one of the applications I work > with at my job apparently uses checksums to detect when one of its > files gets modified from outside of the application. Hi boB, (I haven't looked at it again, since posting yesterday) Yes the thread did seem to become argumentative - unnecessarily (IMHO) because the 'right answer' only exists in the OP's mind, not any of 'us'/ours. However...) To put this question into the context of the original post, the issue was not so much the likelihood of a file becoming corrupt, per-se; it was a question of whether the mechanism of (one of) the proposed 'solutions' exposed the user to an higher, or perhaps less-obvious, risk of corruption. One idea was to keep a dynamic list within a (disk) file. As the program ran, this list would be extended and/or altered to record progress/success. - when a file is opened (+a or +w) it must be closed properly. - the OP specified being able to interrupt operation using Ctrl+c. Put those two together and the risk becomes 'obvious' (to those with the experience/expertise to see it). Some mitigation was offered. As an alternative, using an RDBMS was suggested. This was disputed. The other alternative (me causing 'trouble' again), was to query the need to use such a blunt instrument (Ctrl+c) to 'exit'. So, if I've interpreted your interest correctly, the pertinent part of that (perhaps less apparent in your question) is: what can we do about the risk of corruption, when files which may have been updated, have not been/cannot be, closed properly? "Here be dragons!" (and by that I don't mean that Alan is about to take you on a hike around Snowdonia/Wales...) -- Regards =dn _______________________________________________ Tutor maillist - Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From alan.gauld at yahoo.co.uk Fri Jan 31 19:20:21 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Sat, 1 Feb 2020 00:20:21 +0000 Subject: [Tutor] Corrupt file(s) likelihood and prevention? In-Reply-To: References: Message-ID: On 01/02/2020 00:04, Adam Slowley wrote: > What is this ? I never asked about corrupt files > > ________________________________ > From: Tutor on behalf of DL Neil via Tutor You are subscribed to the list which means you get all of the tutor emails, not just the ones you send/receive. That's how we learn from each other - by being exposed to issues we may not even have considered before. If the traffic is too high for you then you have the option of choosing to receive the digest instead if individual messages. All of the same material but in a single message instead of a stream. -- 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 From alan.gauld at yahoo.co.uk Fri Jan 31 19:38:15 2020 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Sat, 1 Feb 2020 00:38:15 +0000 Subject: [Tutor] Printing Bold Text In-Reply-To: References: Message-ID: On 31/01/2020 18:30, Adam Slowley wrote: > So I?m a beginner, learning Python for the first time so this might be a dumb question but here goes. Not a dumb question, but a surprisingly difficult one. > I?m trying to print a text in bold. For example: > > def main(): > name=input("Enter your name:") > print("You entered for name a value of",name) > > I want whatever the user inputs as his/her name to be printed in Bold The solution to this is rather complex. Python reads and writes to stdin/out on the host computer. Python does not know anything about what stdin/out are other than that it can send text to them or read text from them. It could be an old style teleprinter, a VDT, a window within a GUI(the most common these days) or a speech unit for the visually impaired. So python has no way of making text bold or otherwise - indeed in the text reader case it is a meaningless concept, unless it shouted the bold bits perhaps? So how is bold text handled? It's down to the device and if that is a typical text console in a GUI window then it will have a set of control codes that affect how text is displayed. The three most common sets of control codes are the VT100(Unix land), the 3270 (IBM mainframes) and the ANSI terminal (from MS DOS). Once you know which terminal you are using you can write those codes out. Let's assume you've looked up the reference material and stored their values in some variables: BOLD and NORMAL Then you can do something like: print(NORMAL, "Enter your name: ", BOLD) name = input() print(NORMAL) That will switch the terminal between BOLD and NORMAL as required. But its all a bit tedious and the minute your user switches terminal then it breaks! [ Note: It is slightly easier to do this in a GUI environment because you can control the fonts used for the various parts of the screen. So you could have the label asking for the name use normal font and the entry box into which you type the reply use a bold font. but then you have all the extra complexity of building a GUI!] As a response to this difficulty the curses library was created in the 1980's(?) and it has been ported to most environments so that you can use different font styles and even colours in a non GUI terminal. It still requires writing out specific text settings like the BOLD and NORMAL above but at least the codes are standard and it will work across terminal types without change. curses is a standard module with Python on MacOS and Unix but not on Windows. (You need to download a third party version for Windows, or use the cygwin version of Python). Most of us just learn to live with not having bold/underline etc when working in a terminal. It makes life much easier! -- 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 From cs at cskk.id.au Fri Jan 31 21:05:21 2020 From: cs at cskk.id.au (Cameron Simpson) Date: Sat, 1 Feb 2020 13:05:21 +1100 Subject: [Tutor] Corrupt file(s) likelihood and prevention? In-Reply-To: References: Message-ID: <20200201020521.GA6289@cskk.homeip.net> On 01Feb2020 00:20, Alan Gauld wrote: >On 01/02/2020 00:04, Adam Slowley wrote: >> What is this ? I never asked about corrupt files >> ________________________________ >> From: Tutor on behalf of DL Neil via Tutor > >You are subscribed to the list which means you get all of the >tutor emails, not just the ones you send/receive. That's how >we learn from each other - by being exposed to issues we may >not even have considered before. > >If the traffic is too high for you then you have the option >of choosing to receive the digest instead if individual messages. >All of the same material but in a single message instead of >a stream. BTW, I discourage this approach. Far better to have your mailer filter the tutor messages off into their own folder. Replying to digests brings much pain for all, because replies are not direct replies to a specific message. Cheers, Cameron Simpson