Re: [Tutor] Figuring out selective actions in Python
On Fri, May 04, 2018 at 03:53:41PM -0400, Daniel Bosah wrote: > Hello, > > I'm trying to figure out how to do blank in blank things. For example, if I > want to delete 5 MB ( or anything ) for every 20 MB, how would the could > look like? I'm essentially trying to do an action in one order of the > sequence out of an entire sequence. Huh? Does your question have anything to do with Python? What do you mean, "do blank in blank things"? Sounds like the old "Blankety-Blanks" game show... https://en.wikipedia.org/wiki/Blankety_Blanks_%28Australian_game_show%29#Gameplay -- Steve ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] Need help with a virtual environment mess
In a prior thread you guys helped me fix a problem with pip after I upgraded an installed version of python 3.6 on my Mint 18 system. Pip would not run in my python 3.6 virtual environment. The fix was to use synaptic to install python3-distutils. I thought everything was ok until I tried to run a old script from a different VE using python 3.5 which could not import tkinter. I have 4 versions of python on this system: system Python2 = 2.7.12 (default) system Python3 = 3.5.2 (default) a VE called env = 3.5.2 a Ve called env36 = 3.6.5 This is the error I get trying to import tkinter in env, I also get the same error if I try to import it in system python3. jfb@jims-mint18 ~ $ source /home/jfb/EVs/env/bin/activate (env) jfb@jims-mint18 ~ $ python Python 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import tkinter as tk Traceback (most recent call last): File "/usr/lib/python3.5/tkinter/__init__.py", line 36, in import _tkinter ImportError: No module named '_tkinter' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "", line 1, in File "/usr/lib/python3.5/tkinter/__init__.py", line 38, in raise ImportError(str(msg) + ', please install the python3-tk package') ImportError: No module named '_tkinter', please install the python3-tk package >>> If I go to synaptic and install the python3-tk it installs version 3.6.5 of the package and I can still not import tkinter in env with python 3.5.2, but I can in env36 with python 3.6.5. I don't know if it makes a difference but I installed python3.6 from LP-PPA-jonathonf-python-3.6/now. Thanks, Jim ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Extract main text from HTML document
On 05/05/18 18:59, Simon Connah wrote: Hi, I'm writing a very simple web scraper. It'll download a page from a website and then store the result in a database of some sort. The problem is that this will obviously include a whole heap of HTML, JavaScript and maybe even some CSS. None of which is useful to me. I was wondering if there was a way in which I could download a web page and then just extract the main body of text without all of the HTML. The title is obviously easy but the main body of text could contain all sorts of HTML and I'm interested to know how I might go about removing the bits that are not needed but still keep the meaning of the document intact. Does anyone have any suggestions on this front at all? Thanks for any help. Simon. A combination of requests http://docs.python-requests.org/en/master/ and beautiful soup https://www.crummy.com/software/BeautifulSoup/bs4/doc/ should fit the bill. Both are installable with pip and are regarded as best of breed. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Extract main text from HTML document
Thanks for the replies, everyone. Beautiful Soup looks like a good option. My primary goal is to extract the main body text, the title and the meta description from a web page and run it through one of the cloud Natural Language processing services to find out some information that I'd like to know and I'd like to do it to quite a few websites. This is all for a little project I have in mind. I'm not even sure if it'll work but it'll be fun to try. I might have to do some custom work on top of what Beautiful Soup offers though as I need to get very specific data in a certain format. On 5 May 2018 at 22:43, boB Stepp wrote: > On Sat, May 5, 2018 at 12:59 PM, Simon Connah wrote: > >> I was wondering if there was a way in which I could download a web >> page and then just extract the main body of text without all of the >> HTML. > > I do not have any experience with this, but I like to collect books. > One of them [1] says on page 245: > > "Beautiful Soup is a module for extracting information from an HTML > page (and is much better for this purpose than regular expressions)." > > I believe this topic has come up before on this list as well as the > main Python list. You may want to check it out. It can be installed > with pip. > > [1] "Automate the Boring Stuff with Python -- Practical Programming > for Total Beginners" by Al Sweigart. > > HTH! > -- > boB > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Python C extension - which method?
If I may ask, what's the difference between these two? 1) import ctypes hello = ctypes.WinDLL('hello', use_last_error=True) 2) from ctypes import cdll hello = cdll.LoadLibrary('hello.dll') Both of them can return "1980" from this: hello.c #include __declspec(dllexport) int say_something() { return 1980; } On Sun, May 6, 2018 at 8:39 AM, Brad M wrote: > Does this have any downside? I have noticed that printf("HI") in my .DLL; > doesn't really print anything. > I am on windows. > > cdll.LoadLibrary('helloworld.dll') > > > > My next question is that I need to return an array or a list of address or > int or some type of data, but > if I returned a pointer to the array of some data type, how does the > python cope with it? > > For instance, my plan is to have 3 functions: > > 1) a function that return all addresses that have a given value to python, > say, 9001 > 2) then I do something with python so that values change, the target value > is now 1009. > 3) a second function that takes all the addresses as input and then return > adddress that have values of 1009. > > 4) repeat 2 and 3 until there is only 1 or so of address which correct > values. > 5) A 3rd function that takes only 1 address and simply return the values > at that address. > > So the whole point of having 1-4 is to find the one address that contain > that values I want, and then > all I do is to call the 3rd function to find the values at that address. > > > > So basically what I need to know is if the method I am using, > cdll.LoadLibrary('helloworld.dll') > is proper or I need some other method, and then I would like to find out > how to handle the data returned. > (address) > > Thanks!!! > > > > On Sun, May 6, 2018 at 5:44 AM, Stefan Behnel wrote: > >> Hi, >> >> Brad M schrieb am 04.05.2018 um 11:30: >> > I want to create a C-based memory scanner for Python, and so far this is >> > how I do it: >> > >> > Python: >> > >> > from ctypes import cdll >> > mydll = cdll.LoadLibrary('hello.dll') >> > print(mydll.say_something()) >> > >> > and hello.dll: >> > >> > #include >> > __declspec(dllexport) int say_something() >> > { >> > return 1980; >> > } >> > >> > so the printout is "1980" >> > >> > Is this alright? >> >> >> Depends on your needs and your C/C++ knowledge. >> >> If you have a shared library that provides the ready-made functionality, >> and accessing that native code at all is more important than calling it >> very quickly (e.g. you only do a few longish-running calls into it), then >> wrapping a shared library with ctypes (or preferably cffi) is a good way >> to >> do it. >> >> Otherwise, try either a native wrapper generator like pybind11, or write >> your wrapper in Cython. >> >> Specifically, if you are not just calling into an external library 1:1, >> but >> need to do (or can benefit from doing) non-trivial operations in native >> code, definitely use Cython. >> >> http://cython.org >> >> >> > I am aware that there is another much more complicated >> > method such as this: >> > >> > https://tutorialedge.net/python/python-c-extensions-tutorial >> /#building-and-installing-our-module >> >> Well, yes, it exists, but I advise against wrapping C code manually that >> way. It's just too cumbersome and error prone. Leave it to the experts who >> have already written their tools for you. >> >> Stefan >> >> >> Disclosure: I'm a Cython core dev, so I'm biased and I absolutely know >> what >> I'm talking about. >> >> ___ >> Tutor maillist - Tutor@python.org >> To unsubscribe or change subscription options: >> https://mail.python.org/mailman/listinfo/tutor >> > > ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] ValueError: Procedure probably called with too many arguments (8 bytes in excess)
Hi all: I am experimenting with python calling some .DLL and this is my setup: scan.py memscan = ctypes.WinDLL('mahdll', use_last_error=True) print(memscan.say_something(1,2)) # So I pass to int to the DLL function. DLL: #include __declspec(dllexport) int say_something(int a, int b) { printf("Hello World"); return math(a, b); } int math(int a, int b) { return a + b; } # So I found it wont print "hello world" and it give this error: ValueError: Procedure probably called with too many arguments (8 bytes in excess) # Also, if I pass no argument to the function, it won't printf("Hello world") on the python interpreter or opens a console window to print it. So what's the right thing to do to pass arguments to a DLL function? Thanks! ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Extract main text from HTML document
Two things. The first thing is that you can download the page as a string and delete a everything between tags. Secondly It might be worth looking at Udacity cs101 as this course is all about a search engine. On Sat, 5 May 2018 at 22:27, Simon Connah wrote: > Hi, > > I'm writing a very simple web scraper. It'll download a page from a > website and then store the result in a database of some sort. The > problem is that this will obviously include a whole heap of HTML, > JavaScript and maybe even some CSS. None of which is useful to me. > > I was wondering if there was a way in which I could download a web > page and then just extract the main body of text without all of the > HTML. > > The title is obviously easy but the main body of text could contain > all sorts of HTML and I'm interested to know how I might go about > removing the bits that are not needed but still keep the meaning of > the document intact. > > Does anyone have any suggestions on this front at all? > > Thanks for any help. > > Simon. > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Python C extension - which method?
Does this have any downside? I have noticed that printf("HI") in my .DLL; doesn't really print anything. I am on windows. cdll.LoadLibrary('helloworld.dll') My next question is that I need to return an array or a list of address or int or some type of data, but if I returned a pointer to the array of some data type, how does the python cope with it? For instance, my plan is to have 3 functions: 1) a function that return all addresses that have a given value to python, say, 9001 2) then I do something with python so that values change, the target value is now 1009. 3) a second function that takes all the addresses as input and then return adddress that have values of 1009. 4) repeat 2 and 3 until there is only 1 or so of address which correct values. 5) A 3rd function that takes only 1 address and simply return the values at that address. So the whole point of having 1-4 is to find the one address that contain that values I want, and then all I do is to call the 3rd function to find the values at that address. So basically what I need to know is if the method I am using, cdll.LoadLibrary('helloworld.dll') is proper or I need some other method, and then I would like to find out how to handle the data returned. (address) Thanks!!! On Sun, May 6, 2018 at 5:44 AM, Stefan Behnel wrote: > Hi, > > Brad M schrieb am 04.05.2018 um 11:30: > > I want to create a C-based memory scanner for Python, and so far this is > > how I do it: > > > > Python: > > > > from ctypes import cdll > > mydll = cdll.LoadLibrary('hello.dll') > > print(mydll.say_something()) > > > > and hello.dll: > > > > #include > > __declspec(dllexport) int say_something() > > { > > return 1980; > > } > > > > so the printout is "1980" > > > > Is this alright? > > > Depends on your needs and your C/C++ knowledge. > > If you have a shared library that provides the ready-made functionality, > and accessing that native code at all is more important than calling it > very quickly (e.g. you only do a few longish-running calls into it), then > wrapping a shared library with ctypes (or preferably cffi) is a good way to > do it. > > Otherwise, try either a native wrapper generator like pybind11, or write > your wrapper in Cython. > > Specifically, if you are not just calling into an external library 1:1, but > need to do (or can benefit from doing) non-trivial operations in native > code, definitely use Cython. > > http://cython.org > > > > I am aware that there is another much more complicated > > method such as this: > > > > https://tutorialedge.net/python/python-c-extensions- > tutorial/#building-and-installing-our-module > > Well, yes, it exists, but I advise against wrapping C code manually that > way. It's just too cumbersome and error prone. Leave it to the experts who > have already written their tools for you. > > Stefan > > > Disclosure: I'm a Cython core dev, so I'm biased and I absolutely know what > I'm talking about. > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] passing values and C pointers
Hi all: Although I have college C++ 101 and Python 101 down my belt, I wan't taught how to read a reference manual to figure this out :( Say I have an array of values, say addresses or int produced by a c module/ c function that's in a DLL , how do I pass that array back to the python code? from ctypes import cdll mydll = cdll.LoadLibrary('mydll') a = mydll.c_get_data() or, as I have learned in C, I can use malloc to create a linked list(new beast for me) and then return the head pointer to the previous C function. But eventually I am going to pass that C pointer back to python, right? 1) how do i do that, 2) how do do work on that data on that pointer? let's say the pointer points to a linked list. Thanks all! ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Python C extension - which method?
On Sun, May 6, 2018 at 12:49 AM, Brad M wrote: > If I may ask, what's the difference between these two? > > 1) > import ctypes > hello = ctypes.WinDLL('hello', use_last_error=True) > > 2) > from ctypes import cdll > hello = cdll.LoadLibrary('hello.dll') Use ctypes.CDLL and ctypes.WinDLL instead of cdll.LoadLibrary and windll.LoadLibrary. The latter is more typing for no benefit and prevents using the constructor arguments: handle, mode (POSIX), use_errno, and use_last_error (Windows). You need the latter two options if the library requires C errno or Windows GetLastError(), in which case you should use ctypes.get_errno() or ctypes.get_last_error() to get the error values after a C function call. > Both of them can return "1980" from this: > > hello.c > > #include > > __declspec(dllexport) int say_something() > { > return 1980; > } CDLL is the cdecl calling convention, and WinDLL is stdcall. There is no difference in 64-bit Windows (x64 ABI). In 32-bit Windows (x86 ABI), cdecl has the caller clean the stack (i.e. pop arguments), and stdcall has the callee (the called function) clean the stack. cdecl allows functions with a variable number of arguments, such as the CRT printf function. In MSVC, cdecl is the default convention if you don't declare a function as __stdcall. A library can export functions with varying calling conventions, so in general you may need to mix CDLL and WinDLL. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] passing values and C pointers
On Sun, May 6, 2018 at 2:17 AM, Brad M wrote: > > Say I have an array of values, say addresses or int produced by a c module/ > c function that's in a DLL , how do I pass that array back to > the python code? C arrays are passed and returned automatically as pointers to the first element. The array length has to be passed separately, unless there's a known sentinel value. A simple pattern is to let the caller allocate the array and pass a pointer and the length. This gives the caller explicit control over the lifetime of the array, which is especially simple for ctypes since it uses reference-counted objects. Say you have a function in C such as the following: int DLLAPI get_data(int *data, size_t length) { size_t i; for (i=0, i < length; i++) { if (do_something(i, &data[i]) == -1) { return -1; /* failure */ } } return 0; /* success */ } In Python, set up and call this function as follows: import ctypes mydll = ctypes.CDLL('mydll') # setup mydll.get_data.argtypes = ( ctypes.POINTER(ctypes.c_int), # data ctypes.c_size_t) # length # call data = (ctypes.c_int * 10)() status = mydll.get_data(data, len(data)) if status == -1: raise MyDllException('get_data: ...') for i, value in enumerate(data): result = do_something_else(i, value) ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] How to separate UI code from program logic?
I was solving a programming problem in one of my books concerning the generation of a Collatz sequence (https://en.wikipedia.org/wiki/Collatz_conjecture), and started to wonder how I should separate my program's output from its logic. It seems like this should be obvious to me, but, unfortunately, it isn't. The core functions from my program are: # def collatz(integer): """Returns the Collatz sequence number corresponding to integer. integer must be > 0, or the sequence will not converge to 1.""" if integer % 2 == 0: return integer // 2 else: return 3 * integer + 1 def generate_collatz_sequence(seed): """Generates a Collatz sequence starting from seed. seed must be a positive integer, or the sequence will not coverge to 1.""" counter = 0 collatz_number = seed print("Collatz seed number: ", collatz_number) while True: counter += 1 collatz_number = collatz(collatz_number) print("Collatz number", counter, ": ", collatz_number) if collatz_number == 1: print("The Collatz sequence has once again converged to 1!") break # My understanding of best practice here is that I should not have any print() calls inside my generate_collatz_sequence() function. I _could_ store the generated sequence in a list and return it, but that does not seem like good use of RAM if some user-supplied seed value led to kazillions of Collatz sequence numbers being generated. As it stands there will be no theoretical RAM issues as the numbers are being generated and then outputted one at a time. OTOH, if I created some kind of display messages function, I don't see how I have gained anything as these calls to a display message function would just be taking the place of the print() calls. The end result would be the same -- display code interleaved with program logic code. What am I being dense about here? -- boB ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Need help with a virtual environment mess
On Sun, May 6, 2018 at 11:05 AM, Jim wrote: > In a prior thread you guys helped me fix a problem with pip after I upgraded > an installed version of python 3.6 on my Mint 18 system. Pip would not run > in my python 3.6 virtual environment. The fix was to use synaptic to install > python3-distutils. I thought everything was ok until I tried to run a old > script from a different VE using python 3.5 which could not import tkinter. > > I have 4 versions of python on this system: > system Python2 = 2.7.12 (default) > system Python3 = 3.5.2 (default) > a VE called env = 3.5.2 > a Ve called env36 = 3.6.5 > > This is the error I get trying to import tkinter in env, I also get the same > error if I try to import it in system python3. > > jfb@jims-mint18 ~ $ source /home/jfb/EVs/env/bin/activate > (env) jfb@jims-mint18 ~ $ python > Python 3.5.2 (default, Nov 23 2017, 16:37:01) > [GCC 5.4.0 20160609] on linux > Type "help", "copyright", "credits" or "license" for more information. import tkinter as tk > Traceback (most recent call last): > File "/usr/lib/python3.5/tkinter/__init__.py", line 36, in > import _tkinter > ImportError: No module named '_tkinter' > > During handling of the above exception, another exception occurred: > > Traceback (most recent call last): > File "", line 1, in > File "/usr/lib/python3.5/tkinter/__init__.py", line 38, in > raise ImportError(str(msg) + ', please install the python3-tk package') > ImportError: No module named '_tkinter', please install the python3-tk > package > > If I go to synaptic and install the python3-tk it installs version 3.6.5 of > the package and I can still not import tkinter in env with python 3.5.2, but > I can in env36 with python 3.6.5. > As I have not yet tried to play around with virtual environments, I may be about to do more harm than help, but I will plow ahead anyway! ~(:>)) My primitive understanding of installing Python versions that are different from the system Python version into a virtual environment, is that you have to install all dependencies you need from within that virtual environment you created. If I am correct about this then your error messages suggest you need to install the tkinter stuff from within that virtual environment using that virtual environment's pip. Hopefully I am too far off from the truth here, but in any event, I hope this helps you in your problem! -- boB ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] How to separate UI code from program logic?
On 6 May 2018, at 23:00, boB Stepp wrote: >I was solving a programming problem in one of my books concerning the >generation of a Collatz sequence >(https://en.wikipedia.org/wiki/Collatz_conjecture), and started to >wonder how I should separate my program's output from its logic. It >seems like this should be obvious to me, but, unfortunately, it isn't. >The core functions from my program are: ># >def collatz(integer): > """Returns the Collatz sequence number corresponding to integer. integer > must be > 0, or the sequence will not converge to 1.""" > if integer % 2 == 0: > return integer // 2 > else: > return 3 * integer + 1 >def generate_collatz_sequence(seed): > """Generates a Collatz sequence starting from seed. > seed must be a positive integer, or the sequence will not > coverge to 1.""" > counter = 0 > collatz_number = seed > print("Collatz seed number: ", collatz_number) > while True: > counter += 1 > collatz_number = collatz(collatz_number) > print("Collatz number", counter, ": ", collatz_number) > if collatz_number == 1: > print("The Collatz sequence has once again converged to 1!") > break ># >My understanding of best practice here is that I should not have any >print() calls inside my generate_collatz_sequence() function. I >_could_ store the generated sequence in a list and return it, but that >does not seem like good use of RAM if some user-supplied seed value >led to kazillions of Collatz sequence numbers being generated. The clue is in that last word. Write a generator function that yields the intermediate results. Alan g. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] How to separate UI code from program logic?
On Sun, May 6, 2018 at 5:05 PM, Alan Gauld wrote: > On 6 May 2018, at 23:00, boB Stepp wrote: >>My understanding of best practice here is that I should not have any >>print() calls inside my generate_collatz_sequence() function. I >>_could_ store the generated sequence in a list and return it, but that >>does not seem like good use of RAM if some user-supplied seed value >>led to kazillions of Collatz sequence numbers being generated. > > The clue is in that last word. > Write a generator function that yields the intermediate results. Aha! Of course the book I am poking around in does not even cover generator functions ... But I will plow ahead as usual and implement your suggestion. Thanks, Alan! -- boB ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] How to separate UI code from program logic?
On 06/05/18 22:59, boB Stepp wrote: I was solving a programming problem in one of my books concerning the generation of a Collatz sequence (https://en.wikipedia.org/wiki/Collatz_conjecture), and started to wonder how I should separate my program's output from its logic. It seems like this should be obvious to me, but, unfortunately, it isn't. The core functions from my program are: # def collatz(integer): """Returns the Collatz sequence number corresponding to integer. integer must be > 0, or the sequence will not converge to 1.""" if integer % 2 == 0: return integer // 2 else: return 3 * integer + 1 def generate_collatz_sequence(seed): """Generates a Collatz sequence starting from seed. seed must be a positive integer, or the sequence will not coverge to 1.""" counter = 0 collatz_number = seed print("Collatz seed number: ", collatz_number) while True: counter += 1 collatz_number = collatz(collatz_number) print("Collatz number", counter, ": ", collatz_number) if collatz_number == 1: print("The Collatz sequence has once again converged to 1!") break # My understanding of best practice here is that I should not have any print() calls inside my generate_collatz_sequence() function. I _could_ store the generated sequence in a list and return it, but that does not seem like good use of RAM if some user-supplied seed value led to kazillions of Collatz sequence numbers being generated. As it stands there will be no theoretical RAM issues as the numbers are being generated and then outputted one at a time. OTOH, if I created some kind of display messages function, I don't see how I have gained anything as these calls to a display message function would just be taking the place of the print() calls. The end result would be the same -- display code interleaved with program logic code. What am I being dense about here? You're not being dense, you're just trying to wrap your head around a difficult concept. I suggest you read up on the model view controller pattern. First hit on google https://www.tomdalling.com/blog/software-design/model-view-controller-explained/ seems as good as any. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] How to separate UI code from program logic?
On Sun, May 6, 2018 at 5:05 PM, Alan Gauld wrote: > On 6 May 2018, at 23:00, boB Stepp wrote: >>My understanding of best practice here is that I should not have any >>print() calls inside my generate_collatz_sequence() function. I >>_could_ store the generated sequence in a list and return it, but that >>does not seem like good use of RAM if some user-supplied seed value >>led to kazillions of Collatz sequence numbers being generated. > > The clue is in that last word. > Write a generator function that yields the intermediate results. Ok. I've been reading up on generators and playing around with them today. And then, per Alan's hint, tried to separate all display code from my program logic code. What was originally meant to be a throw-away effort, has somehow mushroomed into something I hope resembles a finished product. The results I came up with are as follows: # #!/usr/bin/env python3 """This program will generate a Collatz sequence from a user-supplied positive integer. According to Wikipedia (https://en.wikipedia.org/wiki/Collatz_conjecture): "The Collatz conjecture is a conjecture in mathematics that concerns a sequence defined as follows: start with any positive integer n. Then each term is obtained from the previous term as follows: if the previous term is even, the next term is one half the previous term. Otherwise, the next term is 3 times the previous term plus 1. The conjecture is that no matter what value of n, the sequence will always reach 1." """ def get_positive_integer(): """Get a positive integer from the user.""" while True: try: integer = int(input("Please enter a positive integer: ")) if integer > 0: return integer else: print("That was not a positive integer!") continue except ValueError: print("That was not an integer!") continue def display_collatz_numbers(seed, collatz_generator): """Display a Collatz sequence, one value per line, given the seed (Which will be the first number in the sequence.) and a generator which will yield the Collatz sequence.""" print("\nThe seed number: ", seed) for sequence_index, number in enumerate(collatz_generator): print("Sequence number", sequence_index + 1, ": ", number) def ask_to_continue(): choice = input("Do you wish to generate another Collatz sequence?").lower() return choice.startswith('y') def get_collatz_number(integer): """Returns the Collatz sequence number corresponding to integer. integer must be > 0, or the sequence will not converge to 1.""" if integer % 2 == 0: return integer // 2 else: return 3 * integer + 1 def generate_collatz_sequence(seed): """Creates a generator, which will yield a Collatz sequence starting from seed. seed must be a positive integer, or the sequence will not converge to 1.""" collatz_number = seed while True: collatz_number = get_collatz_number(collatz_number) yield collatz_number if collatz_number == 1: return def main(): """Run program.""" while True: seed = get_positive_integer() display_collatz_numbers(seed, generate_collatz_sequence(seed)) if ask_to_continue(): continue else: break if __name__ == '__main__': main() # Questions and comments: 1) I am open to a general critique on making this code better. 2) I spent a lot of effort trying to come up with a way to combine the two functions, get_collatz_number() and generate_collatz_sequence(), into something both more compact and more readable, but I was unsuccessful. I suspect there is a better way. Is there? And how would I do it? 3) Is this the correct way to separate display code from program logic code? Is there a better way to do this? 4) I think this is the first time I've actually tried to implement a generator function. Did I do a good Pythonic implementation of this? As always, thanks! -- boB ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor