Re: Running issues

2024-04-05 Thread MRAB via Python-list

On 2024-04-05 22:32, shannon makasale via Python-list wrote:

Hi there,
My name is Shannon. I installed Python 3.12 on my laptop a couple months ago, 
but realised my school requires me to use 3.11.1.

I uninstalled 3.12 and installed 3.11.1.

Unfortunately, I am unable to run python now. It keeps asking to be modified, 
repaired or uninstalled.

Do you have any suggestions on how to fix this?

Any help you can offer is greatly appreciated. Thank you for your time.


Hope to hear from you soon.


That’s the installer. All it does is install the software.

There isn’t an IDE as such, although there is IDLE, which you should be 
able to find on the Start Menu under Python (assuming you’re using Windows).


There are a number of 3rd-party editors available that you can use when 
working with Python, or you can use Visual Studio Code from Microsoft.


--
https://mail.python.org/mailman/listinfo/python-list


Re: Running issues

2024-04-05 Thread o1bigtenor via Python-list
On Fri, Apr 5, 2024 at 4:40 PM shannon makasale via Python-list <
python-list@python.org> wrote:

> Hi there,
> My name is Shannon. I installed Python 3.12 on my laptop a couple months
> ago, but realised my school requires me to use 3.11.1.
>
> I uninstalled 3.12 and installed 3.11.1.
>
> Unfortunately, I am unable to run python now. It keeps asking to be
> modified, repaired or uninstalled.
>
> Do you have any suggestions on how to fix this?
>

Sorry - - - but you just haven't given enough information so that you can
be helped.

What OS are you using?

How did you install python 3.12?

How did you un-install it?

How did you install python3.11.1?

With answers to all of the questions - - - it would be much easier to
answer you?


HTH
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Running issues

2024-04-05 Thread Thomas Passin via Python-list

On 4/5/2024 5:32 PM, shannon makasale via Python-list wrote:

Hi there,
My name is Shannon. I installed Python 3.12 on my laptop a couple months ago, 
but realised my school requires me to use 3.11.1.

I uninstalled 3.12 and installed 3.11.1.

Unfortunately, I am unable to run python now. It keeps asking to be modified, 
repaired or uninstalled.

Do you have any suggestions on how to fix this?


It would be helpful to know how you uninstalled it.

The message you saw looks like it comes from the installer rather than 
from the Python interpreter.  Try invoking Python with "py" (assuming 
you are using Windows). That is the standard Python launcher that is 
installed by the installer from python.org.


For the future, know that you can have several different versions of 
Python installed at the same time.  On Windows, you can launch a 
specific version using the launcher:


py -3.11
py -3.12

And so on.

On Linux you should use the full name, such as

python3.11
python3.12

etc., depending on which versions have been installed.  "python3" will 
get you the version used by the system, which may not be the one you 
want to use.


To install Python packages with pip, make sure you specify which version 
of pip to use, like this:


py -3.11 -m pip (Windows)

or

python3.11 -m pip (Linux)

Otherwise you make accidentally install the package into the wrong 
Python installation.



Any help you can offer is greatly appreciated. Thank you for your time.


Hope to hear from you soon.



Shannon Makasale


--
https://mail.python.org/mailman/listinfo/python-list


Running issues

2024-04-05 Thread shannon makasale via Python-list
Hi there,
My name is Shannon. I installed Python 3.12 on my laptop a couple months ago, 
but realised my school requires me to use 3.11.1.

I uninstalled 3.12 and installed 3.11.1.

Unfortunately, I am unable to run python now. It keeps asking to be modified, 
repaired or uninstalled.

Do you have any suggestions on how to fix this?

Any help you can offer is greatly appreciated. Thank you for your time.


Hope to hear from you soon.



Shannon Makasale
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: A technique from a chatbot

2024-04-05 Thread Mark Bourne via Python-list

Stefan Ram wrote:

Mark Bourne  wrote or quoted:

I don't think there's a tuple being created.  If you mean:
 ( word for word in list_ if word[ 0 ]== 'e' )
...that's not creating a tuple.  It's a generator expression, which
generates the next value each time it's called for.  If you only ever
ask for the first item, it only generates that one.


   Yes, that's also how I understand it!

   In the meantime, I wrote code for a microbenchmark, shown below.

   This code, when executed on my computer, shows that the
   next+generator approach is a bit faster when compared with
   the procedural break approach. But when the order of the two
   approaches is being swapped in the loop, then it is shown to
   be a bit slower. So let's say, it takes about the same time.


There could be some caching going on, meaning whichever is done second 
comes out a bit faster.



   However, I also tested code with an early return (not shown below),
   and this was shown to be faster than both code using break and
   code using next+generator by a factor of about 1.6, even though
   the code with return has the "function call overhead"!


To be honest, that's how I'd probably write it - not because of any 
thought that it might be faster, but just that's it's clearer.  And if 
there's a `do_something_else()` that needs to be called regardless of 
the whether a word was found, split it into two functions:

```
def first_word_beginning_with_e(target, wordlist):
for w in wordlist:
if w.startswith(target):
return w
return ''

def find_word_and_do_something_else(target, wordlist):
result = first_word_beginning_with_e(target, wordlist)
do_something_else()
return result
```


   But please be aware that such results depend on the implementation
   and version of the Python implementation being used for the benchmark
   and also of the details of how exactly the benchmark is written.

import random
import string
import timeit

print( 'The following loop may need a few seconds or minutes, '
'so please bear with me.' )

time_using_break = 0
time_using_next = 0

for repetition in range( 100 ):
 for i in range( 100 ): # Yes, this nesting is redundant!

 list_ = \
 [ ''.join \
   ( random.choices \
 ( string.ascii_lowercase, k=random.randint( 1, 30 )))
   for i in range( random.randint( 0, 50 ))]

 start_time = timeit.default_timer()
 for word in list_:
 if word[ 0 ]== 'e':
 word_using_break = word
 break
 else:
 word_using_break = ''
 time_using_break += timeit.default_timer() - start_time

 start_time = timeit.default_timer()
 word_using_next = \
 next( ( word for word in list_ if word[ 0 ]== 'e' ), '' )
 time_using_next += timeit.default_timer() - start_time

 if word_using_next != word_using_break:
 raise Exception( 'word_using_next != word_using_break' )

print( f'{time_using_break = }' )
print( f'{time_using_next = }' )
print( f'{time_using_next / time_using_break = }' )


--
https://mail.python.org/mailman/listinfo/python-list


Re: A technique from a chatbot

2024-04-05 Thread Mark Bourne via Python-list

avi.e.gr...@gmail.com wrote:

That is an excellent point, Mark. Some of the proposed variants to the 
requested problem, including mine, do indeed find all instances only to return 
the first. This can use additional time and space but when done, some of the 
overhead is also gone. What I mean is that a generator you create and invoke 
once, generally sits around indefinitely in your session unless it leaves your 
current range or something. It does only a part of the work and must remain 
suspended and ready to be called again to do more.


It goes out of scope at the end of the function.  Unless you return it 
or store a reference to it elsewhere, it will then be deleted.


Or in this case, since the `first_word_beginning_with_e` function 
doesn't even have a local reference to the generator (it is just created 
and immediately passed as an argument to `next`), it goes out of scope 
once the `next` function returns.



If you create a generator inside a function and the function returns, 
presumably it can be garbage-collected.


Exactly.  It probably doesn't even need to wait for garbage collection - 
once the reference count is zero, it can be destroyed.



But if it is in the main body, I have to wonder what happen.


If you mean in the top-level module scope outside of any 
function/method, then it would remain in memory until the process exits.



There seem to be several related scenarios to consider.

- You may want to find, in our example, a first instance. Right afterwards, you 
want the generator to disassemble anything in use.
- You may want the generator to stick around and later be able to return the 
next instance. The generator can only really go away when another call has been 
made after the last available instance and it cannot look for more beyond some 
end.
- Finally, you can call a generator with the goal of getting all instances such 
as by asking it to populate a list. In such a case, you may not necessarily 
want or need to use a generator expression and can use something 
straightforward and possible cheaper.


Yes, so you create and assign it at an appropriate scope.  In the 
example here, it's just passed to `next` and then destroyed.  Passing a 
generator to the `list` constructor (or the `tuple` constructor in my 
"FWIW") would behave similarly - you'd get the final list/tuple back, 
but the generator would be destroyed once that call is done.  If you 
assigned it to a function-local variable, it would exist until the end 
of that function.



What confuses the issue, for me, is that you can make fairly complex 
calculations in python using various forms of generators that implement a sort 
of just-in-time approach as generators call other generators which call yet 
others and so on.


Yes, you can.  It can be quite useful when used appropriately.


Imagine having folders full of files that each contain a data structure such as 
a dictionary or set and writing functionality that searches for the first match 
for a key in any of the dictionaries (or sets or whatever) along the way? Now 
imagine that dictionary items can be a key value pair that can include the 
value being a deeper dictionary, perhaps down multiple levels.

You could get one generator that generates folder names or opens them and 
another that generates file names and reads in the data structure such as a 
dictionary and yet another that searches each dictionary and also any 
internally embedded dictionaries by calling another instance of the same 
generator as much as needed.


You probably could do that.  Personally, I probably wouldn't use 
generators for that, or at least not custom ones - if you're talking 
about iterating over directories and files on disk, I'd probably just 
use `os.walk` (which probably is a generator) and iterate over that, 
opening each file and doing whatever you want with the contents.



You can see how this creates and often consumes generators along the way as 
needed and in a sense does the minimum amount of work needed to find a first 
instance. But what might it leave open and taking up resources if not finished 
in a way that dismantles it?


You'd need to make sure any files are closed (`with open(...)` helps 
with that).  If you're opening files within a generator, I'm pretty sure 
you can do something like:

```
def iter_files(directory):
for filename in directory:
with open(filename) as f:
yield f
```

Then the file will be closed when the iterator leaves the `with` block 
and moved on to the next item (presumably there's some mechanism for the 
context manager's `__exit__` to be called if the generator is destroyed 
without having iterated over the items - the whole point of using `with` 
is that `__exit__` is guaranteed to be called whatever happens).


Other than that, the generators themselves would be destroyed once they 
go out of scope.  If there are no references to a generator left, 
nothing is going to be able to call `next` (nor