From phillor9 at gmail.com Wed Dec 1 01:24:58 2021 From: phillor9 at gmail.com (Phil) Date: Wed, 1 Dec 2021 17:24:58 +1100 Subject: [Tutor] pylint(too-many-nested-blocks) In-Reply-To: <34dc6d5f-99f7-7826-95f3-1e5965ddfb87@DancesWithMice.info> References: <2c34791d-dc21-4070-82d2-1c98d67616ce@gmail.com> <703ee90c-2d81-5ea3-91aa-7b2a399e4531@DancesWithMice.info> <34dc6d5f-99f7-7826-95f3-1e5965ddfb87@DancesWithMice.info> Message-ID: <2306a13a-99e0-39b6-dd10-29c3d2381f45@gmail.com> Thank you again dn, Cameron and Alex for more useful information and links. > OTOH, the non-practical pursuit of perfection leads to one never being > 'finished'! That's why I revisited this project. Indecently, I've reduced the complexity of the function that I posted by splitting it into two and, as a bonus, I cured a problem that prevented the code from breaking out of a nested loop. I do remember reading somewhere many years ago that a function should fit onto a screen without scrolling. That's about 20 lines in my case. > There are (apparently) employers who will look at your portfolio of work > (should you ever be applying for such a job), Not at my age, thankfully. > Incidentally, there's an impression that you've adopted a 'grey gypsy' > life-style. That's correct. My wife and I first hit the road during May 2005. > However, if you can gain decent Internet access, perhaps an > online course might confer similar additional/alternate benefit? While I haven't enrolled in any on-line courses while travelling I have completed one rather lengthy course of Python exercises. I do have an extensive range of mostly dated on-line books, however, having them and reading them is not the same thing. > I cheerfully blather-on about pytest and TDD. I know of pytest, although I've never looked into it's use. Something else to study. > Are you using an IDE such as PyCharm or Codium (the F/LOSS-y alternative > to VS-Code)? I've tried several IDEs over the years and have settled on VS Code. I only use a small fraction of it's capabilities and I do like it's debugger. I still use IDLE if I want to test something simple and I do find it's debugger to be useful. > (https://www.youtube.com/watch?v=UANN2Eu6ZnM) Well worth the time - and > maybe you have a YouTube downloader if Internet-reliability is an issue.. Two of my favourite down-loaders are Firefox plug-ins. > Back to the earlier question about IDEs. I'm using PyCharm at the moment I have used the community version but VS Code's all-in-one features won me over. -- Regards, Phil From phillor9 at gmail.com Wed Dec 1 01:32:10 2021 From: phillor9 at gmail.com (Phil) Date: Wed, 1 Dec 2021 17:32:10 +1100 Subject: [Tutor] pylint(too-many-nested-blocks) In-Reply-To: References: Message-ID: <228d8097-c645-e85a-4de8-c80a7e2863d6@gmail.com> Thanks Cameron. > Doctest is pretty simple, though it lends itself mostly to pretty simple > tests. Simple is good and I see that there are lots of tutorials on it's use. That and reducing cylomatic complexity will keep me occupied for quite some time. -- Regards, Phil From PyTutor at DancesWithMice.info Wed Dec 1 03:38:52 2021 From: PyTutor at DancesWithMice.info (dn) Date: Wed, 1 Dec 2021 21:38:52 +1300 Subject: [Tutor] OT: pylint(too-many-nested-blocks) In-Reply-To: <2306a13a-99e0-39b6-dd10-29c3d2381f45@gmail.com> References: <2c34791d-dc21-4070-82d2-1c98d67616ce@gmail.com> <703ee90c-2d81-5ea3-91aa-7b2a399e4531@DancesWithMice.info> <34dc6d5f-99f7-7826-95f3-1e5965ddfb87@DancesWithMice.info> <2306a13a-99e0-39b6-dd10-29c3d2381f45@gmail.com> Message-ID: <1f6d0f42-7fdf-d8d4-8e9d-dc20df49bc28@DancesWithMice.info> >> Incidentally, there's an impression that you've adopted a 'grey gypsy' >> life-style. > That's correct. My wife and I first hit the road during May 2005. >> ? However, if you can gain decent Internet access, perhaps an >> online course might confer similar additional/alternate benefit? In case you speak Python better than "Oz" (or "Oztrarlian") the slang term: "grey gypsy" (amongst others), refers to the significant number of retirees/superannuitants/pensioners who have traded suburbia for a mobile-home, camper-van, or caravan; and are enjoying their 'twilight years' in travel, new experiences, meeting new people, ... Something else which is likely to surprise, is that Australia (by itself) offers a huge expanse in which to travel. The "How big is Australia" web page (https://www.virtualoceania.net/australia/maps/how-big-is-australia.shtml) overlays maps of Europe and USA for direct-comparison. -- Regards, =dn From joao.oliveira at ufob.edu.br Wed Dec 1 09:23:48 2021 From: joao.oliveira at ufob.edu.br (Joao Carlos Silva de Oliveira Matos) Date: Wed, 1 Dec 2021 11:23:48 -0300 Subject: [Tutor] Send SOAP to Proxy In-Reply-To: References: Message-ID: It does, but I get the 200 message saying that my IP is not allowed. I'm starting to think that's it's an issue with the VPN not allowing me to request to the WebService. But I do need to pass through the proxy because it's the only client that can receive the data. Em ter., 30 de nov. de 2021 ?s 19:35, Cameron Simpson escreveu: > On 29Nov2021 18:59, Joao Carlos Silva de Oliveira Matos < > joao.oliveira at ufob.edu.br> wrote: > >I've been trying to access a WSDL Service via a SOAP protocol. But first I > >need to get to a proxy in my University because the server will only > accept > >it as a client. > > Skipping the complexities of the library, if it is using the builtin > Python libraries underneath I expect it to honour the normal environment > variables $http_proxy and $https_proxy, meaning your code does not need > to specify a proxy. > > If you set these in your environment and try your code with no proxy > specification in it, do things work? > > Bourne shell syntax to set these: > > http_proxy='http://proxy-ip-adress:8080' > https_proxy='http://proxy-ip-adress:8080' > export http_proxy https_proxy > > If you set them in your environment, do things work (provided you strip > the settings out of the inline code itself i.e. comment out the > "session.proxies" stuff? > > Cheers,Cameron Simpson > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > -- [image: Ficheiro:Bras?o da UFOB.png ? Wikip?dia, a enciclop?dia livre] Jo?o Carlos Silva de Oliveira Matos Bolsista de Inova??o e Tecnologia PROFNIT - Centro das Humanidades - UFOB Mat. 2020100150 From cs at cskk.id.au Wed Dec 1 15:29:27 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Thu, 2 Dec 2021 07:29:27 +1100 Subject: [Tutor] Send SOAP to Proxy In-Reply-To: References: Message-ID: On 01Dec2021 11:23, Joao Carlos Silva de Oliveira Matos wrote: >It does, but I get the 200 message saying that my IP is not allowed. Does it tell you which IP it thinks you have? If it is your own IP then the proxy settings are probably being ignored. >I'm >starting to think that's it's an issue with the VPN not allowing me to >request to the WebService. If you're gettings a 200 response then you're probably talking to the web service. >But I do need to pass through the proxy because >it's the only client that can receive the data. I'd start with a little lower level testing first, maybe using wget or curl command line tools to manually do a single SOAP request. They both honour proxy setting environment variables and also accept command line arguments to specify it. That should let you see different behaviours with different settings. Cheers, Cameron Simpson From joao.oliveira at ufob.edu.br Wed Dec 1 15:54:54 2021 From: joao.oliveira at ufob.edu.br (Joao Carlos Silva de Oliveira Matos) Date: Wed, 1 Dec 2021 17:54:54 -0300 Subject: [Tutor] Send SOAP to Proxy In-Reply-To: References: Message-ID: NIce tip, I'm gonna do it the next time. Em qua., 1 de dez. de 2021 ?s 17:31, Cameron Simpson escreveu: > On 01Dec2021 11:23, Joao Carlos Silva de Oliveira Matos < > joao.oliveira at ufob.edu.br> wrote: > >It does, but I get the 200 message saying that my IP is not allowed. > > Does it tell you which IP it thinks you have? If it is your own IP then > the proxy settings are probably being ignored. > > >I'm > >starting to think that's it's an issue with the VPN not allowing me to > >request to the WebService. > > If you're gettings a 200 response then you're probably talking to the > web service. > > >But I do need to pass through the proxy because > >it's the only client that can receive the data. > > I'd start with a little lower level testing first, maybe using wget or > curl command line tools to manually do a single SOAP request. They both > honour proxy setting environment variables and also accept command line > arguments to specify it. That should let you see different behaviours > with different settings. > > Cheers, > Cameron Simpson > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > -- [image: Ficheiro:Bras?o da UFOB.png ? Wikip?dia, a enciclop?dia livre] Jo?o Carlos Silva de Oliveira Matos Bolsista de Inova??o e Tecnologia PROFNIT - Centro das Humanidades - UFOB Mat. 2020100150 From phillor9 at gmail.com Wed Dec 1 19:24:57 2021 From: phillor9 at gmail.com (Phil) Date: Thu, 2 Dec 2021 11:24:57 +1100 Subject: [Tutor] function return values Message-ID: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> I was quite pleased with myself that I had reduced the complexity of a function until I tried to reuse the same code elsewhere. I had the return values set to some dummy values and that was good in this case. However, this idea failed when I tried to test if the pairs are in the same column. So, what is the correct way to handle a case where there isn't a valid pair, row or list value to return? ??? def boxPairsInSameRow(self, pairs, x, y): ??????? col_list = [] ??????? for item, count in pairs.items(): ??????????? if count == 2:? # then item is a pair candidate ??????????????? for row in range(x, x + 3): ??????????????????? col_list = [] ??????????????????? for col in range(y, y + 3): ??????????????????????? if item in self.solution[row][col]: ??????????????????????????? col_list.append(col) ??????????????????? return item, row, col_list ??????? return None, None, None # item, row and col_list were originally dummy values This is the calling function: ??????????? item, row, column_list = self.boxPairsInSameRow(candidate_pairs, x , y) ??????????? if item is not None and row is not None and column_list is not None: ??????????? ??? do this only if the returned values are valid. In other words, this is a pair in a row. Maybe the boxPairsInSameRow function is still too complex? The aim is to feed in the pairs, from a counter dictionary, and the row and column starting coordinates and have it return the pairs.item, the row that the item is on and a list containing the two columns for that row position. Once the pairs have been found on a row then there is no need to search the remaining row or rows. It all sounds simple, however, evidently it's not simple enough. -- Regards, Phil From PyTutor at DancesWithMice.info Wed Dec 1 22:34:52 2021 From: PyTutor at DancesWithMice.info (dn) Date: Thu, 2 Dec 2021 16:34:52 +1300 Subject: [Tutor] function return values In-Reply-To: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> Message-ID: <34ce759a-a45e-21cc-0705-c210cbacaa63@DancesWithMice.info> On 02/12/2021 13.24, Phil wrote: > I was quite pleased with myself that I had reduced the complexity of a > function until I tried to reuse the same code elsewhere. I had the > return values set to some dummy values and that was good in this case. > However, this idea failed when I tried to test if the pairs are in the > same column. So, what is the correct way to handle a case where there > isn't a valid pair, row or list value to return? > > ??? def boxPairsInSameRow(self, pairs, x, y): > ??????? col_list = [] > > ??????? for item, count in pairs.items(): > ??????????? if count == 2:? # then item is a pair candidate > ??????????????? for row in range(x, x + 3): > ??????????????????? col_list = [] > ??????????????????? for col in range(y, y + 3): > ??????????????????????? if item in self.solution[row][col]: > ??????????????????????????? col_list.append(col) > ??????????????????? return item, row, col_list > ??????? return None, None, None # item, row and col_list were originally > dummy values > > This is the calling function: > > ??????????? item, row, column_list = > self.boxPairsInSameRow(candidate_pairs, x , y) > ??????????? if item is not None and row is not None and column_list is > not None: > ??????????? ??? do this only if the returned values are valid. In other > words, this is a pair in a row. > > Maybe the boxPairsInSameRow function is still too complex? The aim is to > feed in the pairs, from a counter dictionary, and the row and column > starting coordinates and have it return the pairs.item, the row that the > item is on and a list containing the two columns for that row position. > Once the pairs have been found on a row then there is no need to search > the remaining row or rows. > > It all sounds simple, however, evidently it's not simple enough. Suggest creating another function which returns a boolean. Then use that to initiate (or not) the scan. Perhaps along these lines: if is_pair_in_row( item, row, column_list ): ( item, row, column_list, ) = self.boxPairsInSameRow(candidate_pairs, x , y) which requires: def is_pair_in_row( item, row, column_list ): """Docstring.""" return item and row and column_list NB am not guaranteeing that I have understood which argument/parameter/identifier goes where! NBB asking "if xyz" is the same as "if xyz is not None" - a concept known as "truthiness" (falsiness) NBBB Python's PEP-008 (a guide (maybe) and not a "standard") for naming identifiers and functions: box_pairs_in_same_row() -- Regards, =dn From wlfraed at ix.netcom.com Thu Dec 2 00:00:05 2021 From: wlfraed at ix.netcom.com (Dennis Lee Bieber) Date: Thu, 02 Dec 2021 00:00:05 -0500 Subject: [Tutor] Non sequitur -- Re: function return values References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> <34ce759a-a45e-21cc-0705-c210cbacaa63@DancesWithMice.info> Message-ID: On Thu, 2 Dec 2021 16:34:52 +1300, dn via Tutor declaimed the following: >NB am not guaranteeing that I have understood which >argument/parameter/identifier goes where! > >NBB asking "if xyz" is the same as "if xyz is not None" - a concept >known as "truthiness" (falsiness) > >NBBB Python's PEP-008 (a guide (maybe) and not a "standard") for naming >identifiers and functions: > >box_pairs_in_same_row() Since NB -> nota bene translates as "note well", NBB would be "note well well" etc. Seems that "NNB" would be more correct for emphasis -- "note note well" Though in truth, I doubt this follows the pattern of PS -> post script, in which PPS -> post post script does make logical sense (vs PSS -> post script script) -- Wulfraed Dennis Lee Bieber AF6VN wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ From PyTutor at DancesWithMice.info Thu Dec 2 00:57:39 2021 From: PyTutor at DancesWithMice.info (dn) Date: Thu, 2 Dec 2021 18:57:39 +1300 Subject: [Tutor] Non sequitur -- Re: function return values In-Reply-To: References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> <34ce759a-a45e-21cc-0705-c210cbacaa63@DancesWithMice.info> Message-ID: <0a8492a8-86d3-550b-702e-4edb16fbf5a8@DancesWithMice.info> On 02/12/2021 18.00, Dennis Lee Bieber wrote: > On Thu, 2 Dec 2021 16:34:52 +1300, dn via Tutor > declaimed the following: > > >> NB am not guaranteeing that I have understood which >> argument/parameter/identifier goes where! >> >> NBB asking "if xyz" is the same as "if xyz is not None" - a concept >> known as "truthiness" (falsiness) >> >> NBBB Python's PEP-008 (a guide (maybe) and not a "standard") for naming >> identifiers and functions: >> >> box_pairs_in_same_row() > > Since NB -> nota bene translates as "note well", NBB would be "note > well well" etc. Seems that "NNB" would be more correct for emphasis -- > "note note well" > > Though in truth, I doubt this follows the pattern of PS -> post script, > in which PPS -> post post script does make logical sense (vs PSS -> post > script script) In many languages, repetition indicates a superior form. Also, in Linux, as you likely know, -v requests a verbose output, and some systems offer the opportunity to select -vv, ie an even more verbose/illustrative output. Back to English, you may remember: There was a little girl, Who had a little curl, Right in the middle of her forehead. And when she was good, She was very, very good. But when she was bad, She was horrid. (and here I'm likely to land into more trans-Atlantic dispute) but whereas some Americans attribute it to Longfellow, it was likely a British nursery rhyme more than 150-years ago. - cue @Alan Perhaps I'll sail close to the wind of the Python CofP when I say that I prefer (the idea of meeting someone espousing) the Mae West variation: ?When I'm good, I'm very good, but when I'm bad, I'm better.? This device was used to great effect in a British TV series (don't know if it still runs) called "The Vicar of Dibley". One of the aged-characters had a bad stutter, and it was a running-joke (repeated, programme after programme - NB British spelling!!!) to have him say "no, no, no, no, -pause-, yes" (or was it vice-versa?). At first, you would interpret him to be emphatic in his negative expression, only to find that he really means the exact opposite. In Hindi, the famous imperious phrase was "chaldi, chaldi!" (EN: quickly, quickly - or hurry, hurry). In Tok Pisin, younger children could be "lik, lik" (EN: little, little - or smaller than little). In French we add emphasis or indicate enthusiasm with repetition, such as "tr?s, tr?s bien" (EN: very, very, good), and a similar device exists in other human-languages. Further replies 'here' will likely form a catalog of such... To bring us back to something remotely approaching 'the topic', and given our habit of writing loops/iterating, can these be restated as: "To err is human, but to really foul things up, you need a computer!" Thus, aligning repetition to emphasis, I'd go with your pattern - repeat the second letter/word. As NB = Nota Bene; and taking "bene" as (still, after all the centuries) "well" or "good" in modern Italian, I'd stick with NBB = Nota Bene Bene, as 'note very well'. However, the 'bottom line', or en-fin (EN: at the end), which appears in EN:AU as "at the end of the day", should you prefer, the adding of "B"s was only to distinguish each of a series of notes from one-another. In future, I'll list them as NB1, NB2, etc. Oh wait, won't that start you into a debate about zero-based indexing? It doesn't take much to amuse my (small) mind! -- -- Regards, =dn From cs at cskk.id.au Thu Dec 2 02:16:46 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Thu, 2 Dec 2021 18:16:46 +1100 Subject: [Tutor] function return values In-Reply-To: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> Message-ID: On 02Dec2021 11:24, Phil wrote: >I was quite pleased with myself that I had reduced the complexity of a >function until I tried to reuse the same code elsewhere. I had the >return values set to some dummy values and that was good in this case. >However, this idea failed when I tried to test if the pairs are in the >same column. So, what is the correct way to handle a case where there >isn't a valid pair, row or list value to return? > >??? def boxPairsInSameRow(self, pairs, x, y): >??????? col_list = [] >??????? for item, count in pairs.items(): >??????????? if count == 2:? # then item is a pair candidate >??????????????? for row in range(x, x + 3): >??????????????????? col_list = [] >??????????????????? for col in range(y, y + 3): >??????????????????????? if item in self.solution[row][col]: >??????????????????????????? col_list.append(col) >??????????????????? return item, row, col_list >??????? return None, None, None # item, row and col_list were >originally dummy values > >This is the calling function: > >??????????? item, row, column_list = >self.boxPairsInSameRow(candidate_pairs, x , y) >??????????? if item is not None and row is not None and column_list is >not None: >??????????? ??? do this only if the returned values are valid. In >other words, this is a pair in a row. > >Maybe the boxPairsInSameRow function is still too complex? The aim is >to feed in the pairs, from a counter dictionary, and the row and >column starting coordinates and have it return the pairs.item, the row >that the item is on and a list containing the two columns for that row >position. Once the pairs have been found on a row then there is no need >to search the remaining row or rows. For a function returning a valid result or no result you have two main approaches in Python: - return the result or None (or None-ish, as you are doing) - return the result or raise a ValueError("no solution found") The former requires a test by the caller. The latter means you need to catch an exception for "no solution". Wordier, but it might mean you can defer that to more outer pieces of the code, or not catch it at all if this programme should always find a solution. The exception approach means you would replace the last: return None, None, None with: raise ValueError("no solution found") # better message needed For the None-ish approach, you might: - return None (or some other sentinel, but None is best if None is not itself a valid return) - return a (None,None,None) tuple as you have done to match the expected result The former requires code like this if you return None: solution = self.boxPairsInSameRow(candidate_pairs, x, y) if solution is None: ... no solution ... else: item, row, column_list = solution ... use the solution ... or this for your None-ish approach: item, row, column_list =_ self.boxPairsInSameRow(candidate_pairs, x , y) if (item, row, column_list) == (None, None, None): ... no solution ... else: ... use the solution ... The exception approach looks like this: try: item, row, column_list =_ self.boxPairsInSameRow(candidate_pairs, x , y) except ValueError as e: warning("no box pairs found: %s", e) ... handle no solution here ... else: ... use the solution .. If you always expect a solution, this becomes: item, row, column_list = self.boxPairsInSameRow(candidate_pairs, x , y) ... use the solution .. which is very simple. I suppose if that were the case you would not need your None-ish test in the first place. A third alternative turns on your statement: "Once the pairs have been found on a row then there is no need to search the remaining row or rows". Is it _meaningful_ to search for more solutions, even if you _currently_ just want the first one? If it is you might recast this as a generator: ??? def boxPairsInSameRow(self, pairs, x, y): ??????? col_list = [] ??????? for item, count in pairs.items(): ??????????? if count == 2:? # then item is a pair candidate ??????????????? for row in range(x, x + 3): ??????????????????? col_list = [] ??????????????????? for col in range(y, y + 3): ??????????????????????? if item in self.solution[row][col]: ??????????????????????????? col_list.append(col) ??????????????????? yield item, row, col_list This is a generator function, which walks your data structure and yields solutions as they are found. Note that because it will continue after the first solution you may need to clear some things after the "yield" statement, for example maybe "col_list" should be emptied before proceeding. Then at the calling end you can iterate over the generator: solutions = list(self.boxPairsInSameRow(candidate_pairs, x , y)) which would get a list of all the solutions. In your scenario, you only want to search to the first solution: solution = None for solution in self.boxPairsInSameRow(candidate_pairs, x , y): # exit the loop on the first solution found break if solution is None: ... no solution yielded ... else: ... use the solution ... This runs the generator to the first solution, but _does not_ continue executing it because you exit the for-loop. So no further iteration happens and the generator ceases execution (it stopped at the "yield", and here you never resume it). In fact Pythons for-loop has a little used feature: an else-clause: for solution in self.boxPairsInSameRow(candidate_pairs, x , y): # exit the loop on the first solution found ... use the solution ... break else: ... no solution yielded ... The "else" clause runs after the for-loop finishes, _unless_ you "break" out of the loop. So here we have the loop body handle the solution and the "else:" handle the no solution path (you can leave the "else:" off if there is nothing to do if there is no solution). Cheers, Cameron Simpson From alan.gauld at yahoo.co.uk Thu Dec 2 13:37:47 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 2 Dec 2021 18:37:47 +0000 Subject: [Tutor] function return values In-Reply-To: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> Message-ID: On 02/12/2021 00:24, Phil wrote: > ??? def boxPairsInSameRow(self, pairs, x, y): > ??????? col_list = [] You don't need this since you set it below > > ??????? for item, count in pairs.items(): > ??????????? if count == 2:? # then item is a pair candidate > ??????????????? for row in range(x, x + 3): > ??????????????????? col_list = [] > ??????????????????? for col in range(y, y + 3): > ??????????????????????? if item in self.solution[row][col]: > ??????????????????????????? col_list.append(col) > ??????????????????? return item, row, col_list This return will happen after the first row is processed, you never process the other rows. I suspect that's not what you intended? -- 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 phillor9 at gmail.com Thu Dec 2 23:11:37 2021 From: phillor9 at gmail.com (Phil) Date: Fri, 3 Dec 2021 15:11:37 +1100 Subject: [Tutor] function return values In-Reply-To: References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> Message-ID: <24fde022-ff82-bd86-4d52-2c3cb7f39ba2@gmail.com> On 3/12/21 05:37, Alan Gauld via Tutor wrote: > On 02/12/2021 00:24, Phil wrote: > >> ??? def boxPairsInSameRow(self, pairs, x, y): >> ??????? col_list = [] > You don't need this since you set it below > >> ??????? for item, count in pairs.items(): >> ??????????? if count == 2:? # then item is a pair candidate >> ??????????????? for row in range(x, x + 3): >> ??????????????????? col_list = [] >> ??????????????????? for col in range(y, y + 3): >> ??????????????????????? if item in self.solution[row][col]: >> ??????????????????????????? col_list.append(col) >> ??????????????????? return item, row, col_list > This return will happen after the first row is processed, > you never process the other rows. I suspect that's not > what you intended? You are correct Alan. The pair exists on row zero in my test case. This function should return a valid result if two numbers are in the same row. So, if a number passes the "if count == 2" test then that number is a pair and if the pair is found in row zero then it cannot exist anywhere else so there is no need to keep searching. But, what if the pair is in row 1 or 2 and not in the first row? I think a "found" test is needed before the return statement and the first thing that comes to mind is: if len(col_list) == 2: ??? return item, row, col_list else: ??? continue And, if the pair is not in the same row then return None, None, None. I'll give this a test tomorrow. -- Regards, Phil From phillor9 at gmail.com Fri Dec 3 00:34:22 2021 From: phillor9 at gmail.com (Phil) Date: Fri, 3 Dec 2021 16:34:22 +1100 Subject: [Tutor] function return values In-Reply-To: References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> Message-ID: <2fffbd36-34d1-b47a-44bf-91d469117c8d@gmail.com> On 2/12/21 18:16, Cameron Simpson wrote: > > For a function returning a valid result or no result you have two main > approaches in Python: > - return the result or None (or None-ish, as you are doing) > - return the result or raise a ValueError("no solution found") Thank you Cameron for your detailed reply which has given me something to think about. I think the return of None, None, None is the simplest option. My calling function was originally only testing for a list of two elements and ignoring the other two variables. I think this is still correct, an empty list or a list containing 1 element is not valid. I'll experiment further with this. Alan has pointed out an error: I do not need to keep searching after a pair has been found but I do need to search beyond the first row. The pair could be on row 2 or 3. I've seldom ever used exceptions and I've never found a case where I could use the yield statement. I can see that it would be useful if I needed to return multiple values of the same variable from a function. I'll try to keep the yield statement in mind it could be useful somewhere and better than the use of a list. > or this for your None-ish approach: > > item, row, column_list =_ self.boxPairsInSameRow(candidate_pairs, x , y) > if (item, row, column_list) == (None, None, None): Should the Nones be enclosed within brackets? I didn't do that. I know that "if (a + b) == 2" is C syntax. > A third alternative turns on your statement: "Once the pairs have been > found on a row then there is no need to search the remaining row or > rows". Is it _meaningful_ to search for more solutions, even if you > _currently_ just want the first one? There can only be a pair and they must be on the same row, not necessarily on the first row. So what I wrote was not correct. I do not need to continue searching once a pair has been found on a row but I do need to search beyond the first row in case the pair is on row 1 or 2. I don't think the use of a generator is called for here. -- Regards, Phil From phillor9 at gmail.com Fri Dec 3 00:57:34 2021 From: phillor9 at gmail.com (Phil) Date: Fri, 3 Dec 2021 16:57:34 +1100 Subject: [Tutor] function return values In-Reply-To: <34ce759a-a45e-21cc-0705-c210cbacaa63@DancesWithMice.info> References: <82a53068-6e6d-2038-cf8c-f297a0419c19@gmail.com> <34ce759a-a45e-21cc-0705-c210cbacaa63@DancesWithMice.info> Message-ID: <72d79228-0924-5614-3a6f-aac928473dd8@gmail.com> > Suggest creating another function which returns a boolean. Then use that > to initiate (or not) the scan. Perhaps along these lines: > > if is_pair_in_row( item, row, column_list ): > ( item, > row, > column_list, > ) = self.boxPairsInSameRow(candidate_pairs, x , y) > That was exactly my first thought dn until I realised that I needed the function to return the item, row and a list of the columns where the pair was found. Asking if a pair is on row 0 column 0 and if not then asking if the pair is on row 0 col 1 etc would not reduce the complexity of the calling function (unless I've misunderstood your reply, which is not unlikely) which is the aim of the current exercise. > NBBB Python's PEP-008 (a guide (maybe) and not a "standard") for naming > identifiers and functions: > > box_pairs_in_same_row() This was originally isBoxPairsInSameRow, I just dropped the "is" (Pairs should have been Pair) since I'm not returning a boolean value. Coming up with descriptive names? for functions and variable is not one of my strong points and I must check if camel text is acceptable in Python. -- Regards, Phil From cs at cskk.id.au Fri Dec 3 02:04:54 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Fri, 3 Dec 2021 18:04:54 +1100 Subject: [Tutor] function return values In-Reply-To: <2fffbd36-34d1-b47a-44bf-91d469117c8d@gmail.com> References: <2fffbd36-34d1-b47a-44bf-91d469117c8d@gmail.com> Message-ID: On 03Dec2021 16:34, Phil wrote: >On 2/12/21 18:16, Cameron Simpson wrote: >>For a function returning a valid result or no result you have two main >>approaches in Python: >>- return the result or None (or None-ish, as you are doing) >>- return the result or raise a ValueError("no solution found") > >Thank you Cameron for your detailed reply which has given me something >to think about. > >I think the return of None, None, None is the simplest option. That's fine. >I've seldom ever used exceptions and I've never found a case where I >could use the yield statement. I can see that it would be useful if I >needed to return multiple values of the same variable from a function. Generators are good when: - there's a series of values, sometimes unbounded (eg primes) - the series is expensive to compute, either per item or in total ("all the primes" has infinite cost) because they compute until they yield a value. The code consuming/using those values can process that value. It can then resume the generator (which has simply paused at the "yield") or just cease using it, so the generator no longer runs. So they compute only what gets consumed, nothing more. >I'll try to keep the yield statement in mind it could be useful >somewhere and better than the use of a list. Not that generator functions (functions containing a "yield" statement) are used _differently_ to normal functions. def gen(): x = 1 while True: yield x x += 2 This generates odd numbers. The _return_ of the function is an iterable, the generator: g = gen() At this point the function _has not run_. If you print(g) you'll see a generator object. It is iterable, and a common way to use it is to iterable with a for-loop: for n in g: print(n) Each iteration of the loop rns the function until it yields, and you get what it yielded as the next value in the iteration. So the for-loop about assigns odd numbers to "n". >>or this for your None-ish approach: >> >> item, row, column_list =_ self.boxPairsInSameRow(candidate_pairs, x , y) >> if (item, row, column_list) == (None, None, None): > >Should the Nones be enclosed within brackets? I didn't do that. I know >that "if (a + b) == 2" is C syntax. That's a precedence thing (and a readability thing, possibly). This is a 3-tuple: None, None, None You know you don't need the brackets - your return-statement used no brackets, and it returns a tuple. (All Python functions return exactly one object - your object just happens to be a tuple with 3 elements inside it). _However_, the == operator binds more tightly that the , operator. So this: if item, row, column_list == None, None, None: is actually a 5-tuple: item row column_list==None None None Being a non-empty tuple, it is always true. Not what you wanted to test. This: if (item, row, column_list) == (None, None, None): is a Boolean expression comparing 2 3-tuples. Inside brackets to get the groups right. So "item, row, column_list" is the left hand 3-tuple, and it is in brackets to keep it together. No different to: (2 + 3) * 5 meaning 25 instead of: 2 + 3 * 5 meaning 17, because arithmetic precedence groups the "*" tighter than the "+". Brackets to override default operator precedence. >>A third alternative turns on your statement: "Once the pairs have been >>found on a row then there is no need to search the remaining row or >>rows". Is it _meaningful_ to search for more solutions, even if you >>_currently_ just want the first one? > >There can only be a pair and they must be on the same row, not >necessarily on the first row. So what I wrote was not correct. I do >not need to continue searching once a pair has been found on a row but >I do need to search beyond the first row in case the pair is on row 1 >or 2. I don't think the use of a generator is called for here. On that basis, I don't think so either. Cheers, Cameron Simpson From cs at cskk.id.au Fri Dec 3 02:10:13 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Fri, 3 Dec 2021 18:10:13 +1100 Subject: [Tutor] function return values In-Reply-To: <72d79228-0924-5614-3a6f-aac928473dd8@gmail.com> References: <72d79228-0924-5614-3a6f-aac928473dd8@gmail.com> Message-ID: On 03Dec2021 16:57, Phil wrote: >This was originally isBoxPairsInSameRow, I just dropped the "is" (Pairs >should have been Pair) since I'm not returning a boolean value. Coming >up with descriptive names? for functions and variable is not one of my >strong points and I must check if camel text is acceptable in Python. Functions starting with "is" usually return a Boolean because they usually test something ("is this blue?" etc). If your function actually returns the pair found (with some context information) you'd give it a nounlike name, naming the thing it returns somehow. def is_odd(n): return n % 2 != 0 def square(n): return n * n # print the squares of the odd numbers < 100 for n in range(100): if is_odd(n): print(square(n)) Anyway, it's your code. Do what you find easiest to read later. Having things sort-of-like English grammar helps me though. And don't stress about names too much. If you find a name confusing or not to your liking it is easy to fix with an editor later. Cheers, Cameron Simpson From __peter__ at web.de Fri Dec 3 12:14:33 2021 From: __peter__ at web.de (Peter Otten) Date: Fri, 3 Dec 2021 18:14:33 +0100 Subject: [Tutor] function return values In-Reply-To: References: <2fffbd36-34d1-b47a-44bf-91d469117c8d@gmail.com> Message-ID: On 03/12/2021 08:04, Cameron Simpson wrote: > _However_, the == operator binds more tightly that the , operator. So > this: > > if item, row, column_list == None, None, None: > > is actually a 5-tuple: > > item > row > column_list==None > None > None > > Being a non-empty tuple, it is always true. Not what you wanted to test. I believed you, but something made me feed this to the interpreter: >>> 1, 2 == 3, 4 (1, False, 4) >>> if 1, 2 == 3, 4: print("equal") SyntaxError: invalid syntax So the unsuspecting writer gets one more chance to put the parens into the right places ;) From cs at cskk.id.au Fri Dec 3 15:46:57 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Sat, 4 Dec 2021 07:46:57 +1100 Subject: [Tutor] function return values In-Reply-To: References: Message-ID: On 03Dec2021 18:14, Peter Otten <__peter__ at web.de> wrote: >On 03/12/2021 08:04, Cameron Simpson wrote: >>_However_, the == operator binds more tightly that the , operator. So >>this: >> >> if item, row, column_list == None, None, None: >> >>is actually a 5-tuple: >> >> item >> row >> column_list==None >> None >> None >> >>Being a non-empty tuple, it is always true. Not what you wanted to test. > >I believed you, but something made me feed this to the interpreter: > >>>> 1, 2 == 3, 4 >(1, False, 4) >>>> if 1, 2 == 3, 4: print("equal") >SyntaxError: invalid syntax > >So the unsuspecting writer gets one more chance to put the parens into >the right places ;) Fascinating. I did not expect this! On consideration, this will only confuse people coming from C, who might think "oh I need brackets around the condition" and write: >>> if (1, 2 == 3, 4): ... print(1) ... 1 In Python 3.8 I get this: >>> if 1, 2 == 3, 4: File "", line 1 if 1, 2 == 3, 4: ^ SyntaxError: invalid syntax which does suggest the commas as the issue, not a missing opening bracket, but still... Thanks, Cameron Simpson From phillor9 at gmail.com Fri Dec 3 17:46:06 2021 From: phillor9 at gmail.com (Phil) Date: Sat, 4 Dec 2021 09:46:06 +1100 Subject: [Tutor] function return values In-Reply-To: References: <2fffbd36-34d1-b47a-44bf-91d469117c8d@gmail.com> Message-ID: <6eb515c6-9294-89f0-e484-5beaed8fae17@gmail.com> > Not that generator functions (functions containing a "yield" statement) > are used _differently_ to normal functions. > > def gen(): > x = 1 > while True: > yield x > x += 2 > > This generates odd numbers. The _return_ of the function is an iterable, > the generator: > > g = gen() > > At this point the function _has not run_. If you print(g) you'll see a > generator object. It is iterable, and a common way to use it is to > iterable with a for-loop: > > for n in g: > print(n) > > Each iteration of the loop rns the function until it yields, and you get > what it yielded as the next value in the iteration. So the for-loop > about assigns odd numbers to "n". Thank Cameron for the extra generator information and the example that illustrates it's use well. > if (item, row, column_list) == (None, None, None): > > is a Boolean expression comparing 2 3-tuples. Of course, and thank you for pointing that out. -- Regards, Phil From nulla.epistola at web.de Sat Dec 4 07:46:40 2021 From: nulla.epistola at web.de (Sibylle Koczian) Date: Sat, 4 Dec 2021 13:46:40 +0100 Subject: [Tutor] function return values In-Reply-To: References: Message-ID: Am 03.12.2021 um 21:46 schrieb Cameron Simpson: > > Fascinating. I did not expect this! > > On consideration, this will only confuse people coming from C, who might > think "oh I need brackets around the condition" and write: > > >>> if (1, 2 == 3, 4): > ... print(1) > ... > 1 > > In Python 3.8 I get this: > > >>> if 1, 2 == 3, 4: > File "", line 1 > if 1, 2 == 3, 4: > ^ > SyntaxError: invalid syntax > > which does suggest the commas as the issue, not a missing opening > bracket, but still... > Still shorter: >>> if 1, 2: print("ja") File "", line 1 if 1, 2: print("ja") ^ SyntaxError: invalid syntax >>> if (1, 2): print("ja") ... ja Python 3.10, that's probably unimportant. From deshwill at iu.edu Mon Dec 6 09:59:26 2021 From: deshwill at iu.edu (Deshaun Williams) Date: Mon, 6 Dec 2021 09:59:26 -0500 Subject: [Tutor] SOS pleas help Message-ID: im trying to code a web application to use API but i cant get it to work heres my current code. <% import math, json from bottle import request, route url = "https://www.gamerpower.com/api/giveaways?platform=pc" r = request.get(url) request.forms.get("cosnole") gamer = request.forms.get("cosnole") pathstring ="" if gamer == "pc": pathstring = ('/pc.html') elif gamer == "switch": pathstring == ('/switch.html') end response = request.get(" https://www.gamerpower.com/api/giveaways?platform=pc") data = json.load(response) # write the results in a csv file %>

nothing

any help you can give would be grealy appreciated From juliushamilton100 at gmail.com Mon Dec 6 17:37:40 2021 From: juliushamilton100 at gmail.com (Julius Hamilton) Date: Mon, 6 Dec 2021 23:37:40 +0100 Subject: [Tutor] urllib vs requests Message-ID: Hey, Could anyone please let me know why requests.get fetches the following url effectively but urllib.request.urlopen returns ?403 Forbidden?? https://juno.sh/direct-connection-to-jupyter-server/ Is it because they make different types of requests so the web server detects that urllib is something it doesn?t accept? Is there any way around that? By the way, is there any Python email group for slightly more advanced questions than the tutor group? More like troubleshooting or something, from package maintainers? Thanks, Julius From learn2program at gmail.com Mon Dec 6 18:25:30 2021 From: learn2program at gmail.com (Alan Gauld) Date: Mon, 6 Dec 2021 23:25:30 +0000 Subject: [Tutor] urllib vs requests In-Reply-To: References: Message-ID: On 06/12/2021 22:37, Julius Hamilton wrote: > By the way, is there any Python email group for slightly more advanced Yes the main Python list is targetted at more experienced users and especially those working outside the standard library, or looking for new features or packages to be added. Sign up via the python.org site under mailing lists. -- 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 Mon Dec 6 18:26:29 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Mon, 6 Dec 2021 23:26:29 +0000 Subject: [Tutor] SOS pleas help In-Reply-To: References: Message-ID: On 06/12/2021 14:59, Deshaun Williams wrote: > im trying to code a web application to use API but i cant get it to work > heres my current code. <% > import math, json > from bottle import request, route > url = "https://www.gamerpower.com/api/giveaways?platform=pc" > r = request.get(url) > request.forms.get("cosnole") > gamer = request.forms.get("cosnole") I suspect those two strings should be spelled "console" ? -- 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 Mon Dec 6 18:27:29 2021 From: mats at wichmann.us (Mats Wichmann) Date: Mon, 6 Dec 2021 16:27:29 -0700 Subject: [Tutor] urllib vs requests In-Reply-To: References: Message-ID: <4ec2b57b-5953-9dcf-3da3-6eee86ea2fdd@wichmann.us> On 12/6/21 15:37, Julius Hamilton wrote: > Hey, > > Could anyone please let me know why requests.get fetches the following url > effectively but urllib.request.urlopen returns ?403 Forbidden?? > > https://juno.sh/direct-connection-to-jupyter-server/ > > Is it because they make different types of requests so the web server > detects that urllib is something it doesn?t accept? Is there any way around > that? If there's a proxy involved, that's probably the issue, last I knew it it didn't talk to https through a proxy without some further hacks. also note that urllib docs themselves recommend using requests if it makes sense. > > By the way, is there any Python email group for slightly more advanced > questions than the tutor group? More like troubleshooting or something, > from package maintainers? python-list is one http://mail.python.org/mailman/listinfo/python-list can also try https://discuss.python.org/ which is a board, but you can also access it via email. Or the Python Discord: https://discord.com/invite/python From cs at cskk.id.au Mon Dec 6 18:32:14 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Tue, 7 Dec 2021 10:32:14 +1100 Subject: [Tutor] SOS pleas help In-Reply-To: References: Message-ID: On 06Dec2021 09:59, Deshaun Williams wrote: >im trying to code a web application to use API but i cant get it to work Doesn't work is a little vague. Some remarks on the code inline below: >import math, json >from bottle import request, route >url = "https://www.gamerpower.com/api/giveaways?platform=pc" >r = request.get(url) >request.forms.get("cosnole") This does a GET but does nothing with the result. >gamer = request.forms.get("cosnole") Both this GET and the preceeding one might misspell "console". GETs typically want an entire URL. >pathstring ="" >if gamer == "pc": > pathstring = ('/pc.html') You don't need the brackets here, though they do no harm. >elif gamer == "switch": > pathstring == ('/switch.html') This is an equality test ("=="), not an assigment ("="). >end "end"? This is not a Python keyword. As such, it is a variable reference, and you have no "end" variable. This will produce a NameError when you get here. >response = request.get("https://www.gamerpower.com/api/giveaways?platform=pc") Looks like url from earlier. If so, just say url here, it will avoid accidentally not using the same URL string. >data = json.load(response) json.load() is for files. You probably want json.loads() (load from a string). You should also check that response is actually a string, and not a more complex "HTTP response" type object. A: print(type(response)) before this line will tel you that. > # write the results in a csv file I don't see anycode to write a CSV file. See the "csv" module documentation: https://docs.python.org/3/library/csv.html#module-csv Cheers, Cameron Simpson From phillor9 at gmail.com Tue Dec 7 02:19:51 2021 From: phillor9 at gmail.com (Phil) Date: Tue, 7 Dec 2021 18:19:51 +1100 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 22/11/21 11:55, Alan Gauld via Tutor wrote: > > Also, using python "2D" lists there is no way to > access the entire column (in your example) you can > only access a whole row. > > You can build a copy of a column easily enough with: > > col6 = [row[5] for row in solution] > > But if you change any values you need to regenerate > the copy so it's not exactly efficient. I've used this idea to simplify a section of my otherwise complex code to access columns. I've spent the whole day trying to simplify another area of confusing code, the code is simpler but it doesn't do what I want and I can see why but I don't know how to proceed. What I have is a 9 x 9 grid and there are three 3 x 3 boxes per line which means that I have nine 3 x 3 boxes in total. Each box is accessed by it's top left coordinates: ??????? box_start = [(0, 0), (0, 3), (0, 6), ??????????? (3, 0), (3, 3), (3, 6), ??????????? (6, 0), (6, 3), (6, 6) ??????????? ] ??????? for x, y in box_start: ??????????? ... ??????? for col in range(x, x + 3): ??????????? ... ??????????? for row in range(y, y + 3): ??????????????? print(f'self.solution[row][col] {self.solution[row][col]}') This works but I know that it's not the correct method and I still find accessing columns within a 2D array confusing. I only got this working by trial and error by swapping the x and y coordinates. I think all I've done is turn the grid onto it's side. So, by adapting Alan's suggestion I can print the top three boxes as columns: ??????? for c in range(y, y + 3): ??????????? col = [row[c] for row in self.solution[:3]] ??????????? print(col) This does exactly what I want. The question is, how do I use the x variable to step down to the next row of boxes? The next row starts at 3 instead of 0. I hope this is less confusing to the reader than it is to me. -- Regards, Phil From bouncingcats at gmail.com Tue Dec 7 03:39:06 2021 From: bouncingcats at gmail.com (David) Date: Tue, 7 Dec 2021 19:39:06 +1100 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On Tue, 7 Dec 2021 at 18:21, Phil wrote: > box_start = [(0, 0), (0, 3), (0, 6), > (3, 0), (3, 3), (3, 6), > (6, 0), (6, 3), (6, 6) > ] > > for x, y in box_start: > ... > > for col in range(x, x + 3): > ... > for row in range(y, y + 3): > print(f'self.solution[row][col] > {self.solution[row][col]}') > > This works but I know that it's not the correct method and I still find > accessing columns within a 2D array confusing. I only got this working > by trial and error by swapping the x and y coordinates. I think all I've > done is turn the grid onto it's side. Hi, I'm not going to dig into your code at all. But I am offering to help, just by making sure that you understand the basic idea of 2D indexing of lists. Also I suggest to stop using the word 'array' at this stage. Python has both 'list' and 'array' and they are slightly different. So just stick to 'list' for the time being, and stop calling your lists 'arrays' because that's unclear. > col = [row[c] for row in self.solution[:3]] Also I suggest to stop using comprehensions at this stage. Just use simple 'for' loops like I have, until they make sense. After that, you can start to use comprehensions and slicing. I don't know what it is that you aren't understanding. But you seem to be unclear so I have written a quick demo of 2D list indexing below for you. I suggest you have a look at *and* *run* the code below until you have no trouble understanding it. I think that being confident about this concept will make your actual problem much clearer to you. I have pasted the code below, but it might get mangled or line-wrapped by the email system, so I have attached a file copy of it also. # have a look at this code, maybe it helps you understand # i is row index # j is column index # lets use a list for each row, each row-list has 3 columns row_0 = [0, 1, 2] row_1 = [3, 4, 5] row_2 = [6, 7, 8] # how to print each column of row_0 for j in range(0, 3): print(f"row_0[{j}] = row_0, column {j} = {row_0[j]}") # lets make box contain 3 rows, let box be a list of 3 rows box = [row_0, row_1, row_2] # note: box is a list, of three rows # note: each row is a list, of three elements # how to print each row of box for i in range(0, 3): print(f"box[{i}] = row_{i} = {box[i]}") # now combine the above two print ranges, to print the whole box, by rows for i in range(0, 3): # get a row, which is one of the three lists in box row = box[i] # now print each column of the row for j in range(0, 3): # print each j of the box[i] row print(f"row[{j}] = {row[j]}") # but we dont really need that variable 'row' # because row = box[i] # so instead of naming 'row', we can refer to 'box[i]' # they are the same thing # so for example row[2] can be written box[i][2] # because box[i] is the row list, and then we ask for the [2] element of it # and by the same thinking, any element can be accessed as box[i][j] # where box[i] is the row, which is then indexed using [j] # the below print statement is exactly the same as the previous one for i in range(0, 3): for j in range(0, 3): print(f"box[{i}][{j}] = {box[i][j]}") -------------- next part -------------- # have a look at this code, maybe it helps you understand # i is row index # j is column index # lets use a list for each row, each row-list has 3 columns row_0 = [0, 1, 2] row_1 = [3, 4, 5] row_2 = [6, 7, 8] # how to print each column of row_0 for j in range(0, 3): print(f"row_0[{j}] = row_0, column {j} = {row_0[j]}") # lets make box contain 3 rows, let box be a list of 3 rows box = [row_0, row_1, row_2] # note: box is a list, of three rows # note: each row is a list, of three elements # how to print each row of box for i in range(0, 3): print(f"box[{i}] = row_{i} = {box[i]}") # now combine the above two print ranges, to print the whole box, by rows for i in range(0, 3): # get a row, which is one of the three lists in box row = box[i] # now print each column of the row for j in range(0, 3): # print each j of the box[i] row print(f"row[{j}] = {row[j]}") # but we dont really need that variable 'row' # because row = box[i] # so instead of naming 'row', we can refer to 'box[i]' # they are the same thing # so for example row[2] can be written box[i][2] # because box[i] is the row list, and then we ask for the [2] element of it # and by the same thinking, any element can be accessed as box[i][j] # where box[i] is the row, which is then indexed using [j] # the below print statement is exactly the same as the previous one for i in range(0, 3): for j in range(0, 3): print(f"box[{i}][{j}] = {box[i][j]}") From phillor9 at gmail.com Tue Dec 7 04:13:16 2021 From: phillor9 at gmail.com (Phil) Date: Tue, 7 Dec 2021 20:13:16 +1100 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 7/12/21 19:39, David wrote: > > I don't know what it is that you aren't understanding. > But you seem to be unclear so I have written a quick > demo of 2D list indexing below for you. Thank you David. I will study and run your code trough a debugger. While I was typing my previous e-mail I thought of a solution. col = [row[c] for row in self.solution] gives me the entire column. So what I need is to extract the first three rows for the top three boxes. Then for the next row of boxes I need the middle section of the column (rows 3, 4 and 5) and for the last row of boxes I need the last three rows of the column. At the moment I don't know how I might do this but that's a project for tomorrow. -- Regards, Phil From alan.gauld at yahoo.co.uk Tue Dec 7 04:34:46 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Tue, 7 Dec 2021 09:34:46 +0000 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 07/12/2021 07:19, Phil wrote: > What I have is a 9 x 9 grid and there are three 3 x 3 boxes per line > which means that I have nine 3 x 3 boxes in total. I believe you are building a model of a Sudoku game? But from your description it's not clear what data structure you are using. Please just show it! For example, do you really have a 9x9 grid - that is, a list with 81 single elements? solution = [0,1,2....,79, 80] Or is it a list with 9 sublists each of 9 elements? solution = [[0...8], [9...17], .... [71...80]] Or is it a list with 9 elements(boxes), each of which is itself a list with 9 elements solution = [[0,1,2,9,10,11,18,19,20],[3,4,5,12,13,14...],[6,7,8....] [...][...],[...] [...],[...],[...,78,79,80]] or is each box a list with 3 sublists each with 3 elements? solution = [[[0,1,2], [9,10,11], [18,19,20] ], [[3,4,5],....], ... ] >From your description you could have any of these structures. Each box is accessed > by it's top left coordinates: > > ??????? box_start = [(0, 0), (0, 3), (0, 6), > ??????????? (3, 0), (3, 3), (3, 6), > ??????????? (6, 0), (6, 3), (6, 6) > ??????????? ] This suggests to me you are using the second option above? ie a list with 9 sub lists of 9 elements? > This works but I know that it's not the correct method If it works it is "correct" for some definition of correct. But personally here is how I would tackle your problem. I'd just use a single list of 81 cells. I'd then write helper functions to extract the sublists I needed: def get_cell(table, row, col) def get_row(table, row_num) def get_col(table, col_num) def get_box(table, row_num, col_num) # or just box_num? The next problem (based on previous posts) is that your cells are sets which are immutable, so you need to: 1) create another set of functions for writing back changes to a cell - tricky... OR 2) use lists instead of sets because they are mutable. OR 3) Use OOP and make your cells objects with methods to add/remove values and using a set internally to check uniqueness etc. This would be my choice. Personally, I'd use OOP for the table too, and make the functions above methods of the table. So you would have a table object with 81 mutable cells. But you haven't mentioned OOP up to now so that may be an extra learning curve you don't want to deal with. -- 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 wlfraed at ix.netcom.com Tue Dec 7 12:46:11 2021 From: wlfraed at ix.netcom.com (Dennis Lee Bieber) Date: Tue, 07 Dec 2021 12:46:11 -0500 Subject: [Tutor] The Python way and two dimensional lists References: Message-ID: <302vqgtro4a2dul5oa5pkghktdka5bara1@4ax.com> On Tue, 7 Dec 2021 20:13:16 +1100, Phil declaimed the following: I'd also emphasize using Python names for the Python structures... A list is a dynamically allocated (automatically resizes if needed) heterogeneous (mixed content) structure. An array is a specialized structure optimized for math operations -- mostly superceded by packages like NumPy/SciPy and/or Pandas. The standard library array module only supports homogeneous arrays -- and essentially limited to integer, float, and likely complex numbers (Nope: just checked and complex are not listed: https://docs.python.org/3/library/array.html -- just byte, 16-bit int, 32-bit int, 64-bit int, 32-bit float, 64-bit double). NumPy array apparently allows for heterogeneous content. >While I was typing my previous e-mail I thought of a solution. > >col = [row[c] for row in self.solution] gives me the entire column. So >what I need is to extract the first three rows for the top three boxes. >Then for the next row of boxes I need the middle section of the column >(rows 3, 4 and 5) and for the last row of boxes I need the last three >rows of the column. At the moment I don't know how I might do this but >that's a project for tomorrow. As mentioned elsewhere, a linear list with a set of functions to translate (row, col) into the linear index is probably going to be the simplest route. That is what one would have used in assembly or other early languages where one just has a chunk of memory (granted, Python lists are not "just a chunk of memory", but the addressing scheme still works). For zero-based addressing: cell_address = row * 9 + col (or col * 9 + row depending on which you prefer -- row-major or column-major) You could make that a short function and just use board[cell_address(row, col)] where board is the 81 element list. Or, if the board is a class instance... (watch out for line wrap). There is no error checking in the following. Coordinates should be checked for range 0..8, values maybe should be checked to be set type. -=-=- """ Rudimentary class for accessing Sudoko board Dennis L Bieber Dec 7 2021 """ class Board(object): def __init__(self): self._cells = [{_} for _ in range(9 * 9)] def _cell_address(self, row, col): return row * 9 + col def get_cell(self, row, col): return self._cells[self._cell_address(row, col)] def set_cell(self, row, col, value): self._cells[self._cell_address(row, col)] = value def get_row(self, row): return [self._cells[self._cell_address(row, col)] for col in range(9)] def set_row(self, row, values): self._cells[self._cell_address(row, 0):self._cell_address(row, 9)] = values def get_col(self, col): return [self._cells[self._cell_address(row, col)] for row in range(9)] def set_col(self, col, values): for row, value in enumerate(values): self._cells[self._cell_address(row, col)] = value -=-=- C:\Users\Wulfraed\Documents\_Hg-Repositories\Python Progs>python3 Python ActivePython 3.8.2 (ActiveState Software Inc.) based on on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import SudokuBoard as sb >>> b = sb.Board() >>> b._cells [{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}, {42}, {43}, {44}, {45}, {46}, {47}, {48}, {49}, {50}, {51}, {52}, {53}, {54}, {55}, {56}, {57}, {58}, {59}, {60}, {61}, {62}, {63}, {64}, {65}, {66}, {67}, {68}, {69}, {70}, {71}, {72}, {73}, {74}, {75}, {76}, {77}, {78}, {79}, {80}] >>> b.get_cell(1, 5) {14} >>> b.set_cell(1, 5, {2, 3}) >>> b.get_cell(1, 5) {2, 3} >>> b.get_row(1) [{9}, {10}, {11}, {12}, {13}, {2, 3}, {15}, {16}, {17}] >>> b.get_col(5) [{5}, {2, 3}, {23}, {32}, {41}, {50}, {59}, {68}, {77}] >>> I'd have preferred to make those get/set methods Python properties, but I don't know how to manage a property that takes in multiple arguments (need row, col, and value for setter). -- Wulfraed Dennis Lee Bieber AF6VN wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ From PyTutor at DancesWithMice.info Tue Dec 7 16:47:12 2021 From: PyTutor at DancesWithMice.info (dn) Date: Wed, 8 Dec 2021 10:47:12 +1300 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 07/12/2021 22.13, Phil wrote: > > On 7/12/21 19:39, David wrote: >> >> I don't know what it is that you aren't understanding. >> But you seem to be unclear so I have written a quick >> demo of 2D list indexing below for you. > > Thank you David. I will study and run your code trough a debugger. > > While I was typing my previous e-mail I thought of a solution. > > col = [row[c] for row in self.solution] gives me the entire column. So > what I need is to extract the first three rows for the top three boxes. > Then for the next row of boxes I need the middle section of the column > (rows 3, 4 and 5) and for the last row of boxes I need the last three > rows of the column. At the moment I don't know how I might do this but > that's a project for tomorrow. This may represent a significant break-through! You've started thinking about how the data-structure and the algorithm must combine, in order to produce an effective solution. A random selection from a "Sudoku rules" web-search yields the following: "The object of Sudoku is to fill the ... empty cells with numbers between 1 and 9 (1 number only in each cell) according the following guidelines: ... that a number should appear only once on each row, column and a region." A "region" is what has been called a "box" earlier in this thread. Thinking about it, the first two "guidelines" obviate the third. When we print/display a Sudoku tableau, it is easier on the eyes if the nine regions are highlighted. However, do they add anything to the need to check 'the rules' - indeed, is it an easy check? So, when it comes to evaluating a Sudoku 'solution', each column in the tableau must feature all of the nine digits, and each row of the tableau, the same. QED(?) These rules are 1D in nature - taken in-turn. However, shifting our view slightly, when we look at a Sudoku puzzle we see the tableau as a matrix, and in two dimensions. Notice how the evaluations (above) are 1D, but the visual 'picture' is 2D! Thus, if we can dispense with the 'region rule', the only 'use' we have for regions is for back-end display formatting. When you separate the two ways to look at a Sudoku, life becomes a lot easier. We can look at it one 'way' when running the algorithm, and any 'way' when displaying the tableau (remember earlier advice about "stepwise decomposition" - break large problem into multiple smaller ones!) @Alan has made the suggestion of using a single list to represent the tableau (I refer to this a "snake" - rather than a separate construct, the second row becomes (something like) an extension of the first). The 2D complexity is reduced to 1D - however, keeping track of where rows begin/end adds (some/different) complexity back in. The algorithm has been partially-developed by @wulfraed. This is likely to be a very storage-efficient method, but some may find the 'snake' a little awkward to visualise, and having to manipulate the math (especially zero-based) will challenge others. Contrarily, your own first foray utilised a 2D approach, which is much easier to visualise. Probably because that's the way Sudoku puzzles are presented! Because lists are (typically) built by appending, the techniques which we may have applied in other languages where arrays are 'dimensioned' at compile-time, can't be carried-across directly. This is why folk will talk about writing Python 'pythonically', ie using a Python idiom, rather than trying to literally translate from a previous-language to Python, 'word for word'. NB this is a little different from the confusion of which dimension represents rows, and which 'holds' the columns (an issue of "consistency") - from which apparently, you've been suffering/dug yourself out. For this reason, I would use 2D/nested dict[ionarie]s. This has the added-advantage that the author may label each column/row, as-desired. For whatever reason, these could run 1~9, rather than Python's 0~8 (even 'Row1', 'Row2', and thus distinct from 'Col1' - which may further dispel earlier confusion/promote consistent use. Such may also assist comprehension and/or building the logic. I guess that if we really want to go 'mathematically-nuts' we could consider the opening Sudoku tableau to be a "sparse matrix". Such approaches typically store the data as a triplet - (row, column, value). Thus, each time a row must be evaluated (eg to see if it obeys that rule), the entire list of triplets must be scanned - similarly for each column. Added to that, a completed tableau is the exact opposite of a 'sparse' matrix. So, I'll say no more about that idea, and claim its consideration as 'making progress' - if only because we can concentrate our investigations elsewhere! There is another idea that you may like to consider - and perhaps as a next step from yesterday's report mental-breakthrough/learning. There are two 'rules'. Why not have two data-structures: one to facilitate the checking of one rule, and the other, um, the other? In the case of a Sudoku tableau, our 'data lake' is exactly 81 integers in size. [pauses to wipe sweat from eyes] So, if we were to duplicate the data, it's not particularly "expensive". The idea here, is to maintain a list of nine lists which represent the nine rows of the tableau. The second pile of nine lists represents the columns. Yes, when you boil-it-down, the data is repeated - each 'cell' appears once in a row list and again in a column list - and when a new value is added to the tableau, it must be added in two places. Ouch! Computer people will tell you that this 'repetition' is a "code smell" - a recipe for future-faults (and that assessment is completely correct - see "DRY = Do not Repeat Yourself Principle"). However, this 'separation' is another way to 'decompose' the problem of dealing with the tableau into a single 'view' - think horizontally first (your choice) and prove that, then switch... (after all, this is a method many use to solve a puzzle!) It is an inefficient solution, but offers a learning-path and an opportunity to figure-out a simpler version of 2D lists, and builds upon your 'consistency' determination. (maybe - YMMV!) So, after theorising, it was time to 'have some fun'. To compare the 'snake' with a 2D dict solution, I prepared some code and sufficient tests which progress from: An initial tableau: ------------------------------------- | | 2 | 7 | | 8 | 9 | 6 | | | ------------------------------------- | | | | | | 1 | 7 | | 2 | ------------------------------------- | 9 | | | | | | | | | ------------------------------------- | | | | | 5 | | | | 7 | ------------------------------------- | 1 | 7 | | | | | | 2 | 6 | ------------------------------------- | 8 | | | | 4 | | | | | ------------------------------------- | | | | | | | | | 4 | ------------------------------------- | 2 | | 8 | 5 | | | | | | ------------------------------------- | | | 1 | 8 | 6 | | 2 | 3 | | ------------------------------------- To 'proving' the first row and the first column, and assuring the handling of data-entry errors: ------------------------------------- | 3 | 2 | 7 | 4 | 8 | 9 | 6 | 5 | 1 | ------------------------------------- | 5 | | | | | 1 | 7 | | 2 | ------------------------------------- | 9 | | | | | | | | | ------------------------------------- | 4 | | | | 5 | | | | 7 | ------------------------------------- | 1 | 7 | | | | | | 2 | 6 | ------------------------------------- | 8 | | | | 4 | | | | | ------------------------------------- | 6 | | | | | | | | 4 | ------------------------------------- | 2 | | 8 | 5 | | | | | | ------------------------------------- | 7 | | 1 | 8 | 6 | | 2 | 3 | | ------------------------------------- NB due apologies - text-only email will not carry the visual information that the puzzle-values, as-provided, are emboldened; whereas the user-entries are not. (which rather ruins the effect) This emphasises another important question, which IIRC you have already struck: the realisation that a cell must be in one of three 'states': no value, an initial-tableau value (as forms the puzzle, and which may not be changed), and a user-entered value (which may be changed). Thus the question: how to represent the three 'states'? Once again, I counsel that the needs/best way to implement the methods and data-structures implementing the algorithm, may be quite different from the way the puzzle is 'output'. That's OK - you can perform necessary translation as part of the output-routine (eg at the same time as the row and column delimiting lines are added - emboldened around each box/region (which I didn't bother to do)). After developing those two 'sample-solutions', I experimented with the two-lists method, to see if (a) it would work, and (b) to attempt to assess if it might be an easier learning-approach. So, there are now three classes, identical in function but different in data-structure and algorithm. Each initialises the board/tableau, accepts input (either to set-up the puzzle, or from the user attempting solution), checks that entered-values are legal, checks that the board is legal, and displays a rather Q&D board (as above). It does not actually play a game (there's no front-end, at all), nor will it start ringing the bells or flashing lights, once the tableau is (correctly and completely) filled. Purely the mechanics/the algorithms! The total weighs-in at about 400-lines. So, I wouldn't be thanked for posting it to the list! If you (or you, gentle (and patient) reader) would like to review the code, please contact me directly, and I'll point you at a 'repo'... (NB I recommend, if not expect, that you'll attempt your own solution(s) first - because that's the best way to learn!) Are the several descriptions sufficient to start you off? -- Regards, =dn From phillor9 at gmail.com Tue Dec 7 19:31:45 2021 From: phillor9 at gmail.com (Phil) Date: Wed, 8 Dec 2021 11:31:45 +1100 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 7/12/21 20:34, Alan Gauld via Tutor wrote: Thank you Alan, Dennis and dn, now I have even more to think about! I previously mentioned that this current sudoku solver project is one that I've translated from a C++ project that I wrote many years ago. It does, as it stands, solve all but the more difficult puzzles. I've added some extra solving strategies over the past couple of months and I'm attempting to make the code, even though it works, more understandable. Visualising and manipulating objects in space is one of my many failings and one that cost me a job at an IBM interview. > I believe you are building a model of a Sudoku game? > But from your description it's not clear what data > structure you are using. Please just show it! > The first list of lists represents the board. ??????? self.solution = [[None] * self.num_cols for _ in range(self.num_rows)] The second list of lists represents the Tkinter GUI where the user can enter a puzzle to be solved. The programme solves the puzzle, not the user. ??????? self.entry_grid = [[None] * self.num_cols for _ in range(self.num_rows)] > If it works it is "correct" for some definition of correct. Both working correctly and being understandable is the issue here. > But personally here is how I would tackle your problem. > > I'd just use a single list of 81 cells. I'll think about this. As dn pointed out, knowing where the line endings are could be a complication. I'll think about this and helper functions. I'm a little reluctant to move from the model that I have because I understand how it represents the board, I'm just having a problem with manipulating the board grid to extract the columns. I don't have a problem with the rows. I will experiment with the class methods provided by Dennis, it may persuade me to change from a list of lists to a snake list as dn calls it. > I'd then write helper functions to extract the sublists I needed: > > def get_cell(table, row, col) > > def get_row(table, row_num) > > def get_col(table, col_num) > > def get_box(table, row_num, col_num) # or just box_num? I don't have a function that just gets a column. Column extraction is performed as part of other functions. I know that a function should do just one job, so I'll work on that. I have started a function that gets the box number but I cannot see, at the moment, how to integrate it with the rest of my programme. > The next problem (based on previous posts) is that your > cells are sets which are immutable, so you need to: > 1) create another set of functions for writing back changes > to a cell - tricky... I'm not sure that I understand why this is difficult. This is how I update a cell: self.solution[row][col] = set(num) > OR > 2) use lists instead of sets because they are mutable. I had originally used lists instead of sets as a result of my translation from arrays to lists. The use of sets was an experiment that didn't cause any harm so it's use stayed. > OR > 3) Use OOP and make your cells objects with methods > to add/remove values and using a set internally to > check uniqueness etc. This would be my choice. I'll give this some thought as well. > Personally, I'd use OOP for the table too, and make the > functions above methods of the table. So you would have > a table object with 81 mutable cells. > > But you haven't mentioned OOP up to now so that may be > an extra learning curve you don't want to deal with. A grid class would probably be a good idea, more to think about. -- Regards, Phil From phillor9 at gmail.com Tue Dec 7 20:06:51 2021 From: phillor9 at gmail.com (Phil) Date: Wed, 8 Dec 2021 12:06:51 +1100 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 8/12/21 08:47, dn via Tutor wrote: > > row, column and a region." A "region" is what has been called a "box" > earlier in this thread. To me a box is a 3 x 3 grid of cells. > When you separate the two ways to look at a Sudoku, life becomes a lot > easier.self.solution[row][found_col] = set(num)mpleted this project I pride myself in not being a rigid thinker but I suppose I am, other wise I would have completed this project by now. > For this reason, I would use 2D/nested dict[ionarie]s. This has the > added-advantage that the author may label each column/row, as-desired. Someone else also suggested the use of a list of dictionaries but I didn't see the point at the time. However, now that I've used the counter method from the collections module I can see that a dictionary might be useful after all. > The idea here, is to maintain a list of nine lists which represent the > nine rows of the tableau. The second pile of nine lists represents the > columns. Yes, when you boil-it-down, the data is repeated - each 'cell' > appears once in a row list and again in a column list - and when a new > value is added to the tableau, it must be added in two places. Ouch! I'm not sure about the idea of two tables, more complications perhaps? > So, after theorising, it was time to 'have some fun'. To compare the > 'snake' with a 2D dict solution, I prepared some code and sufficient > tests which progress from: > > An initial tableau: > > ------------------------------------- > | | 2 | 7 | | 8 | 9 | 6 | | | > ------------------------------------- > | | | | | | 1 | 7 | | 2 | > ------------------------------------- > | 9 | | | | | | | | | > ------------------------------------- > | | | | | 5 | | | | 7 | > ------------------------------------- > | 1 | 7 | | | | | | 2 | 6 | > ------------------------------------- > | 8 | | | | 4 | | | | | > ------------------------------------- > | | | | | | | | | 4 | > ------------------------------------- > | 2 | | 8 | 5 | | | | | | > ------------------------------------- > | | | 1 | 8 | 6 | | 2 | 3 | | > ------------------------------------- This is a rather easy puzzle to solve, my programme solved it in 3 passes. I'm not skiting, just pointing out that I have achieved something. Are the several descriptions sufficient to start you off? Yes, thank you and keep your code safe, I may contact you. -- Regards, Phil From alan.gauld at yahoo.co.uk Tue Dec 7 20:10:56 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Wed, 8 Dec 2021 01:10:56 +0000 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 08/12/2021 00:31, Phil wrote: > The second list of lists represents the Tkinter GUI where the user can > enter a puzzle to be solved. The programme solves the puzzle, not the user. I try to keep the presentation logic as far away from the game logic as possible. Usually drawing a user friendly picture of algorithmic data is easier than forming algorithms around user friendly layouts! >> I'd just use a single list of 81 cells. > I'll think about this. As dn pointed out, knowing where the line endings > are could be a complication. That's why you need the helper function. It abstracts the underlying data structure from the higher level concept of solving the problem per row, column and box. This keeps the game rules separate from the slicing and dicing of data collection. Each function (row/column/box returns the appropriate list of cells. The validation is the exact same for each, you just pass the list of 9 cells to the validator function > I'm a little reluctant to move from the model that I have because I > understand how it represents the board, I'm just having a problem with > manipulating the board grid to extract the columns. Exactly, you are trying to separate rows/columns and boxes all in one place while simultaneously trying to apply the rules. That's a lot of mental concepts to juggle in one piece of code. > I'd then write helper functions to extract the sublists I needed: >> >> def get_cell(table, row, col) >> >> def get_row(table, row_num) >> >> def get_col(table, col_num) >> >> def get_box(table, row_num, col_num) # or just box_num? > > I don't have a function that just gets a column. Column extraction is > performed as part of other functions. I know that a function should do > just one job, so I'll work on that. I have started a function that gets > the box number but I cannot see, at the moment, how to integrate it with > the rest of my programme. As I understand it you need to check rows columns and boxes. If its an interactive game you might just need to check the move for validity and completion of the puzzle - thats much easier than actually solving the puzzle. But if you are catering for interactive play rather than purely solving the puzzle then you probably need helper functions that can return the required items for any given cell anyway, so those helpers get used both in checking and in solving. >> The next problem (based on previous posts) is that your >> cells are sets which are immutable, so you need to: >> 1) create another set of functions for writing back changes >> to a cell - tricky... > > I'm not sure that I understand why this is difficult. This is how I > update a cell: > > self.solution[row][col] = set(num) I'm not entirely sure what the set holds, I've assuming the set of possible values, which reduces in size as other cells get completed? The problem is you cannot easily edit a cell directly, you need to extract the set, create a new set and then assign the new set back to the cell. If you use a list instead you can address the cell list directly and edit in place. For example if you just use bare sets in a list: sl = [{1,2},{3,4}] for st in sl: sl = sl + 9 # uh uh, immutable set Does not modify the original values in sl. You need to use the index and replace the original list with a new one. But using a list: ll = [[1,2],[3,4]] for lst in ll: lst.append(9) ## ok, mutable list Does modify the original list. Much simpler code. >> 3) Use OOP and make your cells objects with methods >> Personally, I'd use OOP for the table too, and make the >... > A grid class would probably be a good idea, more to think about. I notice you said to dn that this originally was C++ so I'll assume classes and objects are familiar even though you haven't used them thus far. Classes could simplify the top level code but will not make the overall code any shorter. But they should hide the more murky details. -- 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 Tue Dec 7 23:43:33 2021 From: PyTutor at DancesWithMice.info (dn) Date: Wed, 8 Dec 2021 17:43:33 +1300 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: On 08/12/2021 13.31, Phil wrote: > On 7/12/21 20:34, Alan Gauld via Tutor wrote: > > I previously mentioned that this current sudoku solver project is one > that I've translated from a C++ project that I wrote many years ago. It > does, as it stands, solve all but the more difficult puzzles. I've added > some extra solving strategies over the past couple of months and I'm > attempting to make the code, even though it works, more understandable. Which indicates I have 'fallen-down on the job' because the code I threw-together only assures a 'move' - it doesn't 'design' the next move/step towards solution! (further comments, to follow) > Visualising and manipulating objects in space is one of my many failings > and one that cost me a job at an IBM interview. This is a surprise, because back in the ?good, old, days (when our Stone Age computers ran on dino-ary code), we spent a lot of time drawing diagrams. In the beginning we were banned from writing code unless we had first flowchart-ed a solution. Later we moved to DFDs (Data Flow Diagrams) and DSDs (Data Structure Diagrams) etc. Yes, the world has moved-on, and many don't feel they are 'working' unless they're writing code. (wrong!) That doesn't mean that we 'silver surfers' have moved with it! However, before some young-buck jumps-in to prove superiority, it actually gives us an advantage, because we can pick-and-choose between approaches and use the one which works best for us - instead of 'the one' that 'everyone' uses! Suitability beats 'trending', all day long! I laugh when people ask me to demonstrate problem-solving ("whiteboarding" a problem/solution), usually with the expectation that this will present some sort of 'challenge' (like the fear of public-speaking?); because that's exactly what I do, always, even something 'small' like this, and even if I have to use the proverbial paper-napkin or 'back of a post-card' ("no correspondence will be entered into"). Cognitive Psychology, my research area (yes, that's two counts of 'weird' - at least), has shown that people who write notes, or even doodle, during a lecture, will remember 'more'. An irony is that many write notes and then don't need to refer to them later - which becomes an error when 'logic' says: don't bother making the notes then! Perhaps you might try sketching a few rows and columns to see if the visual (and labelling!) helps, particularly when it comes to the problem of extracting columnar information from what appears to be horizontal data-structures. >> I believe you are building a model of a Sudoku game? >> But from your description it's not clear what data >> structure you are using. Please just show it! >> > The first list of lists represents the board. > > ??????? self.solution = [[None] * self.num_cols for _ in > range(self.num_rows)] > > The second list of lists represents the Tkinter GUI where the user can > enter a puzzle to be solved. The programme solves the puzzle, not the user. > > ??????? self.entry_grid = [[None] * self.num_cols for _ in > range(self.num_rows)] +1 (refer back to @Alan's advice about Separation of Concerns, and mine about different ways of 'seeing'/handling the same information) >> If it works it is "correct" for some definition of correct. > Both working correctly and being understandable is the issue here. You've had such a fire-hose of information thrown at you, it'll take time - and much re-reading and 'reading around', to absorb it all. Perhaps some slowing down to 'walk before we can run' is in-order? Much of the advice is related to simplification and solving a series of 'lower-level' problems rather than trying to do 'too much' in one go. (this echoed and echoing...) >> But personally here is how I would tackle your problem. >> >> I'd just use a single list of 81 cells. > I'll think about this. As dn pointed out, knowing where the line endings > are could be a complication. I'll think about this and helper functions. > I'm a little reluctant to move from the model that I have because I > understand how it represents the board, I'm just having a problem with > manipulating the board grid to extract the columns. I don't have a > problem with the rows. I will experiment with the class methods provided > by Dennis, it may persuade me to change from a list of lists to a snake > list as dn calls it. >> I'd then write helper functions to extract the sublists I needed: >> >> def get_cell(table, row, col) >> >> def get_row(table, row_num) >> >> def get_col(table, col_num) >> >> def get_box(table, row_num, col_num)? # or just box_num? > > I don't have a function that just gets a column. Column extraction is > performed as part of other functions. I know that a function should do > just one job, so I'll work on that. I have started a function that gets > the box number but I cannot see, at the moment, how to integrate it with > the rest of my programme. Neither could I, but then 'solving' wasn't in the spec. With regard to these 'helper functions' (or methods), three of us have now suggested the exact same thing. (I'll be billing both of them for "stealing" my idea...) Because selecting a column of data will be required at various stages within the program[me], doesn't that SHOUT: "make me a function"? If it's only a few lines of code (see below), that's not a problem. A function (once tested and proven) is one less 'problem' to consider! (conversely, a function with only one line of code is possibly NOT a valid candidate) Thinking 'small' is probably a virtue, at this stage of learning. With the two-list idea, finding columnar-data is trivial - which column, ie which list, do you want? EOJ! With the 2D dicts (that is a dict of dicts, not a dict of lists, or v-v!), what is required is to 'hold' the row-index fixed, and iterate the column-index. I'd think this very familiar from the approaches required/imposed/implemented in earlier languages, eg: def check_col( self, col: int, value: int, ) -> None: """Assure no duplicate values.""" column = [ self.board[ row ][ col ] for row in self.board_dimensions ] if value in column: raise ValueError( "No duplicate values." ) Notice that because my partial-solution/illustration can enjoy the relative-luxury of not needing a transition between algorithm data-structures and GUI-structures, the only time column-data is needed is during the duplicate-check. In your situation, it would be better to split the above function into halves, perhaps something like (untested): def get_column( self, col: int, ) -> list[ int ]: """Extract nominated column-data from the board.""" return [ self.board[ row ][ col ] for row in self.board_dimensions ] def check_col( self, col: int, value: int, ) -> None: """Assure no duplicate values.""" column = self.get_column( col ) if value in column: raise ValueError( "No duplicate values." ) If the list-comprehension (in the return) is more complex than preferred, 'unwind' it to a multi-line for-loop, appending one item per loop, with preparatory list declaration, and use that list as the return-value. Now, get_column() can be used wherever needed, from multiple points in the code, etc. The solution for the 'snake' is to use slices (and 'striding') - by definition all of the values which constitute one 'column' must be nine cells apart from each-other in the 1D 'snake'. Thus, row = 0, column = 0 is the zero-th cell, and row = 1, column = 0 will have a snake-index of 9, ie 0 + 9. Proceeding: row = 2, column = 0 is nine cells further 'along', ie 0 + 9 + 9. Pretty soon, you'll notice that instead of adding 9 each time, we could multiply by the number of rows 'down'! def get_col( self, col_index: int, ) -> None: """Return single column from board.""" first_cell_index = col_index % 9 return [ self.board[ cell_index ] for cell_index in range( first_cell_index, 81, 9 ) ] The reasons for the "remainder", is the presumption that the column could be defined as that above-and-below any cell on the board, cf a column-number. Once the nominated-cell's column is ascertained, the column-number becomes the start of the 'stride'. (81 being the length of the snaking tableau, and 9 being the width of each row) As you can see, in both 'solutions', a 'get-column' "helper function" furnishes utility! Also, here's-hoping that one-or-the-other will help to make the technique 'click' in your mind. (no 'click' is perhaps not a snooty, cognitive-psych term - but who will understand these days if I say "when the penny drops"? Maybe an "ahah moment"!) >> The next problem (based on previous posts) is that your >> cells are sets which are immutable, so you need to: >> 1) create another set of functions for writing back changes >> to a cell - tricky... > > I'm not sure that I understand why this is difficult. This is how I > update a cell: > > self.solution[row][col] = set(num) Why a set? How to be sure that the cell is not already 'occupied' by part of the puzzle-definition? >> OR >> 2) use lists instead of sets because they are mutable. > I had originally used lists instead of sets as a result of my > translation from arrays to lists. The use of sets was an experiment that > didn't cause any harm so it's use stayed. Be careful! A set has no (guarantee of) sequence. Sequence is important when it comes to the tableau. Sets are useful when it comes to ensuring that a digit isn't used twice in one row/column/region... (as below) = tools for the job >> OR >> 3) Use OOP and make your cells objects with methods >> to add/remove values and using a set internally to >> check uniqueness etc. This would be my choice. > I'll give this some thought as well. >> Personally, I'd use OOP for the table too, and make the >> functions above methods of the table. So you would have >> a table object with 81 mutable cells. >> >> But you haven't mentioned OOP up to now so that may be >> an extra learning curve you don't want to deal with. > > A grid class would probably be a good idea, more to think about. > I pride myself in not being a rigid thinker but I suppose I am, other wise I would have completed this project by now. You have been considering so many alternatives, started and re-started, are learning Python, and trying to 'remember' old-code. Confusion reigns! > I'm not sure about the idea of two tables, more complications perhaps? "Two" sounds more complicated than "one'. However, it will have the advantage of forcing you to concentrate on one (little) thing at a time. If you take-to-heart the suggestion of 'helper functions', you can do everything (easier) that needs to be done with rows first. Thereafter, you can put such thoughts out of your mind, and concentrate on similar for columns. Because the two function independently at all times, except when data is (twice) added to the tableau - only one is necessary to assess completeness/success/transfer to output, etc; it does not make 'the work' more complicated. However, success at this is going to require the process of sitting-down and planning/sketching/charting. When you know how to put individual data-points into the row/column/snake/tableau, how to pull-out data from specific cells, or entire row/column constructs, and such-like; then you have the 'little problems' which can be individually solved, coded one-at-a-time AND you can test each as you go, eg if column-n is requested the value-returned will be of x-type and contain the following values... This enables you to ensure that each helper function works (properly!) BEFORE incorporating it/the columnar-data retrieved, into a wider part of the algorithm! eg if the final 'solution' is presented with whole rows of 1s and rows of 2s, there is plainly a fault - but where? It is unlikely to be in the GUI, or the solver. Almost certainly it will 'start' at a lower-level, in - (wait for it) - a helper-function. So, testing each 'little bit' first, is worthwhile, because it adds confidence as the snow-ball starts to grow larger, and at the same time as that happens, reduces the number of things to be kept in-mind... > This is a rather easy puzzle to solve, my programme solved it in 3 passes. I'm not skiting, just pointing out that I have achieved something. ...and well done! Nothing succeeds like success! Yes, I think it was described as a 'medium' or maybe 'hard' puzzle. No matter, it was only needed to test those (dare I say it again?) 'helper functions'. > Are the several descriptions sufficient to start you off? > > Yes, thank you and keep your code safe, I may contact you. Because you are treating this as a learning exercise (and after reviewing it again), I'm still comfortable recommending the two-list approach. Once solved, you won't keep it (shouldn't keep it?) but it might help with solving the non-Python problems you have outlined. Once you have a 'model' straightened-out in your mind (confidence that you know where to go and how to get there), then I'd suggest using/expanding/amending that set of helper-functions to implement the 2D dictionary approach. Once you're really feeling like you've 'earned your stripes', then have a go at the 'snake'. However, if you prefer the more mathematical type of problem/algorithm, if the first step and/or diagramming/charting gives you confidence, then maybe snake-second. An algorithmic thinker will prefer the 'snake', because (s)he has greater confidence in 'playing with numbers' and an ability to 'see the patterns'. The 2D approach has the advantage that one is more easily able to 'see' what is what, and which is where. That code is sitting in my GitLab instance - but remember, its purpose is to illustrate 'the basics' and compare approaches. It does not 'do' half of what your system apparently intends! -- Regards =dn -- Regards, =dn From wlfraed at ix.netcom.com Wed Dec 8 00:56:38 2021 From: wlfraed at ix.netcom.com (Dennis Lee Bieber) Date: Wed, 08 Dec 2021 00:56:38 -0500 Subject: [Tutor] The Python way and two dimensional lists References: Message-ID: On Wed, 8 Dec 2021 12:06:51 +1100, Phil declaimed the following: >This is a rather easy puzzle to solve, my programme solved it in 3 >passes. I'm not skiting, just pointing out that I have achieved something. > How do you define "passes"? Just for giggles I took an old, unfinished solver I'd started over a decade ago, and modified it for Python3.x (I think I'd already 2to3 over it as the "print" statements were up-to-date -- but the change of "int / int => float" killed it. Had to change the "/" to "//". I'm going to put it as text attachments to avoiding spamming too many -- as I recall the group does allow plain text as an attachment. I'll also include a run of the program with your data set. As you'll see, I only manage to solve the simpler levels and reach deadlock for some cases. This code does NOT use the class I illustrated earlier. 1) Python2.x hadn't introduced "sets" at the time I wrote it, so everything is lists; 2) it uses three classes: Cell (one cell with flags for locked [provided start value, and maybe solved value] along with a list of candidates); Grid (a 3x3 set of Cell -- your box/region); Table (a 3x3 set of Grid). -- Wulfraed Dennis Lee Bieber AF6VN wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ -------------- next part -------------- # Dec 8 2021 -- update to Python 3.x import time class Cell(object): def __init__(self): self.locked = False # final value found self.value = None self.candidates = [1, 2, 3, 4, 5, 6, 7, 8, 9] def set(self, value): self.locked = True self.value = value self.candidates = None def eliminate(self, value): if not self.locked: if value in self.candidates: self.candidates.remove(value) return True return False class Grid(object): def __init__(self): self.cells = [ [Cell(), Cell(), Cell()], [Cell(), Cell(), Cell()], [Cell(), Cell(), Cell()] ] def completed(self): for r in range(3): for c in range(3): if not self.cells[r][c].locked: return False return True class Table(object): def __init__(self): self.grids = [ [Grid(), Grid(), Grid()], [Grid(), Grid(), Grid()], [Grid(), Grid(), Grid()] ] self.rows = [None] * 9 for r in range(9): row = [None] * 9 for c in range(9): row[c] = self.grids[r // 3][c // 3].cells[r % 3][c % 3] def set(self, row, col, value): self.grids[row // 3][col // 3].cells[row % 3][col % 3].set(value) def get(self, row, col): return self.grids[row // 3][col // 3].cells[row % 3][col % 3] def eliminate(self, row, col, value): changed = False for c in range(9): changed = (self.grids[row // 3][c // 3].cells[row % 3][c % 3].eliminate(value) or changed) for r in range(9): changed = (self.grids[r // 3][col // 3].cells[r % 3][col % 3].eliminate(value) or changed) grid = self.grids[row // 3][col // 3] for c in range(3): for r in range(3): changed = (grid.cells[r][c].eliminate(value) or changed) return changed def display(self): print("\n\n0 1 2 3 4 5 6 7 8\n==================") for r in range(9): for c in range(9): if self.get(r, c).value: print("%s" % (self.get(r, c).value), end=' ') else: print(" ", end=' ') print("|%s" % r) def completed(self): for r in range(3): for c in range(3): if not self.grids[r][c].completed(): return False return True if __name__ == "__main__": myTable = Table() print("\n\nEnter a value of 0 to exit setup") print("\tRow and Column range 0..8") while True: cin = input("Enter the cell Value Row Column (space separated): ") try: cv, cr, cc = cin.split() value = int(cv) if value == 0: break row = int(cr) col = int(cc) myTable.set(row, col, value) myTable.display() print() except: print("Error processing input: try again") pass print("\n\nBuilding table ", end=' ') for r in range(9): for c in range(9): if myTable.get(r, c).locked: myTable.eliminate(r, c, myTable.get(r, c).value) myTable.display() while True: print("Evaluating ") change = False done = True for r in range(9): for c in range(9): print(".", end=' ') time.sleep(0.01) if not myTable.get(r, c).locked: if len(myTable.get(r, c).candidates) == 1: myTable.set(r, c, myTable.get(r, c).candidates[0]) change = myTable.eliminate(r, c, myTable.get(r, c).value) myTable.display() if change: break if change: break print() # check for completion if myTable.completed(): print("Completed") break if not change: deadlock = True print("Resolving initial deadlock") for gr in range(3): for gc in range(3): myGrid = myTable.grids[gr][gc] count = [0] * 9 if deadlock: for r in range(3): for c in range(3): if deadlock and myGrid.cells[r][c].candidates: for n in myGrid.cells[r][c].candidates: count[n-1] += 1 try: n = count.index(1) + 1 deadlock = False for r in range(3): for c in range(3): if (myGrid.cells[r][c].candidates and n in myGrid.cells[r][c].candidates): myGrid.cells[r][c].set(n) myTable.eliminate(gr * 3 + r, gc * 3 + c, n) myTable.display() except: pass if deadlock: print("Deadlocked; must be resolved manually") cin = input("Enter value row column: ") cv, cr, cc = cin.split() value = int(cv) row = int(cr) col = int(cc) myTable.set(row, col, value) myTable.eliminate(row, col, value) myTable.display() cv, cr, cc = cin.split() value = int(cv) row = int(cr) col = int(cc) myTable.set(row, col, value) myTable.eliminate(row, col, value) -------------- next part -------------- C:\Users\Wulfraed\Documents\_Hg-Repositories\Python Progs>sudoku.py Enter a value of 0 to exit setup Row and Column range 0..8 Enter the cell Value Row Column (space separated): 2 0 1 0 1 2 3 4 5 6 7 8 ================== 2 |0 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 7 0 2 0 1 2 3 4 5 6 7 8 ================== 2 7 |0 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 8 0 4 0 1 2 3 4 5 6 7 8 ================== 2 7 8 |0 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 9 0 5 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 |0 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 6 0 6 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 1 1 5 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 7 1 6 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 2 1 8 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 9 2 0 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 5 3 4 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 7 3 8 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 1 4 0 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 7 4 1 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 2 4 7 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 6 4 8 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 8 5 0 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 4 5 4 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 |6 |7 |8 Enter the cell Value Row Column (space separated): 4 6 8 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 |7 |8 Enter the cell Value Row Column (space separated): 2 7 0 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 |7 |8 Enter the cell Value Row Column (space separated): 8 7 2 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 |7 |8 Enter the cell Value Row Column (space separated): 5 7 3 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 |8 Enter the cell Value Row Column (space separated): 1 8 2 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 |8 Enter the cell Value Row Column (space separated): 8 8 3 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 |8 Enter the cell Value Row Column (space separated): 6 8 4 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 |8 Enter the cell Value Row Column (space separated): 2 8 6 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 |8 Enter the cell Value Row Column (space separated): 3 8 7 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Enter the cell Value Row Column (space separated): 0 0 0 Building table 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . 0 1 2 3 4 5 6 7 8 ================== 2 7 8 9 6 |0 3 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . 0 1 2 3 4 5 6 7 8 ================== 2 7 4 8 9 6 |0 3 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . 0 1 2 3 4 5 6 7 8 ================== 2 7 4 8 9 6 |0 6 3 1 7 2 |1 9 |2 5 7 |3 1 7 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 1 2 3 4 5 6 7 8 ================== 2 7 4 8 9 6 |0 6 3 1 7 2 |1 9 |2 5 7 |3 1 7 9 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 1 2 3 4 5 6 7 8 ================== 2 7 4 8 9 6 |0 6 3 1 7 2 |1 9 |2 5 7 |3 1 7 3 9 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 1 2 3 4 5 6 7 8 ================== 2 7 4 8 9 6 |0 6 3 1 7 2 |1 9 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock 0 1 2 3 4 5 6 7 8 ================== 2 7 4 8 9 6 |0 6 3 1 7 2 |1 9 1 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock 0 1 2 3 4 5 6 7 8 ================== 2 7 4 8 9 6 |0 6 3 1 7 2 |1 9 1 6 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock 0 1 2 3 4 5 6 7 8 ================== 3 2 7 4 8 9 6 |0 6 3 1 7 2 |1 9 1 6 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock 0 1 2 3 4 5 6 7 8 ================== 3 2 7 4 8 9 6 |0 8 6 3 1 7 2 |1 9 1 6 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock 0 1 2 3 4 5 6 7 8 ================== 3 2 7 4 8 9 6 |0 8 6 3 1 7 2 |1 9 1 6 5 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock 0 1 2 3 4 5 6 7 8 ================== 3 2 7 4 8 9 6 |0 8 6 3 1 7 9 2 |1 9 1 6 5 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock 0 1 2 3 4 5 6 7 8 ================== 3 2 7 4 8 9 6 |0 8 6 3 1 7 9 2 |1 9 1 6 5 |2 5 7 |3 1 7 3 9 8 2 6 |4 8 4 |5 9 4 |6 2 8 5 |7 1 8 6 2 3 |8 Evaluating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resolving initial deadlock Deadlocked; must be resolved manually Enter value row column: From wlfraed at ix.netcom.com Wed Dec 8 01:23:04 2021 From: wlfraed at ix.netcom.com (Dennis Lee Bieber) Date: Wed, 08 Dec 2021 01:23:04 -0500 Subject: [Tutor] The Python way and two dimensional lists References: Message-ID: On Wed, 8 Dec 2021 11:31:45 +1100, Phil declaimed the following: >Visualising and manipulating objects in space is one of my many failings >and one that cost me a job at an IBM interview. > You would have hated the ASVAB of the mid-70s... One section of the test is: given a "flattened/unfolded" box pattern, which of the four boxes (where you only see three sides) matches the FU pattern. {I'm fairly certain I did well in that area... But I'm not sure which part of the score it applied to... My worst score was in "Clerical" -- a 78 I believe -- and that was still better than some of my class-mates best sections. https://www.military.com/join-armed-forces/asvab Clerical doesn't seem to be a category in the current ASVAB https://www.military.com/join-armed-forces/asvab/versions-of-asvab.html -- there it is, on the page showing how the categories are summed to get final results https://www.military.com/join-armed-forces/asvab/asvab-and-army-jobs.html At the time, the student ASVAB only reported on something like 6 final groupings} -- Wulfraed Dennis Lee Bieber AF6VN wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ From phillor9 at gmail.com Wed Dec 8 03:14:05 2021 From: phillor9 at gmail.com (Phil) Date: Wed, 8 Dec 2021 19:14:05 +1100 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: <90b75927-a14c-1c95-451f-17e1ba597b29@gmail.com> On 8/12/21 17:23, Dennis Lee Bieber wrote: > On Wed, 8 Dec 2021 11:31:45 +1100, Phil declaimed the > following: > > >> Visualising and manipulating objects in space is one of my many failings >> and one that cost me a job at an IBM interview. >> > You would have hated the ASVAB of the mid-70s... One section of the > test is: given a "flattened/unfolded" box pattern, which of the four boxes > (where you only see three sides) matches the FU pattern. That's exactly the test that I was referring to. I passed the phone interview but my spatial ability didn't measure up. Back in the olden days I could read a text book from cover to cover and remember enough to achieve a distinction, or better. Now, I cannot remember what I had for lunch. -- Regards, Phil From DomainAdmin at DancesWithMice.info Tue Dec 7 23:41:15 2021 From: DomainAdmin at DancesWithMice.info (David L Neil) Date: Wed, 8 Dec 2021 17:41:15 +1300 Subject: [Tutor] The Python way and two dimensional lists In-Reply-To: References: Message-ID: <6e65603b-7704-79b9-0695-19337c9505ea@DancesWithMice.info> On 08/12/2021 13.31, Phil wrote: > On 7/12/21 20:34, Alan Gauld via Tutor wrote: > > I previously mentioned that this current sudoku solver project is one > that I've translated from a C++ project that I wrote many years ago. It > does, as it stands, solve all but the more difficult puzzles. I've added > some extra solving strategies over the past couple of months and I'm > attempting to make the code, even though it works, more understandable. Which indicates I have 'fallen-down on the job' because the code I threw-together only assures a 'move' - it doesn't 'design' the next move/step towards solution! (further comments, to follow) > Visualising and manipulating objects in space is one of my many failings > and one that cost me a job at an IBM interview. This is a surprise, because back in the ?good, old, days (when our Stone Age computers ran on dino-ary code), we spent a lot of time drawing diagrams. In the beginning we were banned from writing code unless we had first flowchart-ed a solution. Later we moved to DFDs (Data Flow Diagrams) and DSDs (Data Structure Diagrams) etc. Yes, the world has moved-on, and many don't feel they are 'working' unless they're writing code. (wrong!) That doesn't mean that we 'silver surfers' have moved with it! However, before some young-buck jumps-in to prove superiority, it actually gives us an advantage, because we can pick-and-choose between approaches and use the one which works best for us - instead of 'the one' that 'everyone' uses! Suitability beats 'trending', all day long! I laugh when people ask me to demonstrate problem-solving ("whiteboarding" a problem/solution), usually with the expectation that this will present some sort of 'challenge' (like the fear of public-speaking?); because that's exactly what I do, always, even something 'small' like this, and even if I have to use the proverbial paper-napkin or 'back of a post-card' ("no correspondence will be entered into"). Cognitive Psychology, my research area (yes, that's two counts of 'weird' - at least), has shown that people who write notes, or even doodle, during a lecture, will remember 'more'. An irony is that many write notes and then don't need to refer to them later - which becomes an error when 'logic' says: don't bother making the notes then! Perhaps you might try sketching a few rows and columns to see if the visual (and labelling!) helps, particularly when it comes to the problem of extracting columnar information from what appears to be horizontal data-structures. >> I believe you are building a model of a Sudoku game? >> But from your description it's not clear what data >> structure you are using. Please just show it! >> > The first list of lists represents the board. > > ??????? self.solution = [[None] * self.num_cols for _ in > range(self.num_rows)] > > The second list of lists represents the Tkinter GUI where the user can > enter a puzzle to be solved. The programme solves the puzzle, not the user. > > ??????? self.entry_grid = [[None] * self.num_cols for _ in > range(self.num_rows)] +1 (refer back to @Alan's advice about Separation of Concerns, and mine about different ways of 'seeing'/handling the same information) >> If it works it is "correct" for some definition of correct. > Both working correctly and being understandable is the issue here. You've had such a fire-hose of information thrown at you, it'll take time - and much re-reading and 'reading around', to absorb it all. Perhaps some slowing down to 'walk before we can run' is in-order? Much of the advice is related to simplification and solving a series of 'lower-level' problems rather than trying to do 'too much' in one go. (this echoed and echoing...) >> But personally here is how I would tackle your problem. >> >> I'd just use a single list of 81 cells. > I'll think about this. As dn pointed out, knowing where the line endings > are could be a complication. I'll think about this and helper functions. > I'm a little reluctant to move from the model that I have because I > understand how it represents the board, I'm just having a problem with > manipulating the board grid to extract the columns. I don't have a > problem with the rows. I will experiment with the class methods provided > by Dennis, it may persuade me to change from a list of lists to a snake > list as dn calls it. >> I'd then write helper functions to extract the sublists I needed: >> >> def get_cell(table, row, col) >> >> def get_row(table, row_num) >> >> def get_col(table, col_num) >> >> def get_box(table, row_num, col_num)? # or just box_num? > > I don't have a function that just gets a column. Column extraction is > performed as part of other functions. I know that a function should do > just one job, so I'll work on that. I have started a function that gets > the box number but I cannot see, at the moment, how to integrate it with > the rest of my programme. Neither could I, but then 'solving' wasn't in the spec. With regard to these 'helper functions' (or methods), three of us have now suggested the exact same thing. (I'll be billing both of them for "stealing" my idea...) Because selecting a column of data will be required at various stages within the program[me], doesn't that SHOUT: "make me a function"? If it's only a few lines of code (see below), that's not a problem. A function (once tested and proven) is one less 'problem' to consider! (conversely, a function with only one line of code is possibly NOT a valid candidate) Thinking 'small' is probably a virtue, at this stage of learning. With the two-list idea, finding columnar-data is trivial - which column, ie which list, do you want? EOJ! With the 2D dicts (that is a dict of dicts, not a dict of lists, or v-v!), what is required is to 'hold' the row-index fixed, and iterate the column-index. I'd think this very familiar from the approaches required/imposed/implemented in earlier languages, eg: def check_col( self, col: int, value: int, ) -> None: """Assure no duplicate values.""" column = [ self.board[ row ][ col ] for row in self.board_dimensions ] if value in column: raise ValueError( "No duplicate values." ) Notice that because my partial-solution/illustration can enjoy the relative-luxury of not needing a transition between algorithm data-structures and GUI-structures, the only time column-data is needed is during the duplicate-check. In your situation, it would be better to split the above function into halves, perhaps something like (untested): def get_column( self, col: int, ) -> list[ int ]: """Extract nominated column-data from the board.""" return [ self.board[ row ][ col ] for row in self.board_dimensions ] def check_col( self, col: int, value: int, ) -> None: """Assure no duplicate values.""" column = self.get_column( col ) if value in column: raise ValueError( "No duplicate values." ) If the list-comprehension (in the return) is more complex than preferred, 'unwind' it to a multi-line for-loop, appending one item per loop, with preparatory list declaration, and use that list as the return-value. Now, get_column() can be used wherever needed, from multiple points in the code, etc. The solution for the 'snake' is to use slices (and 'striding') - by definition all of the values which constitute one 'column' must be nine cells apart from each-other in the 1D 'snake'. Thus, row = 0, column = 0 is the zero-th cell, and row = 1, column = 0 will have a snake-index of 9, ie 0 + 9. Proceeding: row = 2, column = 0 is nine cells further 'along', ie 0 + 9 + 9. Pretty soon, you'll notice that instead of adding 9 each time, we could multiply by the number of rows 'down'! def get_col( self, col_index: int, ) -> None: """Return single column from board.""" first_cell_index = col_index % 9 return [ self.board[ cell_index ] for cell_index in range( first_cell_index, 81, 9 ) ] The reasons for the "remainder", is the presumption that the column could be defined as that above-and-below any cell on the board, cf a column-number. Once the nominated-cell's column is ascertained, the column-number becomes the start of the 'stride'. (81 being the length of the snaking tableau, and 9 being the width of each row) As you can see, in both 'solutions', a 'get-column' "helper function" furnishes utility! Also, here's-hoping that one-or-the-other will help to make the technique 'click' in your mind. (no 'click' is perhaps not a snooty, cognitive-psych term - but who will understand these days if I say "when the penny drops"? Maybe an "ahah moment"!) >> The next problem (based on previous posts) is that your >> cells are sets which are immutable, so you need to: >> 1) create another set of functions for writing back changes >> to a cell - tricky... > > I'm not sure that I understand why this is difficult. This is how I > update a cell: > > self.solution[row][col] = set(num) Why a set? How to be sure that the cell is not already 'occupied' by part of the puzzle-definition? >> OR >> 2) use lists instead of sets because they are mutable. > I had originally used lists instead of sets as a result of my > translation from arrays to lists. The use of sets was an experiment that > didn't cause any harm so it's use stayed. Be careful! A set has no (guarantee of) sequence. Sequence is important when it comes to the tableau. Sets are useful when it comes to ensuring that a digit isn't used twice in one row/column/region... (as below) = tools for the job >> OR >> 3) Use OOP and make your cells objects with methods >> to add/remove values and using a set internally to >> check uniqueness etc. This would be my choice. > I'll give this some thought as well. >> Personally, I'd use OOP for the table too, and make the >> functions above methods of the table. So you would have >> a table object with 81 mutable cells. >> >> But you haven't mentioned OOP up to now so that may be >> an extra learning curve you don't want to deal with. > > A grid class would probably be a good idea, more to think about. > I pride myself in not being a rigid thinker but I suppose I am, other wise I would have completed this project by now. You have been considering so many alternatives, started and re-started, are learning Python, and trying to 'remember' old-code. Confusion reigns! > I'm not sure about the idea of two tables, more complications perhaps? "Two" sounds more complicated than "one'. However, it will have the advantage of forcing you to concentrate on one (little) thing at a time. If you take-to-heart the suggestion of 'helper functions', you can do everything (easier) that needs to be done with rows first. Thereafter, you can put such thoughts out of your mind, and concentrate on similar for columns. Because the two function independently at all times, except when data is (twice) added to the tableau - only one is necessary to assess completeness/success/transfer to output, etc; it does not make 'the work' more complicated. However, success at this is going to require the process of sitting-down and planning/sketching/charting. When you know how to put individual data-points into the row/column/snake/tableau, how to pull-out data from specific cells, or entire row/column constructs, and such-like; then you have the 'little problems' which can be individually solved, coded one-at-a-time AND you can test each as you go, eg if column-n is requested the value-returned will be of x-type and contain the following values... This enables you to ensure that each helper function works (properly!) BEFORE incorporating it/the columnar-data retrieved, into a wider part of the algorithm! eg if the final 'solution' is presented with whole rows of 1s and rows of 2s, there is plainly a fault - but where? It is unlikely to be in the GUI, or the solver. Almost certainly it will 'start' at a lower-level, in - (wait for it) - a helper-function. So, testing each 'little bit' first, is worthwhile, because it adds confidence as the snow-ball starts to grow larger, and at the same time as that happens, reduces the number of things to be kept in-mind... > This is a rather easy puzzle to solve, my programme solved it in 3 passes. I'm not skiting, just pointing out that I have achieved something. ...and well done! Nothing succeeds like success! Yes, I think it was described as a 'medium' or maybe 'hard' puzzle. No matter, it was only needed to test those (dare I say it again?) 'helper functions'. > Are the several descriptions sufficient to start you off? > > Yes, thank you and keep your code safe, I may contact you. Because you are treating this as a learning exercise (and after reviewing it again), I'm still comfortable recommending the two-list approach. Once solved, you won't keep it (shouldn't keep it?) but it might help with solving the non-Python problems you have outlined. Once you have a 'model' straightened-out in your mind (confidence that you know where to go and how to get there), then I'd suggest using/expanding/amending that set of helper-functions to implement the 2D dictionary approach. Once you're really feeling like you've 'earned your stripes', then have a go at the 'snake'. However, if you prefer the more mathematical type of problem/algorithm, if the first step and/or diagramming/charting gives you confidence, then maybe snake-second. An algorithmic thinker will prefer the 'snake', because (s)he has greater confidence in 'playing with numbers' and an ability to 'see the patterns'. The 2D approach has the advantage that one is more easily able to 'see' what is what, and which is where. That code is sitting in my GitLab instance - but remember, its purpose is to illustrate 'the basics' and compare approaches. It does not 'do' half of what your system apparently intends! -- Regards =dn From mats at wichmann.us Wed Dec 8 10:23:27 2021 From: mats at wichmann.us (Mats Wichmann) Date: Wed, 8 Dec 2021 08:23:27 -0700 Subject: [Tutor] Error When Using CoolProp In-Reply-To: References: Message-ID: <77e9e881-cb51-93b7-e52e-c52a9698e446@wichmann.us> On 12/8/21 08:18, Bernard Marjaba wrote: > Kind reminder please > > On Mon, Nov 29, 2021 at 7:01 PM Bernard Marjaba > > wrote: > > Hello, > > Yeah sure I?m checking with them as well. Meanwhile, this is a > purely python related error following what you suggested to me about > installing the wheel package: > > I tried installing the wheel package using > > (venv) Bernards-MacBook-Pro:Python Bernard$ pip install > /Users/Bernard/Downloads/CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl > > but it gave me this error > > ERROR: CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl is not a > supported wheel on this platform. > > I assume this means that i need python 3.8 for this wheel to work, > which I have. I typed python3 in terminal it gave me 3.8.10, i typed > python3 in PyCharm and it gave me 3.9.5, how come? So i searched for > python 3.9 in my MacBook and i couldn?t find any traces of it, > except for in a project folder: > /Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/lib/python3.9 > > However, 3.8 exists in the Applications folder. Any idea how to > remove all traces of 3.9? > you select the interpreter in PyCharm independently of what the system thinks the default is - it's a clickable field in the bottom of the screen. Probably you'll be okay when you select 3.8. If you choose to make a virtualenv, remember you have to install your packages in that virtualenv for them to be picked up - from the path you show it looks like you ended up with a 3.9 virtualenv. you can do that through pycharm, if that's what you intend to use. they have lots of docs on that. From marjababernard at gmail.com Wed Dec 8 10:18:33 2021 From: marjababernard at gmail.com (Bernard Marjaba) Date: Wed, 8 Dec 2021 10:18:33 -0500 Subject: [Tutor] Error When Using CoolProp In-Reply-To: References: Message-ID: Kind reminder please On Mon, Nov 29, 2021 at 7:01 PM Bernard Marjaba wrote: > Hello, > > Yeah sure I?m checking with them as well. Meanwhile, this is a purely > python related error following what you suggested to me about installing > the wheel package: > > I tried installing the wheel package using > > (venv) Bernards-MacBook-Pro:Python Bernard$ pip install > /Users/Bernard/Downloads/CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl > > but it gave me this error > > ERROR: CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl is not a supported > wheel on this platform. > > I assume this means that i need python 3.8 for this wheel to work, which I > have. I typed python3 in terminal it gave me 3.8.10, i typed python3 in > PyCharm and it gave me 3.9.5, how come? So i searched for python 3.9 in my > MacBook and i couldn?t find any traces of it, except for in a project > folder: /Users/Bernard/PycharmProjects/Isentropic SUS > Model/venv/lib/python3.9 > > However, 3.8 exists in the Applications folder. Any idea how to remove all > traces of 3.9? > > > > *Thanks and regards,Bernard Marjaba(514) 922-9807* > > On Nov 24, 2021, at 6:53 PM, Mats Wichmann wrote: > > On 11/24/21 13:17, Bernard Marjaba wrote: > > Hello > I am having trouble using CoolProp with python. I have macOS Big Sur > 11.5.1 with python version 3.10. > import CoolProp.CoolProp as CP > is giving me the error ModuleNotFoundError: No module named > ?CoolProp.CoolProp? because it is not installed. I am however using it with > MATLAB with no issues. I assume each software has its own wrapper? > So I tried installing CoolProp using pip install coolprop, and got the > following error: > Collecting coolprop > Using cached CoolProp-6.4.1.tar.gz (12.9 MB) > Preparing metadata (setup.py) ... done > Using legacy 'setup.py install' for coolprop, since package 'wheel' is not > installed. > Installing collected packages: coolprop > Running setup.py install for coolprop ... error > ERROR: Command errored out with exit status 1: > command: '/Users/Bernard/PycharmProjects/Isentropic SUS > Model/venv/bin/python' -u -c 'import io, os, sys, setuptools, tokenize; > sys.argv[0] = > '"'"'/private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/setup.py'"'"'; > __file__='"'"'/private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/setup.py'"'"';f > = getattr(tokenize, '"'"'open'"'"', open)(__file__) if > os.path.exists(__file__) else io.StringIO('"'"'from setuptools import > setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', > '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' > install --record > /private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-record-8453i4i7/install-record.txt > --single-version-externally-managed --compile --install-headers > '/Users/Bernard/PycharmProjects/Isentropic SUS > Model/venv/include/site/python3.9/coolprop' > cwd: > /private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/ > Complete output (79 lines): > xcrun: error: invalid active developer path > (/Library/Developer/CommandLineTools), missing xcrun at: > /Library/Developer/CommandLineTools/usr/bin/xcrun > OSX build detected, targetting 10.9 using clang/gcc v0.0. > Cython will not be used; cy_ext is cpp > running install > /Users/Bernard/PycharmProjects/Isentropic SUS > Model/venv/lib/python3.9/site-packages/setuptools/command/install.py:34: > SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and > pip and other standards-based tools. > warnings.warn( > running build > running build_py > creating build > creating build/lib.macosx-10.9-x86_64-3.9 > creating build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/constants.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/__init__.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/BibtexParser.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/HumidAirProp.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/State.py -> build/lib.macosx-10.9-x86_64-3.9/CoolProp > creating build/lib.macosx-10.9-x86_64-3.9/CoolProp/tests > copying CoolProp/tests/runner.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/tests > copying CoolProp/tests/test_plots.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/tests > copying CoolProp/tests/test_Props.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/tests > copying CoolProp/tests/__init__.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/tests > copying CoolProp/tests/test_CoolPropState.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/tests > creating build/lib.macosx-10.9-x86_64-3.9/CoolProp/GUI > copying CoolProp/GUI/__init__.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/GUI > copying CoolProp/GUI/CoolPropGUI.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/GUI > copying CoolProp/GUI/PsychScript.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/GUI > creating build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/ConsistencyPlots_pcsaft.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/PsychChart.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/SimpleCycles.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/__init__.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/psy.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/Plots.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/Common.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/SimpleCyclesCompression.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/ConsistencyPlots.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/PsychScript.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/Tests.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/Plots/SimpleCyclesExpansion.py -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > copying CoolProp/typedefs.pxd -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/CoolProp.pxd -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/State.pxd -> build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/cAbstractState.pxd -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/constants_header.pxd -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/AbstractState.pxd -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp > copying CoolProp/Plots/psyrc -> > build/lib.macosx-10.9-x86_64-3.9/CoolProp/Plots > running build_ext > creating private > creating private/var > creating private/var/folders > creating private/var/folders/1j > creating private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn > creating private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T > creating > private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx > creating > private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4 > gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common > -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g > -I/Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/include > -I/Library/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c > /private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/tmp0hdk2tsu.cpp > -o > private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/tmp0hdk2tsu.o > xcrun: error: invalid active developer path > (/Library/Developer/CommandLineTools), missing xcrun at: > /Library/Developer/CommandLineTools/usr/bin/xcrun > gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common > -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g > -I/Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/include > -I/Library/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c > /private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/tmp0r3vfej0.cpp > -o > private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/tmp0r3vfej0.o > xcrun: error: invalid active developer path > (/Library/Developer/CommandLineTools), missing xcrun at: > /Library/Developer/CommandLineTools/usr/bin/xcrun > gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common > -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g > -I/Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/include > -I/Library/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c > /private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/tmp93394q_4.cpp > -o > private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/tmp93394q_4.o > xcrun: error: invalid active developer path > (/Library/Developer/CommandLineTools), missing xcrun at: > /Library/Developer/CommandLineTools/usr/bin/xcrun > Adding these shared_ptr compilation macros: [] > building 'CoolProp.CoolProp' extension > creating build/temp.macosx-10.9-x86_64-3.9 > creating build/temp.macosx-10.9-x86_64-3.9/CoolProp > creating build/temp.macosx-10.9-x86_64-3.9/src > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends/Cubics > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends/Helmholtz > creating > build/temp.macosx-10.9-x86_64-3.9/src/Backends/Helmholtz/Fluids > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends/IF97 > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends/Incompressible > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends/PCSAFT > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends/REFPROP > creating build/temp.macosx-10.9-x86_64-3.9/src/Backends/Tabular > creating build/temp.macosx-10.9-x86_64-3.9/src/Tests > gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common > -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g -I. -I./include > -I./src -I./externals/Eigen -I./externals/fmtlib > -I./externals/msgpack-c/include -I/Users/Bernard/PycharmProjects/Isentropic > SUS Model/venv/include > -I/Library/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c > CoolProp/CoolProp.cpp -o > build/temp.macosx-10.9-x86_64-3.9/CoolProp/CoolProp.o > xcrun: error: invalid active developer path > (/Library/Developer/CommandLineTools), missing xcrun at: > /Library/Developer/CommandLineTools/usr/bin/xcrun > error: command '/usr/bin/gcc' failed with exit code 1 > ---------------------------------------- > ERROR: Command errored out with exit status 1: > '/Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/bin/python' -u -c > 'import io, os, sys, setuptools, tokenize; sys.argv[0] = > '"'"'/private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/setup.py'"'"'; > __file__='"'"'/private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-install-z5afcrzx/coolprop_6739af7137474652994855ae32a03dd4/setup.py'"'"';f > = getattr(tokenize, '"'"'open'"'"', open)(__file__) if > os.path.exists(__file__) else io.StringIO('"'"'from setuptools import > setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', > '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' > install --record > /private/var/folders/1j/jv39r0t12cb4dhscxkdz97740000gn/T/pip-record-8453i4i7/install-record.txt > --single-version-externally-managed --compile --install-headers > '/Users/Bernard/PycharmProjects/Isentropic SUS > Model/venv/include/site/python3.9/coolprop' Check the logs for full command > output. > > > > looks like you probably don't have the command-line developer tools > installed - it's separate from the main xcode install on MacOS. > > also, you might be able to install without the effort to build if you > installed the wheel package first - see this error from early on: > > > Using legacy 'setup.py install' for coolprop, since package 'wheel' is > not installed. > > I have no idea what CoolProp is, never heard of it, but a check here: > > https://pypi.org/project/CoolProp/#files > > shows there *is* a macosx wheel available for installation - at least as > long as you're using no newer than Python 3.8. The project looks like it > hasn't updated for a good long time so they haven't done anything for 3.9 > or 3.10. That's probably cause for some worry... > > You'd be far better asking this kind of thing directly to the project in > question, we won't be able to answer any further stuff from here. > > > From marjababernard at gmail.com Wed Dec 8 16:31:19 2021 From: marjababernard at gmail.com (Bernard Marjaba) Date: Wed, 8 Dec 2021 16:31:19 -0500 Subject: [Tutor] Error When Using CoolProp In-Reply-To: <77e9e881-cb51-93b7-e52e-c52a9698e446@wichmann.us> References: <77e9e881-cb51-93b7-e52e-c52a9698e446@wichmann.us> Message-ID: <9DDFF682-63D7-488E-8B44-9BB59C57CB6B@gmail.com> ok thanks, i changed the environment to 3.8. but now when i try to run any command with pip in it, it is giving me this error /Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/bin/pip: line 2: /Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/bin/python: No such file or directory /Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/bin/pip: line 2: exec: /Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/bin/python: cannot execute: No such file or directory I checked if this folder exists, and it does. Thanks and regards, Bernard Marjaba (514) 922-9807 > On Dec 8, 2021, at 10:23 AM, Mats Wichmann wrote: > > On 12/8/21 08:18, Bernard Marjaba wrote: >> Kind reminder please >> On Mon, Nov 29, 2021 at 7:01 PM Bernard Marjaba > wrote: >> Hello, >> Yeah sure I?m checking with them as well. Meanwhile, this is a >> purely python related error following what you suggested to me about >> installing the wheel package: >> I tried installing the wheel package using >> (venv) Bernards-MacBook-Pro:Python Bernard$ pip install >> /Users/Bernard/Downloads/CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl >> but it gave me this error >> ERROR: CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl is not a >> supported wheel on this platform. >> I assume this means that i need python 3.8 for this wheel to work, >> which I have. I typed python3 in terminal it gave me 3.8.10, i typed >> python3 in PyCharm and it gave me 3.9.5, how come? So i searched for >> python 3.9 in my MacBook and i couldn?t find any traces of it, >> except for in a project folder: >> /Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/lib/python3.9 >> However, 3.8 exists in the Applications folder. Any idea how to >> remove all traces of 3.9? > > you select the interpreter in PyCharm independently of what the system thinks the default is - it's a clickable field in the bottom of the screen. Probably you'll be okay when you select 3.8. If you choose to make a virtualenv, remember you have to install your packages in that virtualenv for them to be picked up - from the path you show it looks like you ended up with a 3.9 virtualenv. you can do that through pycharm, if that's what you intend to use. they have lots of docs on that. From acianci15 at gmail.com Thu Dec 9 10:28:13 2021 From: acianci15 at gmail.com (Anthony Cianci) Date: Thu, 9 Dec 2021 10:28:13 -0500 Subject: [Tutor] Plotting multiple lines on one graph with same X values Message-ID: Hello, I am looking to plot multiple lines (1,000+) from an excel file. I am new to Python and have figured out how to graph one dataframe using pandas, but I am not sure how to include the rest of the data without writing a new line of code for each "tag". [image: image.png] [image: image.png] Thank you, Anthony From mhedges21 at gmail.com Thu Dec 9 05:07:48 2021 From: mhedges21 at gmail.com (Mike Hedges) Date: Thu, 9 Dec 2021 11:07:48 +0100 Subject: [Tutor] Need help developing DSP within GUI. Message-ID: Hey Tutors! This is my first time attempting this, so I hope I'm doing it accurately. I just need a few pointers on how to get multiple elements to function within the same space. I'm working on an interface that displays a comparison of different pitch tracking methods. I have the YIN and CREPE methods [mostly] scripted out, now I'm trying to build an interface that displays their values as real-time audio is being inputted. Would someone be able to help me wrap my mind around getting this to work together? Maybe we could have a one-on-one session or something? I'm pretty novice with Python, but I have some understanding of how it works. Thanks! Also, my timezone is UTC +01:00. Best Regards, Mike From wlfraed at ix.netcom.com Thu Dec 9 19:29:55 2021 From: wlfraed at ix.netcom.com (Dennis Lee Bieber) Date: Thu, 09 Dec 2021 19:29:55 -0500 Subject: [Tutor] Plotting multiple lines on one graph with same X values References: Message-ID: On Thu, 9 Dec 2021 10:28:13 -0500, Anthony Cianci declaimed the following: >Hello, > >I am looking to plot multiple lines (1,000+) from an excel file. >I am new to Python and have figured out how to graph one dataframe using >pandas, but I am not sure how to include the rest of the data without >writing a new line of code for each "tag". >[image: image.png] > Text attachments only on this forum -- anything else gets stripped. If you must reference an image, you will have to find some image hosting provider, and include URLs to the images one that provider. I recommend you do not use a provider that creates cryptic URLs -- I, for one, will not click on any URL that does not have a definable target (eg: .../image1.png is okay, .../8374917ds87374lf could be anything including trojans and will not be clicked). As for your subject -- does matplotlib have anything that may help? Otherwise you may be down to scripting something that extracts the "tags", and loops over them executing a suitable plot call. -- Wulfraed Dennis Lee Bieber AF6VN wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ From mats at wichmann.us Fri Dec 10 16:51:11 2021 From: mats at wichmann.us (Mats Wichmann) Date: Fri, 10 Dec 2021 14:51:11 -0700 Subject: [Tutor] Error When Using CoolProp In-Reply-To: References: <77e9e881-cb51-93b7-e52e-c52a9698e446@wichmann.us> Message-ID: <29300e4e-4bcf-24be-73ba-5a0c31134e54@wichmann.us> On 12/10/21 13:49, Bernard Marjaba wrote: > Ok I changed the version like you said, and coolprop is successfully > installed. Now I?m getting this error when trying to install matplotlib. > I already installed pip for python 3.8, but i think the issue here is > accessing the right path, because here it shows its accessing 2.7 > instead of 3.8. How do I fix this issue? Use the python for installing that you intend to use for running. if "python" is 2.7 and "python3" is 3.8, then the install should be python3 -m pip install -U matplotlib From marjababernard at gmail.com Fri Dec 10 15:49:32 2021 From: marjababernard at gmail.com (Bernard Marjaba) Date: Fri, 10 Dec 2021 15:49:32 -0500 Subject: [Tutor] Error When Using CoolProp In-Reply-To: <77e9e881-cb51-93b7-e52e-c52a9698e446@wichmann.us> References: <77e9e881-cb51-93b7-e52e-c52a9698e446@wichmann.us> Message-ID: Ok I changed the version like you said, and coolprop is successfully installed. Now I?m getting this error when trying to install matplotlib. I already installed pip for python 3.8, but i think the issue here is accessing the right path, because here it shows its accessing 2.7 instead of 3.8. How do I fix this issue? Bernards-MacBook-Pro:Python Bernard$ python -m pip install -U matplotlib /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python: No module named pip Thanks and regards, Bernard Marjaba (514) 922-9807 > On Dec 8, 2021, at 10:23 AM, Mats Wichmann wrote: > > On 12/8/21 08:18, Bernard Marjaba wrote: >> Kind reminder please >> On Mon, Nov 29, 2021 at 7:01 PM Bernard Marjaba > wrote: >> Hello, >> Yeah sure I?m checking with them as well. Meanwhile, this is a >> purely python related error following what you suggested to me about >> installing the wheel package: >> I tried installing the wheel package using >> (venv) Bernards-MacBook-Pro:Python Bernard$ pip install >> /Users/Bernard/Downloads/CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl >> but it gave me this error >> ERROR: CoolProp-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl is not a >> supported wheel on this platform. >> I assume this means that i need python 3.8 for this wheel to work, >> which I have. I typed python3 in terminal it gave me 3.8.10, i typed >> python3 in PyCharm and it gave me 3.9.5, how come? So i searched for >> python 3.9 in my MacBook and i couldn?t find any traces of it, >> except for in a project folder: >> /Users/Bernard/PycharmProjects/Isentropic SUS Model/venv/lib/python3.9 >> However, 3.8 exists in the Applications folder. Any idea how to >> remove all traces of 3.9? > > you select the interpreter in PyCharm independently of what the system thinks the default is - it's a clickable field in the bottom of the screen. Probably you'll be okay when you select 3.8. If you choose to make a virtualenv, remember you have to install your packages in that virtualenv for them to be picked up - from the path you show it looks like you ended up with a 3.9 virtualenv. you can do that through pycharm, if that's what you intend to use. they have lots of docs on that. From leamhall at gmail.com Fri Dec 10 21:32:31 2021 From: leamhall at gmail.com (Leam Hall) Date: Fri, 10 Dec 2021 20:32:31 -0600 Subject: [Tutor] Using Python for the job search Message-ID: <2f95293a-bd59-b83b-9918-1d83ea2c85bf@gmail.com> Found out that the job I'm on is going away shortly, and I'm enjoying a wonderful job search. Keeping the same resume in multiple formats gets tiring, so I started to convert some old code to Python to make things easier. I could use some feedback on how to improve the code. https://github.com/LeamHall/resume_writer If you want to play with it, move "sample_data" to "data", and then go through the README. Thanks! Leam -- Site Reliability Engineer (reuel.net/resume) Scribe: The Domici War (domiciwar.net) General Ne'er-do-well (github.com/LeamHall) From juliushamilton100 at gmail.com Sat Dec 11 06:47:01 2021 From: juliushamilton100 at gmail.com (Julius Hamilton) Date: Sat, 11 Dec 2021 12:47:01 +0100 Subject: [Tutor] Packaging questions Message-ID: Hey, I just went through the pip packaging tutorial. It seemed to be mainly for making importable modules, i.e. files of functions you can access via import library I was hoping to package a command line application that can be used by invoking its name on the command line. The tutorial had me (basically) make an outermost project directory and put the actual project code in a subdirectory, ?src?, and specify that was the ?root? directory in the config file. As far as I could tell, the only other essential file was either a ?setup.cfg? file or a ?setup.py? file, (and a blank ?__init__? file in src as well). Could anyone let me know why I would choose the one over the other, and if some people choose to have both? Which information in the config file is mandatory? I entered name, description, long_description, project_urls, license and much else. What?s the bare minimum possible? What should I do if I want my pip installation to be a command line application instead of an importable library of functions? Do I need a more sophisticated setup.py file which installs an executable in usr/bin or something? Thanks very much, Julius From juliushamilton100 at gmail.com Sat Dec 11 08:05:24 2021 From: juliushamilton100 at gmail.com (Julius Hamilton) Date: Sat, 11 Dec 2021 14:05:24 +0100 Subject: [Tutor] Publish Python web application Message-ID: Hey, I would like to make a web application available publicly on the internet, likely written in Python. Is there a standard way to host it? Amazon web services or GitHub pages? Thanks, Julius From alan.gauld at yahoo.co.uk Sun Dec 12 06:17:59 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Sun, 12 Dec 2021 11:17:59 +0000 Subject: [Tutor] Packaging questions In-Reply-To: References: Message-ID: Sent from my iPad > What should I do if I want my pip installation to be a command line > application instead of an importable library of functions? Do I need a more > sophisticated setup.py file which installs an executable in usr/bin or > something? You do know about the if __name__ == ?__main__?: Idiom for making a module executable, right? That means an executable script and a module are the same thing. The only other requirement is that the user has a compatible python installed. If you are looking at installing python as well then you might be better off looking at tools like py2exe for windows, or similar tools for other OS? From alan.gauld at yahoo.co.uk Sun Dec 12 06:21:50 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Sun, 12 Dec 2021 11:21:50 +0000 Subject: [Tutor] Publish Python web application In-Reply-To: References: Message-ID: <0C402679-6E1A-48F2-B432-A7A30952137D@yahoo.co.uk> Sent from my iPad > On 12 Dec 2021, at 00:44, Julius Hamilton wrote: > > ?Hey, > > I would like to make a web application available publicly on the internet, > likely written in Python. Do you mean make the code available so that others can set up their own version of it? Or do you simply want to make your application available to all internet users? > > Is there a standard way to host it? If the former then GitHub or pypi. If the latter then any web hosting service. > > Amazon web services or GitHub pages? Amazon might be appropriate depending on the app but my guess is it would be overkill. Alan g. From alexkleider at gmail.com Sun Dec 12 19:35:47 2021 From: alexkleider at gmail.com (Alex Kleider) Date: Sun, 12 Dec 2021 16:35:47 -0800 Subject: [Tutor] class attributes Message-ID: I'm trying to write a __repr__ method that can be easily changed to show only the attributes in which I'm currently interested and want to be able to easily change the attributes of interest. My attempted solution is as follows: def __repr__(self): attrs = ['SECRETARY', # an easy to modify listing 'PATTERN', # of attributes of interest ] ret = [] for attr in attrs: if hasattr(self, attr): ret.append("{}::{}".format(attr, self.attr)) else: ret.append("{}::unassigned".format(attr)) return ','.join(ret) Not surprisingly, I get the following error: """ ret.append("{}::{}".format(attr, self.attr)) AttributeError: 'Club' object has no attribute 'attr' """ I'd be grateful if anyone could suggest a way to get around the problem. The built in method 'hasattr' is able to do the introspection necessary to do its work so it seems what I want should be "doable". Thanks in advance, Alex From bouncingcats at gmail.com Sun Dec 12 20:23:47 2021 From: bouncingcats at gmail.com (David) Date: Mon, 13 Dec 2021 12:23:47 +1100 Subject: [Tutor] class attributes In-Reply-To: References: Message-ID: On Mon, 13 Dec 2021 at 11:37, Alex Kleider wrote: > I'm trying to write a __repr__ method that can be easily changed to > show only the attributes in which I'm currently interested and want to > be able to easily change the attributes of interest. > My attempted solution is as follows: > > def __repr__(self): > attrs = ['SECRETARY', # an easy to modify listing > 'PATTERN', # of attributes of interest > ] > ret = [] > for attr in attrs: > if hasattr(self, attr): > ret.append("{}::{}".format(attr, self.attr)) > else: > ret.append("{}::unassigned".format(attr)) > return ','.join(ret) > > Not surprisingly, I get the following error: > """ > ret.append("{}::{}".format(attr, self.attr)) > AttributeError: 'Club' object has no attribute 'attr' > """ > > I'd be grateful if anyone could suggest a way to get around the > problem. The built in method 'hasattr' is able to do the introspection > necessary to do its work so it seems what I want should be "doable". Python has builtin functions getattr() and setattr() which take a string argument for the attribute name, in the same way such information is passed to hasattr(). From cs at cskk.id.au Sun Dec 12 19:56:10 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Mon, 13 Dec 2021 11:56:10 +1100 Subject: [Tutor] class attributes In-Reply-To: References: Message-ID: On 12Dec2021 16:35, Alex Kleider wrote: >I'm trying to write a __repr__ method that can be easily changed to >show only the attributes in which I'm currently interested and want to >be able to easily change the attributes of interest. >My attempted solution is as follows: > > def __repr__(self): > attrs = ['SECRETARY', # an easy to modify listing > 'PATTERN', # of attributes of interest > ] > ret = [] > for attr in attrs: > if hasattr(self, attr): > ret.append("{}::{}".format(attr, self.attr)) > else: > ret.append("{}::unassigned".format(attr)) > return ','.join(ret) > >Not surprisingly, I get the following error: >""" > ret.append("{}::{}".format(attr, self.attr)) >AttributeError: 'Club' object has no attribute 'attr' >""" > >I'd be grateful if anyone could suggest a way to get around the >problem. The built in method 'hasattr' is able to do the introspection >necessary to do its work so it seems what I want should be "doable". getattr(self, attr_name) Cheers, Cameron Simpson From alexkleider at gmail.com Sun Dec 12 21:06:28 2021 From: alexkleider at gmail.com (Alex Kleider) Date: Sun, 12 Dec 2021 18:06:28 -0800 Subject: [Tutor] class attributes In-Reply-To: References: Message-ID: Problem solved (and so easily!) A sincere thank you to both (Cameron & David) of you (and to the list in general.) Alex On Sun, Dec 12, 2021 at 5:35 PM Cameron Simpson wrote: > On 12Dec2021 16:35, Alex Kleider wrote: > >I'm trying to write a __repr__ method that can be easily changed to > >show only the attributes in which I'm currently interested and want to > >be able to easily change the attributes of interest. > >My attempted solution is as follows: > > > > def __repr__(self): > > attrs = ['SECRETARY', # an easy to modify listing > > 'PATTERN', # of attributes of interest > > ] > > ret = [] > > for attr in attrs: > > if hasattr(self, attr): > > ret.append("{}::{}".format(attr, self.attr)) > > else: > > ret.append("{}::unassigned".format(attr)) > > return ','.join(ret) > > > >Not surprisingly, I get the following error: > >""" > > ret.append("{}::{}".format(attr, self.attr)) > >AttributeError: 'Club' object has no attribute 'attr' > >""" > > > >I'd be grateful if anyone could suggest a way to get around the > >problem. The built in method 'hasattr' is able to do the introspection > >necessary to do its work so it seems what I want should be "doable". > > getattr(self, attr_name) > > Cheers, > Cameron Simpson > _______________________________________________ > 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 Dec 12 18:03:45 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Mon, 13 Dec 2021 10:03:45 +1100 Subject: [Tutor] Packaging questions In-Reply-To: References: Message-ID: On 12Dec2021 11:17, Alan Gauld wrote: >> What should I do if I want my pip installation to be a command line >> application instead of an importable library of functions? Do I need a more >> sophisticated setup.py file which installs an executable in usr/bin or >> something? > >You do know about the > > if __name__ == ?__main__?: > >Idiom for making a module executable, right? > >That means an executable script and a module are the same thing. Just to follow on, you don't even need that (though you probably want it). The dictionary you supply with the module package can contain an entry like this: 'entry_points': { 'console_scripts': [ 'your_command_name = your.module.name:command_function', ], }, which makes a command line script which calls a function of your choice. The example above would create a script called "your_command_name" which called the "command_function" function in the module "your.module.name" when it is invoked. Remember that you're installing into a shared area and make sure that both your module names and the command name itself are not too generic and thus unlikely to collide with other installed things. An end user can always make an alias for a long command if they use it a lot and start finding the typing tedious. Cheers, Cameron Simpson From marisesea at gmail.com Sun Dec 12 17:16:18 2021 From: marisesea at gmail.com (Marise Miville) Date: Sun, 12 Dec 2021 14:16:18 -0800 Subject: [Tutor] File I/O Help please!! Message-ID: [image: Tutor help 1.png] [image: image.png] I am very new at this and I am trying to write 2 short programs. *Program 1: Prepare a list of the primes less than 1500. Save them in binary form to a file named 'SmallPrimes.dat'* *Program 2: Open the file 'SmallPrimes.dat' and read them into a list. Print them 5 at a time until the list is exhausted.* This is how far I could get without success. What am I doing wrong? Can anyone help me correct my codes? Thank you everyone!!! MM From juliushamilton100 at gmail.com Mon Dec 13 03:43:39 2021 From: juliushamilton100 at gmail.com (Julius Hamilton) Date: Mon, 13 Dec 2021 09:43:39 +0100 Subject: [Tutor] Packaging questions In-Reply-To: References: Message-ID: Thanks. It seems like there are many ways to do this. I did one tutorial that I didn?t quite fully understand but they put a script called ?module-run.py? in the directory, which was the command line execution version. I don?t remember what they did to make it easy to execute though. Do you know which documentation page covers this topic? I might read this next seeing as it goes over many different ?options?: https://packaging.python.org/en/latest/overview/ Thanks, Julius On Mon 13. Dec 2021 at 05:11, Cameron Simpson wrote: > On 12Dec2021 11:17, Alan Gauld wrote: > >> What should I do if I want my pip installation to be a command line > >> application instead of an importable library of functions? Do I need a > more > >> sophisticated setup.py file which installs an executable in usr/bin or > >> something? > > > >You do know about the > > > > if __name__ == ?__main__?: > > > >Idiom for making a module executable, right? > > > >That means an executable script and a module are the same thing. > > Just to follow on, you don't even need that (though you probably want > it). The dictionary you supply with the module package can contain an > entry like this: > > 'entry_points': { > 'console_scripts': [ > 'your_command_name = your.module.name:command_function', > ], > }, > > which makes a command line script which calls a function of your choice. > The example above would create a script called "your_command_name" which > called the "command_function" function in the module "your.module.name" > when it is invoked. > > Remember that you're installing into a shared area and make sure that > both your module names and the command name itself are not too generic > and thus unlikely to collide with other installed things. An end user > can always make an alias for a long command if they use it a lot and > start finding the typing tedious. > > Cheers, > Cameron Simpson > _______________________________________________ > Tutor maillist - Tutor at python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > From david at graniteweb.com Mon Dec 13 09:35:18 2021 From: david at graniteweb.com (David Rock) Date: Mon, 13 Dec 2021 08:35:18 -0600 Subject: [Tutor] File I/O Help please!! In-Reply-To: References: Message-ID: <20211213143518.GP18857@graniteweb.com> * Marise Miville [2021-12-12 14:16]: > > I am very new at this and I am trying to write 2 short programs. > > *Program 1: Prepare a list of the primes less than 1500. Save them in > binary form to a file named 'SmallPrimes.dat'* > > *Program 2: Open the file 'SmallPrimes.dat' and read them into a list. > Print them 5 at a time until the list is exhausted.* > > This is how far I could get without success. What am I doing wrong? Can > anyone help me correct my codes? Two problems: 1. This mailing list strips attachments. Please copy/paste your code as text so we can see it instead of attaching an image. 2. we won't do homework for you. We will give suggestions about where to look, but that will depend on what the code you've done looks like. -- David Rock david at graniteweb.com From wlfraed at ix.netcom.com Mon Dec 13 10:01:30 2021 From: wlfraed at ix.netcom.com (Dennis Lee Bieber) Date: Mon, 13 Dec 2021 10:01:30 -0500 Subject: [Tutor] File I/O Help please!! References: Message-ID: On Sun, 12 Dec 2021 14:16:18 -0800, Marise Miville declaimed the following: >[image: Tutor help 1.png] > >[image: image.png] This is a TEXT-ONLY forum. If possible, cut&paste the TEXT from a console window, do not do image captures. If you must use an image (which would mostly be required if having problems with layout of a GUI interface -- unlikely for a beginner), you will have to acquire some hosting site and post a link (URL) to the image on that site. Preferably use a site that does not obscure the contents under some random/hash sequence, but instead displays the actual file name(s) -- there are those of us who will not open URLs that are a string of hex-digits; only those with known file types shown. >I am very new at this and I am trying to write 2 short programs. > >*Program 1: Prepare a list of the primes less than 1500. Save them in >binary form to a file named 'SmallPrimes.dat'* > Given the two part descriptions these sound a lot like homework. I'm surprised a beginner's exercise for Python specifies "binary-form" -- that's a somewhat advanced module. Common Python I/O works in "human-readable" text form. This program requires two things: a function/procedure for generating prime numbers -- with a limit of <1500 even the Sieve of Eratosthenes (sp?) only has to examine factors up to 750.; and logic to handle the I/O. The binary requirement means using the struct module (you don't mention which version of Python you are using, but the module hasn't changed that much over the decades so https://docs.python.org/3.9/library/struct.html should be usable). >*Program 2: Open the file 'SmallPrimes.dat' and read them into a list. >Print them 5 at a time until the list is exhausted.* > Again... the struct module would be needed to read the file if it was used when writing it. Printing "5 at a time" is probably easiest handled by printing them one-at-a-time WITHOUT LINE-FEEDS and using counter to indicate when a line-feed needs to be printed to start the next line. With a suitable format, you can even get the columns to line up >This is how far I could get without success. What am I doing wrong? Can >anyone help me correct my codes? Without seeing the code or error messages, there is not much we can deduce. -- Wulfraed Dennis Lee Bieber AF6VN wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ From cs at cskk.id.au Mon Dec 13 18:10:20 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Tue, 14 Dec 2021 10:10:20 +1100 Subject: [Tutor] Packaging questions In-Reply-To: References: Message-ID: On 13Dec2021 09:43, Julius Hamilton wrote: >It seems like there are many ways to do this. I think Alan and I have outlined the 2 main ways. Alan's suggestion is standard for modules which can themselves be meaningfully run as a command. I do it all the time. Even if I haven't made a command line mode, the __main__ thing might run some tests. The entry_points thing is the standard way to have the package install (run by "pip install") create a command script in the environment where the install is happening, typically a virtualenv. >I did one tutorial that I >didn?t quite fully understand but they put a script called ?module-run.py? >in the directory, which was the command line execution version. That's fiddly, unless they expect you to modify "module-run.py" in some way. Do Alan's __main__ thing, then you can just go: python -m your_module command line args here ... to run it. >I don?t >remember what they did to make it easy to execute though. Well, you could just go: python module-run.py command line args here ... with no special actions. If you want "module-run.py" as a command (a) it needs execute permission and (b) it needs to be in your $PATH (on UNIXlike systems). Cheers, Cameron Simpson From wlfraed at ix.netcom.com Mon Dec 13 20:52:23 2021 From: wlfraed at ix.netcom.com (Dennis Lee Bieber) Date: Mon, 13 Dec 2021 20:52:23 -0500 Subject: [Tutor] Packaging questions References: Message-ID: On Tue, 14 Dec 2021 10:10:20 +1100, Cameron Simpson declaimed the following: >with no special actions. If you want "module-run.py" as a command (a) it >needs execute permission and (b) it needs to be in your $PATH (on >UNIXlike systems). > And just to provide the dark side... On Windows one needs suitable FTYPE and ASSOC assignments -- eg: C:\Users\Wulfraed>assoc .py .py=Python.File C:\Users\Wulfraed>ftype python.file python.file="C:\WINDOWS\py.exe" "%L" %* C:\Users\Wulfraed>py Python ActivePython 3.8.2 (ActiveState Software Inc.) based on on win32 Type "help", "copyright", "credits" or "license" for more information. >>> Going further, if one doesn't want to provide the .py suffix and just use "module-run", one needs to add it to the PATHEXT variable (hmm, I need to clean mine up -- looks like updating Visual Studio added some...) C:\Users\Wulfraed>set pathext PATHEXT=.PY;.PY3;.PYC;.PYO;.PYW;.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.py;.pyw;.rexx;.rex;.rx C:\Users\Wulfraed> -- Wulfraed Dennis Lee Bieber AF6VN wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ From phillor9 at gmail.com Tue Dec 14 00:43:18 2021 From: phillor9 at gmail.com (Phil) Date: Tue, 14 Dec 2021 16:43:18 +1100 Subject: [Tutor] Tkinter frame background colour Message-ID: <644f93ba-3935-1f0b-a780-0338ffb4dfa6@gmail.com> My original GUI worked but it was a bit of a mess so I've spent most of the day building a new interface to my sudoku solver. However, there is one problem. I used to have a red line after the third and sixth column of boxes, the same with the rows. I achieved this by setting the frame's background to red and placing some padding after the third and sixth group of cells so that the red background would show through and show a red line between the 3 x 3 boxes. Otherwise, I'm happy with the result. import tkinter as tk class Root(tk.Tk): ??? def __init__(self): ??????? super().__init__() ??????? self.title('Sudoku Solver') ??????? self.geometry('300x300') ??????? self.frame = tk.Frame() ??????? self.frame.pack(expand=True) ??????? self.frame.bg="red" ??????? self.entry = [None] * 81 ??????? for i in range(81): ??????????? r, c = divmod(i, 9) ??????????? self.entry[i] = tk.Entry(self.frame, width=2, justify=tk.CENTER, fg="red") ??????????? #if i == i % 3: ??????????? pad_x = (4, 0) ??????????? self.entry[i].grid(row=r, column=c, padx=pad_x) ??????? self.control_frame = tk.Frame() ??????? self.control_frame.pack(side=tk.TOP, pady=5) ??????? self.b_quit = tk.Button(self.control_frame, text='Quit', ??????????? command=self.quit) ??????? self.b_quit.pack(side=tk.LEFT) ??????? self.b_solve = tk.Button(self.control_frame, text='Solve', ??????????? command=self.solve) ??????? self.b_solve.pack(side=tk.RIGHT) ??? def quit(self): ??????? self.destroy() ??? def solve(self): ??????? self.entry[2].insert(0, '9') ??????? print(self.entry[2].get()) if __name__ == '__main__': ??? root = Root() ??? root.mainloop() -- Regards, Phil From alan.gauld at yahoo.co.uk Tue Dec 14 03:53:34 2021 From: alan.gauld at yahoo.co.uk (alan.gauld at yahoo.co.uk) Date: Tue, 14 Dec 2021 08:53:34 +0000 Subject: [Tutor] Tkinter frame background colour In-Reply-To: <644f93ba-3935-1f0b-a780-0338ffb4dfa6@gmail.com> References: <2578db48-b904-4003-bb2a-7b8e99bbf936.ref@email.android.com> Message-ID: <2578db48-b904-4003-bb2a-7b8e99bbf936@email.android.com> You are building a grid so why use the pack manager? it would all be so much easier if you use the grid manager. For the red lines I'd consider setting the borders of the entry widgets as a possibility. But I haven't tried it.... Alan g From my phone On 14 Dec 2021 05:43, Phil wrote: My original GUI worked but it was a bit of a mess so I've spent most of the day building a new interface to my sudoku solver. However, there is one problem. I used to have a red line after the third and sixth column of boxes, the same with the rows. I achieved this by setting the frame's background to red and placing some padding after the third and sixth group of cells so that the red background would show through and show a red line between the 3 x 3 boxes. Otherwise, I'm happy with the result. import tkinter as tk class Root(tk.Tk): ??? def __init__(self): ??????? super().__init__() ??????? self.title('Sudoku Solver') ??????? self.geometry('300x300') ??????? self.frame = tk.Frame() ??????? self.frame.pack(expand=True) ??????? self.frame.bg="red" ??????? self.entry = [None] * 81 ??????? for i in range(81): ??????????? r, c = divmod(i, 9) ??????????? self.entry[i] = tk.Entry(self.frame, width=2, justify=tk.CENTER, fg="red") ??????????? #if i == i % 3: ??????????? pad_x = (4, 0) ??????????? self.entry[i].grid(row=r, column=c, padx=pad_x) ??????? self.control_frame = tk.Frame() ??????? self.control_frame.pack(side=tk.TOP, pady=5) ??????? self.b_quit = tk.Button(self.control_frame, text='Quit', ??????????? command=self.quit) ??????? self.b_quit.pack(side=tk.LEFT) ??????? self.b_solve = tk.Button(self.control_frame, text='Solve', ??????????? command=self.solve) ??????? self.b_solve.pack(side=tk.RIGHT) ??? def quit(self): ??????? self.destroy() ??? def solve(self): ??????? self.entry[2].insert(0, '9') ??????? print(self.entry[2].get()) if __name__ == '__main__': ??? root = Root() ??? root.mainloop() -- Regards, Phil _______________________________________________ Tutor maillist? -? Tutor at python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor From phillor9 at gmail.com Tue Dec 14 17:51:06 2021 From: phillor9 at gmail.com (Phil) Date: Wed, 15 Dec 2021 09:51:06 +1100 Subject: [Tutor] Tkinter frame background colour In-Reply-To: <2578db48-b904-4003-bb2a-7b8e99bbf936@email.android.com> References: <2578db48-b904-4003-bb2a-7b8e99bbf936.ref@email.android.com> <2578db48-b904-4003-bb2a-7b8e99bbf936@email.android.com> Message-ID: <07783092-25ae-3559-f496-6bf3e12879c2@gmail.com> Problem solved, I wasn't setting the frame background colour correctly. This is correct: ??????? self.frame.configure(background="red") -- Regards, Phil From mats at wichmann.us Wed Dec 15 12:56:48 2021 From: mats at wichmann.us (Mats Wichmann) Date: Wed, 15 Dec 2021 10:56:48 -0700 Subject: [Tutor] Packaging questions In-Reply-To: References: Message-ID: On December 13, 2021 6:52:23 PM MST, Dennis Lee Bieber wrote: >On Tue, 14 Dec 2021 10:10:20 +1100, Cameron Simpson >declaimed the following: > >>with no special actions. If you want "module-run.py" as a command (a) it >>needs execute permission and (b) it needs to be in your $PATH (on >>UNIXlike systems). >> > And just to provide the dark side... On Windows one needs suitable >FTYPE and ASSOC assignments -- eg: > >C:\Users\Wulfraed>assoc .py >.py=Python.File > >C:\Users\Wulfraed>ftype python.file >python.file="C:\WINDOWS\py.exe" "%L" %* As I've just found out, the value of ftype is fairly useless now, you need to look a lot deeper. >C:\Users\Wulfraed>py >Python ActivePython 3.8.2 (ActiveState Software Inc.) based on > on win32 >Type "help", "copyright", "credits" or "license" for more information. >>>> > > Going further, if one doesn't want to provide the .py suffix and just >use "module-run", one needs to add it to the PATHEXT variable (hmm, I need >to clean mine up -- looks like updating Visual Studio added some...) > >C:\Users\Wulfraed>set pathext >PATHEXT=.PY;.PY3;.PYC;.PYO;.PYW;.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.py;.pyw;.rexx;.rex;.rx > >C:\Users\Wulfraed> > > -- Sent from my Android device with K-9 Mail. Please excuse my brevity. From hemfaume at gmail.com Thu Dec 16 07:52:41 2021 From: hemfaume at gmail.com (Henry Mfaume) Date: Thu, 16 Dec 2021 14:52:41 +0200 Subject: [Tutor] I need help please Message-ID: HI There. i installed anaconda on my pc, and through anaconda installed python, pandas and other programs. i created an environment, but if i want to import from the environment i created, i get the module not found error. someone please help me. Thank you. Henry. From alan.gauld at yahoo.co.uk Fri Dec 17 03:45:12 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Fri, 17 Dec 2021 08:45:12 +0000 Subject: [Tutor] I need help please In-Reply-To: References: Message-ID: On 16/12/2021 12:52, Henry Mfaume wrote: > HI There. > i installed anaconda on my pc, and through anaconda installed python, > pandas and other programs. i created an environment, but if i want to > import from the environment i created, i get the module not found error. Caveat: I've never used anaconda and rarely use environments but... I think that's the correct behaviour. The whole point of virtual environments is to isolate the user from other installations on the computer. So you need to install everything into each environment. It would defeat the purpose of having an environment if they could see and use each others modules. -- 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 Fri Dec 17 13:21:11 2021 From: mats at wichmann.us (Mats Wichmann) Date: Fri, 17 Dec 2021 11:21:11 -0700 Subject: [Tutor] I need help please In-Reply-To: References: Message-ID: On December 17, 2021 1:45:12 AM MST, Alan Gauld via Tutor wrote: >On 16/12/2021 12:52, Henry Mfaume wrote: >> HI There. >> i installed anaconda on my pc, and through anaconda installed python, >> pandas and other programs. i created an environment, but if i want to >> import from the environment i created, i get the module not found error. > >Caveat: >I've never used anaconda and rarely use environments but... > >I think that's the correct behaviour. The whole point of >virtual environments is to isolate the user from other >installations on the computer. So you need to install >everything into each environment. >It would defeat the purpose of having an environment >if they could see and use each others modules. > Do your conda install from inside the active virtualenv -- Sent from my Android device with K-9 Mail. Please excuse my brevity. From phillor9 at gmail.com Wed Dec 22 19:59:37 2021 From: phillor9 at gmail.com (Phil) Date: Thu, 23 Dec 2021 11:59:37 +1100 Subject: [Tutor] method could be a function warning Message-ID: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> I've been in the habit of mixing methods and functions within the GUI class and to get the functions to work I've had to prefix the call to the function with .self and add self as a function argument. The function dummy in the following code is an an example of this. It makes more sense to me, and I think it's correct, to define and call the functions outside the GUI class as I've done with my_function below. Also, I'm thinking that the GUI class should be in a file of it's own because the total number of lines of code exceeds 500 thus making scrolling tedious. I ask because I'm in the process of tidying up a Conway's Game of Life project that I wrote 3 years ago. import tkinter as tk class Root(tk.Tk): ??? def __init__(self): ??????? super().__init__() ??????? self.title('test') ??????? self.geometry('300x300') ??????? self.frame = tk.Frame() ??????? self.dummy() ??? def dummy(self): ??????? print('hello') def my_function(): ??? print('my function') my_function() if __name__ == '__main__': ??? root = Root() ??? root.mainloop() -- Regards, Phil From alan.gauld at yahoo.co.uk Wed Dec 22 20:29:16 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Thu, 23 Dec 2021 01:29:16 +0000 Subject: [Tutor] method could be a function warning In-Reply-To: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> References: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> Message-ID: On 23/12/2021 00:59, Phil wrote: > I've been in the habit of mixing methods and functions within the GUI > class and to get the functions to work I've had to prefix the call to > the function with .self and add self as a function argument. The > function dummy in the following code is an an example of this. The function dummy is a method not a function. A method in OOP is how an object responds to a message. In this case the message is Rootinstance.dummy() and the method for handling that message is Root.dummy In practical terms (in Python) a function defined inside a class which takes as its firs parameter an instance of the same class is a method. The function my_function() is not a method since it is defined outside the class and does not take an instance as its first parameter. > It makes more sense to me, and I think it's correct, to define and call > the functions outside the GUI class as I've done with my_function below. Functions are defined outside the class, methods are defined inside. Functions or methods can be called from inside or outside the class. > Also, I'm thinking that the GUI class should be in a file of it's own > because the total number of lines of code exceeds 500 thus making > scrolling tedious. That's a style issue but once you get beyond a few hundred lines it's usually better to split it into a module. However, your Root class is probably only ever going to have a single instance which represents the application itself. As such I don't know what you are going to put in any higher level file. Won't it just consist of an import and instantiation of Root? (essentially just the last 3 lines below) which seems a bit pointless. > I ask because I'm in the process of tidying up a Conway's Game of Life > project that I wrote 3 years ago. I wouldn't expect that to even take 500 lines in total. I did a (mostly non-OOP*) curses version of life for my recent book and it was only 98 lines in total, a GUI shouldn't be much bigger! Even if you add an interactive start screen which mine didn't have, it shouldn't be more than 200 lines tops. What is making up all that code? (*)It had a Cell class with a few attributes only and 5 functions including a main(). > import tkinter as tk > > class Root(tk.Tk): > ??? def __init__(self): > ??????? super().__init__() > > ??????? self.title('test') > ??????? self.geometry('300x300') > > ??????? self.frame = tk.Frame() > > ??????? self.dummy() > > ??? def dummy(self): > ??????? print('hello') > > def my_function(): > ??? print('my function') > > my_function() > > if __name__ == '__main__': > ??? root = Root() > ??? root.mainloop() > -- 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 Wed Dec 22 20:22:30 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Thu, 23 Dec 2021 12:22:30 +1100 Subject: [Tutor] method could be a function warning In-Reply-To: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> References: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> Message-ID: First: "method could be a function warning" looks like a linter warning. Is it? On 23Dec2021 11:59, Phil wrote: >I've been in the habit of mixing methods and functions within the GUI >class and to get the functions to work I've had to prefix the call to >the function with .self and add self as a function argument. The >function dummy in the following code is an an example of this. Yes. If your linter's complaining, that is because the method does not use self. So it does not need to be a method. That does not not mean it _should _not_ be a method. A function which is tightly associated with a class conceptually does belong in the class. For example, perhaps you've got a method which constructs a string in a way specificly for use with this class. That would naturally be a method. Python has a couple of decorators for methods which don't use self: @classmethod and @staticmethod. You would define dummy() like this and _still_ call it via self. @staticmethod ??? def dummy(): ??????? print('hello') and linters will know you don't expect to use self. These have to do with the context needed for the method. _Mostly_ methods need self because they utilise the object state. Some might just need the class (for example to use some constants defined by the class) - they would need a classmethod: @classmethod def dummy2(cls): return cls.SOMETHING + 1 where SOMETHING is a class attribute. A staticmethod is just a function, and does not need the class or instance for context. >It makes more sense to me, and I think it's correct, to define and >call the functions outside the GUI class as I've done with my_function >below. A function outside a class is a general purpose utility function. I don't have many static methods, but those which are are conceptually related to the class. Here's an example from my Tag class: @staticmethod def is_valid_name(name): ''' Test whether a tag name is valid: a dotted identifier. ''' return is_dotted_identifier(name) So there's a Tag.is_valid_name(name) method for checking that a tag name is valid. It doesn't use the class or instance, but how it is implemented is a direct feature of the class. So it is a static method. >Also, I'm thinking that the GUI class should be in a file of it's own >because the total number of lines of code exceeds 500 thus making >scrolling tedious. You want a tags file, or whatever the equivalent is for your editor. Most code editors have a facility to jump to a symbol definition in your code. This reduces the concern with file size. I prefer to break things up based on their conceptual structure. But there does come a size related time for breaking things up. Cheers, Cameron Simpson From mats at wichmann.us Wed Dec 22 20:38:36 2021 From: mats at wichmann.us (Mats Wichmann) Date: Wed, 22 Dec 2021 18:38:36 -0700 Subject: [Tutor] method could be a function warning In-Reply-To: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> References: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> Message-ID: On 12/22/21 17:59, Phil wrote: > I've been in the habit of mixing methods and functions within the GUI > class and to get the functions to work I've had to prefix the call to > the function with .self and add self as a function argument. The > function dummy in the following code is an an example of this. The suggestion (as I recall it's not a "warning" but a "recommendation", but my memory isn't that great) is that because the method doesn't make any use of "self", why have it be a method that takes "self" as an argument? But it's up to you... it's okay to put something that _could_ have been a global function inside the class because it's logically connected to the things the class does, and not interesting otherwise. You're certainly allowed to add indications (in the form of comments) to tell the checker to shut up about this. > > It makes more sense to me, and I think it's correct, to define and call > the functions outside the GUI class as I've done with my_function below. > Also, I'm thinking that the GUI class should be in a file of it's own > because the total number of lines of code exceeds 500 thus making > scrolling tedious. > > I ask because I'm in the process of tidying up a Conway's Game of Life > project that I wrote 3 years ago. > > import tkinter as tk > > class Root(tk.Tk): > ??? def __init__(self): > ??????? super().__init__() > > ??????? self.title('test') > ??????? self.geometry('300x300') > > ??????? self.frame = tk.Frame() > > ??????? self.dummy() > > ??? def dummy(self): > ??????? print('hello') > > def my_function(): > ??? print('my function') > > my_function() > > if __name__ == '__main__': > ??? root = Root() > ??? root.mainloop() > From cs at cskk.id.au Wed Dec 22 20:46:09 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Thu, 23 Dec 2021 12:46:09 +1100 Subject: [Tutor] method could be a function warning In-Reply-To: References: Message-ID: Just to follow up to myself: On 23Dec2021 12:22, Cameron Simpson wrote: >A function outside a class is a general purpose utility function. I >don't have many static methods, but those which are are conceptually >related to the class. Here's an example from my Tag class: > > @staticmethod > def is_valid_name(name): > ''' Test whether a tag name is valid: a dotted identifier. > ''' > return is_dotted_identifier(name) > >So there's a Tag.is_valid_name(name) method for checking that a tag name >is valid. It doesn't use the class or instance, but how it is >implemented is a direct feature of the class. So it is a static method. So what we're getting from a class staticmethod is that the class name is effectively part of the the function name. Instead of maybe making an external function like: def is_valid_tag_name(name): I've defined a function effectively named "Tag.is_valid_name". Which is quite explicit about what kind of validity it is testing. Also, if you've subclasses, or are writing something else which works on "tag-like" things, you can call: if tag_like_thing.is_valid_name(some_name): and it will get the appropriate is_valid_name() method from the class which implements tag_like_thing, which might not be my "Tag" class but something which works like it. Cheers, Cameron Simpson From phillor9 at gmail.com Wed Dec 22 23:07:36 2021 From: phillor9 at gmail.com (Phil) Date: Thu, 23 Dec 2021 15:07:36 +1100 Subject: [Tutor] method could be a function warning In-Reply-To: References: <6306a283-372d-8406-7021-136e96f8ec5b@gmail.com> Message-ID: On 23/12/21 12:22, Cameron Simpson wrote: > First: "method could be a function warning" looks like a linter warning. > Is it? > > > Yes. If your linter's complaining, that is because the method does not > use self. So it does not need to be a method. That does not not mean it > _should _not_ be a method. Thank you Cameron, Alan and Mats. I didn't mention in my initial message that I had tried: ??????? self.dummy() ??? @staticmethod ??? def dummy(): ??????? print('hello') This quietened pylint but since I still need to prefix the call to dummy() with self. I presumed that what I was doing still wasn't quite correct. Dummy(), not it's real name, doesn't access anything from the gui class. It's used as a helper function, and it's called by several methods that do access a gui widget. So I suppose, based on what you have said, I should us the @staticmethod and leave it at that. -- Regards, Phil From cs at cskk.id.au Wed Dec 22 23:11:03 2021 From: cs at cskk.id.au (Cameron Simpson) Date: Thu, 23 Dec 2021 15:11:03 +1100 Subject: [Tutor] method could be a function warning In-Reply-To: References: Message-ID: On 23Dec2021 15:07, Phil wrote: >Dummy(), not it's real name, doesn't access anything from the gui >class. It's used as a helper function, and it's called by several >methods that do access a gui widget. So I suppose, based on what you >have said, I should us the @staticmethod and leave it at that. If it is conceptually for the purposes of the class, then yes. Cheers, Cameron Simpson From Richard at Damon-Family.org Thu Dec 23 00:19:41 2021 From: Richard at Damon-Family.org (Richard Damon) Date: Thu, 23 Dec 2021 00:19:41 -0500 Subject: [Tutor] method could be a function warning In-Reply-To: References: Message-ID: On 12/22/21 11:11 PM, Cameron Simpson wrote: > On 23Dec2021 15:07, Phil wrote: >> Dummy(), not it's real name, doesn't access anything from the gui >> class. It's used as a helper function, and it's called by several >> methods that do access a gui widget. So I suppose, based on what you >> have said, I should us the @staticmethod and leave it at that. > If it is conceptually for the purposes of the class, then yes. > > Cheers, > Cameron Simpson As a static method, you call call it with self.dummy() or Root.dummy() (i.e. either on an instance of the class or the class itself) as by makeing it a (static) method, the name is put into the namespace of the class. If you don't want to need to prefix it with anything, you need to make it not a member of the class, and then it is just a global. Unlike some languages, just because you are inside a method of a class, Python doesn't automatically assume to start looking in that class for names, but all accesses need to be prefixed with the space to look in. (This may be your confusion, it isn't like C++ for instance which will try to assume the 'this' or 'class' added to the call.) -- Richard Damon From phillor9 at gmail.com Thu Dec 23 03:15:10 2021 From: phillor9 at gmail.com (Phil) Date: Thu, 23 Dec 2021 19:15:10 +1100 Subject: [Tutor] method could be a function warning In-Reply-To: References: Message-ID: <1c5f0cac-7b77-73fa-973d-993c46b5834f@gmail.com> On 23/12/21 16:19, Richard Damon wrote: > > (This may be your confusion, it isn't like C++ for instance which will > try to assume the 'this' or 'class' added to the call.) Thanks Richard, I do find the differences between Python and C++ confusing. I have this difference sorted out now. -- Regards, Phil From chamblissjames51 at gmail.com Fri Dec 24 23:02:56 2021 From: chamblissjames51 at gmail.com (James Chambliss) Date: Fri, 24 Dec 2021 21:02:56 -0700 Subject: [Tutor] (no subject) Message-ID: Need help understanding how to use class decorated...... From alan.gauld at yahoo.co.uk Sat Dec 25 03:53:24 2021 From: alan.gauld at yahoo.co.uk (Alan Gauld) Date: Sat, 25 Dec 2021 08:53:24 +0000 Subject: [Tutor] class decorated was: (no subject) In-Reply-To: References: Message-ID: Please always add a meaningful subject line it helps find things in the archive. On 25/12/2021 04:02, James Chambliss wrote: > Need help understanding how to use class decorated...... You need to be more specific. Are you trying to decorate a class using the standard decorators? Are you trying to write a decorator of your own? Are you trying to decorate the whole class? Or just methods or attributes within the class? Do you have a specific task in mind for this decorator? Or are you just asking out of general interest and curiosity? The more specific the question the more specific the answer. Also, it usually helps if you tell us which Python version and OS you are using. -- 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 juliushamilton100 at gmail.com Sat Dec 25 12:11:26 2021 From: juliushamilton100 at gmail.com (Julius Hamilton) Date: Sat, 25 Dec 2021 18:11:26 +0100 Subject: [Tutor] Break element into list while iterating Message-ID: Hey, I would like to do some kind of list comprehension where elements meeting certain conditions get split on a delimiter. They don?t become lists nested in one element in the previous list; the new elements just get inserted where the old one was. Like this: [?apple?, ?pear?] -> [?a?, ?p?, ?p?, ?l?, ?e?, ?pear?] Is there a list comprehension syntax where if I split an element with e.split(), I can ?insert? those elements at the element they came from? Thanks very much, Julius From juliushamilton100 at gmail.com Sat Dec 25 15:46:02 2021 From: juliushamilton100 at gmail.com (Julius Hamilton) Date: Sat, 25 Dec 2021 21:46:02 +0100 Subject: [Tutor] Segmenting bash help docs Message-ID: Hey, I am pretty close to pulling this off and I thought I?d reach out to fill in a few gaps. I can imagine writing a Bash function which takes the help page and displays it sentence by sentence. If we consider code for doing this step by step: $ help wait > wait.txt $ python3 >>> import spacy, requests >>> f = open(?wait.txt?).read() >>> sen = spacy.load(?en_core_web_sm?)(f) The sentences are pretty well segmented. There is a thread that fascinates me about separating on an even more fine-grained level ( https://stackoverflow.com/questions/65227103/clause-extraction-long-sentence-segmentation-in-python), but I won?t venture there yet. What I can do is first split the current list on any newline regions greater than one newline. >>> sen = [s.text.strip() for s in sen] Then I can compress broken sentences into single lines by replacing extended whitespace with a single whitespace: >>> sen = [re.sub(?[\s]+?, ? ?, s) for s in sen] Not sure if anybody has any opinions on this, or ways they?d improve it. It?d be cool to find a single simple, elegant way to clean, strip and conjoin the sentences/lines in one line of code, and also not to use the ?re? module if not necessary. Thanks, Julius From mats at wichmann.us Sun Dec 26 10:50:32 2021 From: mats at wichmann.us (Mats Wichmann) Date: Sun, 26 Dec 2021 08:50:32 -0700 Subject: [Tutor] Segmenting bash help docs In-Reply-To: References: Message-ID: <4cc909cb-c1ac-6185-0ddc-bfd66e5eba93@wichmann.us> On 12/25/21 13:46, Julius Hamilton wrote: > Not sure if anybody has any opinions on this, or ways they?d improve it. > It?d be cool to find a single simple, elegant way to clean, strip and > conjoin the sentences/lines in one line of code, and also not to use the > ?re? module if not necessary. This is the kind of problem regular expressions were invented for, so don't be shy about using them... If you want to get really carried away, consider the natural language toolkit (nltk): https://www.nltk.org/ From learn2program at gmail.com Sun Dec 26 13:33:14 2021 From: learn2program at gmail.com (Alan Gauld) Date: Sun, 26 Dec 2021 18:33:14 +0000 Subject: [Tutor] Break element into list while iterating In-Reply-To: References: Message-ID: <90eeacde-f765-7891-1567-4e795815f197@yahoo.co.uk> On 25/12/2021 17:11, Julius Hamilton wrote: > Hey, > > I would like to do some kind of list comprehension where elements meeting > certain conditions get split on a delimiter. They don?t become lists nested > in one element in the previous list; the new elements just get inserted > where the old one was. Not a LC as such but I did get this: >>> L = ['apple', 'pear', 'grape'] >>> def splitInListAt(L,idx): ??? return L[:idx] + [n for n in L[idx]] + L[idx+1:] >>> splitInListAt(L,0) ['a', 'p', 'p', 'l', 'e', 'pear', 'grape'] >>> splitInListAt(L,1) ['apple', 'p', 'e', 'a', 'r', 'grape'] >>> splitInListAt(L,2) ['apple', 'pear', 'g', 'r', 'a', 'p', 'e'] >>> Which is generic to any kind of sequence not just strings: >>> L = [[1,2,3],(4,5,6),{7,8,9}] >>> splitInListAt(L,0) [1, 2, 3, (4, 5, 6), {8, 9, 7}] >>> splitInListAt(L,1) [[1, 2, 3], 4, 5, 6, {8, 9, 7}] >>> splitInListAt(L,2) [[1, 2, 3], (4, 5, 6), 8, 9, 7] >>> -- 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