From jonathan at torres-herrera.com  Mon May  2 18:35:04 2022
From: jonathan at torres-herrera.com (Jonathan Torres-Herrera)
Date: Mon, 2 May 2022 16:35:04 -0600
Subject: [Tutor] Help with Excel Workbook and URLs, in Python Charm
Message-ID: <CAEuSK6hn0xDhyiNsRaPVdBnnq3t7PyovjYLZWyP4_dJ4AVa3EQ@mail.gmail.com>

Hi Tutors,

Here's my dilemma.

I have a couple of excel workbooks, let's call them workbook-A and
workbook-B.

Workbook-A holds a unique identifier, which happens to be a list of email
addresses.

On workbook-B, I have a list of URLs, each URL opens a unique online excel
workbook with information that includes emails as well.

Here is what I'm asking for help with.

What would be the best approach to have python take each email address on
workbook-A, and look it up against the list of URLs in workbook-B, and then
return which URL it found it on in workbook-A (on a blank column)?

From alan.gauld at yahoo.co.uk  Tue May  3 04:36:41 2022
From: alan.gauld at yahoo.co.uk (Alan Gauld)
Date: Tue, 3 May 2022 09:36:41 +0100
Subject: [Tutor] Help with Excel Workbook and URLs, in Python Charm
In-Reply-To: <CAEuSK6hn0xDhyiNsRaPVdBnnq3t7PyovjYLZWyP4_dJ4AVa3EQ@mail.gmail.com>
References: <CAEuSK6hn0xDhyiNsRaPVdBnnq3t7PyovjYLZWyP4_dJ4AVa3EQ@mail.gmail.com>
Message-ID: <t4qpiq$4mg$1@ciao.gmane.io>

On 02/05/2022 23:35, Jonathan Torres-Herrera wrote:

> Workbook-A holds a unique identifier, which happens to be a list of email
> addresses.
> 
> On workbook-B, I have a list of URLs, each URL opens a unique online excel
> workbook with information that includes emails as well.
> 
> Here is what I'm asking for help with.
> 
> What would be the best approach to have python take each email address on
> workbook-A, and look it up against the list of URLs in workbook-B, and then
> return which URL it found it on in workbook-A (on a blank column)?

Since network access wull be slow you shoulsd aim to minimise that,
so I'd traverse the list of URLs and build up a local cacche of
the email addresses. Perjhaps a dictionary keyed by email address?

You can then loop over the other spreadsheet and add the corresponding
URL. It's not clear from your description if a single email address
can occur on more than one URL or vice versa. Nor whether thre is a
one to one correspondence - could there be addresses with
no corresponding email or emails with no corresponding URL?
You will need to decide what to do in those cases.

As for reading Excel, there are a couple of third party libraries
that make that easier. Google "excel python reader module"

-- 
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 leamhall at gmail.com  Mon May  9 08:01:59 2022
From: leamhall at gmail.com (Leam Hall)
Date: Mon, 9 May 2022 07:01:59 -0500
Subject: [Tutor] Feedback on coding style
Message-ID: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>

Hey all,

I'm looking for general Python code critique, feel free to share snarky comments.  :)  The parameters are:
  1. Code to Python 3.6 or so.
  2. Use only the standard library or locally created modules.
  3. Keep it clean and simple so new Pythonistas can understand and contribute.

Let me know how to make this better.

Leam
-- 
Automation Engineer        (reuel.net/resume)
Scribe: The Domici War     (domiciwar.net)
General Ne'er-do-well      (github.com/LeamHall)

###

#!/usr/bin/env python3

# name:     bp_tracker.py
# version:  0.0.1
# date:     20220509
# author:   Leam Hall
# desc:     Track and report on blood pressure numbers.

# Notes:
#  Datafile expects three ints and one float, in order.

# TODO
#   Add statistical analysis for standard deviation.
#   Report based on time of day (early, midmorning, afternoon, evening)
#   (?) Add current distance from goal?

import argparse
from datetime import datetime
import os.path

def array_from_file(report_file):
   data = []
   with open(report_file, 'r') as file:
     for line in file:
       line.strip()
       datum = line.split()
       if len(datum) == 4:
         data.append(datum)
       else:
         continue
   return data
  
def report(report_data):
   highest_systolic  = 0
   highest_diastolic = 0
   highest_pulse     = 0
   latest            = -1.0
   for datum in report_data:
     systolic  = int(datum[0])
     diastolic = int(datum[1])
     pulse     = int(datum[2])
     date      = float(datum[3])
     if systolic > highest_systolic:
       highest_systolic = systolic
       highest_systolic_event = datum
     if diastolic > highest_diastolic:
       highest_diastolic = diastolic
       highest_diastolic_event = datum
     if pulse > highest_pulse:
       highest_pulse = pulse
       highest_pulse_event = datum
     if date > latest:
       latest_record = datum

   print("Highest Systolic: {}/{} {} {}".format(*highest_systolic_event))
   print("Highest Diastolic: {}/{} {} {}".format(*highest_diastolic_event))
   print("Highest Pulse: {}/{} {} {}".format(*highest_pulse_event))
   print("Latest Record: {}/{} {} {}".format(*latest_record))

def result_string(report_list):
   return "{} {} {} {}".format(*report_list)

report_file = "bp_numbers.txt"

parser = argparse.ArgumentParser()
parser.add_argument("-a", "--add", nargs=3,
   help = "Add in the order of systolic, diastolic, pulse")
parser.add_argument("-f", "--file", help = "Report file")
args    = parser.parse_args()

if args.file:
   report_file = args.file

if args.add:
   # This format allows sequencing now and parsing later.
   timestamp   = datetime.now().strftime("%Y%m%d.%H%M")
   this_report = args.add
   this_report.append(timestamp)
   with open(report_file, 'a') as file:
     file.write(result_string(this_report) + "\n")
else:
   # Default behavior is to report.
   if os.path.exists(report_file):
     try:
       report_data = array_from_file(report_file)
       report(report_data)
     except:
       print("Error processing report data")
   else:
     print("Cannot find ", report_file)

###

From wlfraed at ix.netcom.com  Mon May  9 11:24:24 2022
From: wlfraed at ix.netcom.com (Dennis Lee Bieber)
Date: Mon, 09 May 2022 11:24:24 -0400
Subject: [Tutor] Feedback on coding style
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
Message-ID: <hd9i7hd4khv8eb0i0to3pj4bnt04m9bict@4ax.com>

On Mon, 9 May 2022 07:01:59 -0500, Leam Hall <leamhall at gmail.com> declaimed
the following:

>Hey all,
>
>I'm looking for general Python code critique, feel free to share snarky comments.  :)  The parameters are:
>  1. Code to Python 3.6 or so.

	First thing... Ditch 3.6. End-of-Life for 3.6 was back in December. 3.7
still has a year before it goes out of support.

	Second -- make it easy on us and don't put your code AFTER your
signature block. Decent news and email clients are designed to NOT QUOTE
stuff after the "-- " signature marker. I had to manually cut&paste...

>
>#!/usr/bin/env python3
>
># name:     bp_tracker.py
># version:  0.0.1
># date:     20220509
># author:   Leam Hall
># desc:     Track and report on blood pressure numbers.
>
># Notes:
>#  Datafile expects three ints and one float, in order.
>
># TODO
>#   Add statistical analysis for standard deviation.
>#   Report based on time of day (early, midmorning, afternoon, evening)
>#   (?) Add current distance from goal?

	I believe common practice is to use a "doc string" for this type of
information. (Unless you have some system in place that parses and updates
comments of specific forms).

#!whatever
"""
	name:			bp_tracker.py
	version:			0.0.1
	date:			20220509

etc.
"""


>def array_from_file(report_file):


	From my viewpoint, "report_file" is misleading -- it does not appear to
be a "report" but rather an accumulating datastore/database or "record of
readings". Same for the use of "report_data" -- again it does not seem to
be a "report" (to me, a report is a formatted output document meant for
human reading, and is not used for updates or input).

	Also, I'd suggest using the standard CSV module (you could set up a
dialect using tab-separation if comma-separation seems "ugly"). This would
simplify some of the parsing code you are doing. cf:
https://docs.python.org/3.7/library/csv.html (normally Google reports the
most recent version, I decided to change the drop-down to the oldest still
supported version).

	In truth though -- presuming "report_file" truly is the data store, and
is only manipulated using this program (ie: no using a text editor to make
changes) -- I'd bypass the CSV module and go directly to the standard
library sqlite3 module. That removes the need to read/parse the
"report_file" into an array on start-up, and writing it back out on exit
should your "add" command option be invoked.

	You'd want to add a command line option to initialize a database file
(create table definition). SQLite3 is rather flexible in data input --
define the columns as numeric and so long as the string representation of
the data is "numeric" it will be taken and used as such.

	All the stuff for min/max should be possible using some set of SQL
SELECT statement (the min/max is no problem, but it may take a two-step
select to get the whole record with date: off the cuff...

select * from bp
where systolic = (select max(systolic) from bp);

NOTE: this could return multiple matches if there are ties for
max(systolic)	)

	Adding a new entry becomes a simple INSERT statement.

<SNIP REST OF CODE>

	Criteria #2 is met fully -- all standard library modules

	Criteria #1 -- well, as advised version 3.6 should be considered dead,
3.7 is the minimum "live" version (I'll probably be on 3.8 until it dies in
two years).

	Criteria #3? SQLite3 may be pushing it as it requires learning the
basics of SQL access to relational databases -- but for your example
program it looks like only one relation is required (I use relation/tuple
in the meaning applied by relational theory -- all data in a tuple is
related to the primary key. NOT the common understanding of SQL JOINs and
foreign keys). So, with one relation, they don't have to learn database
normalization, primary/foreign keys, etc. immediately. Such can be
postponed until a situation arises using multiple interlinked relations.

	OTOH: SQLite3 obviates the need for logic to load/parse the entire
dataset on entry, and format/save the dataset on exit, reducing the chances
for dataloss (if the computer crashes in the middle of overwriting the
dataset file) or having to code something like "save to temporary file,
rename original to backup, rename temporary as original, delete backup"
along with start-up logic to test for the presence of a backup or the
temporary file during start-up (both indicate something died and recovery
efforts need to be performed).

	Using CSV module (which will format/parse the fields) still leaves one
with having to convert text data on input to suitable numeric types (as
mentioned SQLite3 is forgiving -- if the data looks like a number and the
field is numeric, interpret it as a number; otherwise store it as-is) along
with slowly building up lists ("array" is a module in the standard library,
and has different behavior from Python lists).



-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
	wlfraed at ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/


From wlfraed at ix.netcom.com  Mon May  9 14:52:06 2022
From: wlfraed at ix.netcom.com (Dennis Lee Bieber)
Date: Mon, 09 May 2022 14:52:06 -0400
Subject: [Tutor] Feedback on coding style
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
 <hd9i7hd4khv8eb0i0to3pj4bnt04m9bict@4ax.com>
Message-ID: <s4ni7hlt8aq78l8im4qf2tt5raccha38a4@4ax.com>


	o/~	Talking to myself in public	o/~

On Mon, 09 May 2022 11:24:24 -0400, Dennis Lee Bieber
<wlfraed at ix.netcom.com> declaimed the following:

	For some reason I felt challenged, and spent three hours to produce
this variation... WARNING: news client may be adding undesirable line
wrapping -- and should be viewed with fixed width font!

NOTE: I should have created a helper function to do the formatting rather
than repeating all those print blocks.

	I'm actually surprised the DML_MAX_* statements worked first time.
Whereas I must have spent over half an hour tweaking the report format.

-=-=-
#!/usr/bin/env python3
"""
    name:       bp_tracker.py
    version:    0.0.1-a
    date:       20220509
    author:     Leam Hall
    desc:       Track and report on blood pressure numbers

    revisions:
        20220509    Dennis Lee Bieber
        rewritten as an exercise to use SQLite3 for the
        data store

    TODO:
        ADD TRY/EXCEPT BLOCKS TO HANDLE ERRORS IN DATABASE OPERATIONS
        
    TODO:
        add SQLite3 aggregate function for std. deviations

    TODO:
        Add report options for time-of-day
        (early, mid-morning, afternoon, evening)

    TODO:
        Add current distance from goal (requires input of target)

"""

import argparse
import datetime
import os.path as op
import sqlite3 as db

#TIMESTAMP is the name used by the Python API for
#conversion as SQLite3 does not have a native
#date/datetime data type
#I always create an integer primary key, even if not
#needed by the application
DDL = """CREATE TABLE IF NOT EXISTS reading
    (
        ID              INTEGER PRIMARY KEY,
        reading_date    TIMESTAMP NOT NULL,
        systolic        INTEGER NOT NULL,
        diastolic       INTEGER NOT NULL,
        pulse           INTEGER NOT NULL
    )
"""

DML_ADD = """INSERT INTO reading
        (reading_date, systolic, diastolic, pulse)
    VALUES (?, ?, ?, ?)
"""

DML_MAX_SYSTOLIC = """ SELECT
        r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
    FROM reading AS r
    INNER JOIN (SELECT max(m.systolic) AS mx
                FROM reading AS m)
    ON r.systolic = mx
    ORDER BY r.reading_date
"""

DML_MAX_DIASTOLIC = """ SELECT
        r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
    FROM reading AS r
    INNER JOIN (SELECT max(m.diastolic) AS mx
                FROM reading AS m)
    ON r.diastolic = mx
    ORDER BY r.reading_date
"""

DML_MAX_PULSE = """ SELECT
        r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
    FROM reading AS r
    INNER JOIN (SELECT max(m.pulse) AS mx
                FROM reading AS m)
    ON r.pulse = mx
    ORDER BY r.reading_date
"""

DML_MAX_DATE = """ SELECT
        r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
    FROM reading AS r
    INNER JOIN (SELECT max(m.reading_date) AS mx
                FROM reading AS m)
    ON r.reading_date = mx
"""

DML_SUMMARY = """ SELECT
    count(*) AS Count,
    max(systolic) AS Max_Systolic,
    min(systolic) AS Min_Systolic,
    avg(systolic) AS Avg_Systolic,
    max(diastolic) AS Max_Diastolic,
    min(diastolic) AS min_Diastolic,
    avg(diastolic) AS Avg_Diastolic,
    max(pulse) AS Max_Pulse,
    min(pulse) AS Min_Pulse,
    avg(pulse) AS Avg_Pulse
    FROM reading
"""

DEFAULT_DATABASE = "bp_numbers.sqlite"

def open_database(db_name):
    """
        Opens the specified database file.write
        Creates table schema (use of IF NOT EXISTS
        should mean this is safe for pre-existing
        database, yet will initialize a new database.
        CAVEAT: it also means if some random database
        file is provided a new table will be created
        within that database!)
    """
    #specify that datatypes should be parsed -- used to
    #associate datetime converters
    con = db.connect(db_name, detect_types=db.PARSE_DECLTYPES)
    cur = con.cursor()
    cur.execute(DDL)
    con.commit()
    cur.close()
    return con

def add_reading(connection, datestamp, systolic, diastolic, pulse):
    """
        Using the provided database connection, adds a new
        record with provided datestamp, systolic, diastolic, pulse
    """
    cur = connection.cursor()
    cur.execute(DML_ADD, (datestamp, systolic, diastolic, pulse))
    connection.commit()
    cur.close()

def generate_report(connection):
    """
        Produce a summary report showing the record(s) with maximum
        systolic, diastolic, pulse, datestamp.
        Also list overall maximum and minimum, means
        (SQLite3 does not have a built-in standard deviation function;
        one can be defined in Python and registered to the SQLite3
        API so it can be called in SQL statements -- this is left
        for the future)
    """
    cur = connection.cursor()
    #obtain summary max/min values
    cur.execute(DML_SUMMARY)
    summary = cur.fetchone()    #there is only one record
    #should use a dictionary cursor for this, so later formatting
    #can reference by column name, rather than having to know
    #index
    
    #collect the full records for each maximum category
    cur.execute(DML_MAX_SYSTOLIC)
    systolic = cur.fetchall()
    cur.execute(DML_MAX_DIASTOLIC)
    diastolic = cur.fetchall()
    cur.execute(DML_MAX_PULSE)
    pulse = cur.fetchall()
    cur.execute(DML_MAX_DATE)
    date = cur.fetchall()[0]

    print("\n\n\n%s\n" % "*************************************")
    print("\tSummary for %s readings\n" % summary[0])
    print("          %-9s   %-9s   %-9s" %
          ("Systolic", "Diastolic", "  Pulse"))
    print("          %-9s   %-9s   %-9s" %
          ("=========", "=========", "========="))
    print("%-9s %9s   %9s   %9s" %
          ("MAX", summary[1], summary[4], summary[7]))
    print("%-9s %9s   %9s   %9s" %
          ("MIN", summary[2], summary[5], summary[8]))
    print("%-9s %9.9s   %9.9s   %9.9s" %
          ("AVERAGE", summary[3], summary[6], summary[9]))

    print("\n\t******\n")

    print("\tMaximum %s event(s)" % "SYSTOLIC")
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("   ID", "        Datestamp", "Systolic", "Diastolic", "
Pulse"))
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("=========", "=============================",
           "=========", "=========", "========="))
    for record in systolic:
        print("%9s   %-30s   %9s   %9s   %9s" %
              (record[0], record[1], record[2], record[3], record[4]))
    print()

    print("\tMaximum %s event(s)" % "DIASTOLIC")
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("   ID", "        Datestamp", "Systolic", "Diastolic", "
Pulse"))
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("=========", "=============================",
           "=========", "=========", "========="))
    for record in diastolic:
        print("%9s   %-30s   %9s   %9s   %9s" %
              (record[0], record[1], record[2], record[3], record[4]))
    print()

    print("\tMaximum %s event(s)" % "PULSE")
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("   ID", "        Datestamp", "Systolic", "Diastolic", "
Pulse"))
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("=========", "=============================",
           "=========", "=========", "========="))
    for record in pulse:
        print("%9s   %-30s   %9s   %9s   %9s" %
              (record[0], record[1], record[2], record[3], record[4]))
    print()

    print("\tMaximum %s event" % "DATESTAMP")
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("   ID", "        Datestamp", "Systolic", "Diastolic", "
Pulse"))
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("=========", "=============================",
           "=========", "=========", "========="))
    if date:
        print("%9s   %-30s   %9s   %9s   %9s" %
              (date[0], date[1], date[2], date[3], date[4]))
    
    print("\n\n\n%s\n" % "*************************************")

if __name__ == "__main__":
    #create argument/command line parser
    parser = argparse.ArgumentParser()
    parser.add_argument("-a", "--add", nargs=3,
                        help = "Add in the order of systolic, diastolic,
pulse. "
                                "Date/Time is assumed")
    parser.add_argument("-f", "--file", help = "Database file",
                        default = DEFAULT_DATABASE)
    args = parser.parse_args()

    if args.file:
        database = args.file

    #open/initialize database
    con = open_database(database)
    
    if args.add:
        #generate timestamp
        datestamp = datetime.datetime.now()
        add_reading(connection=con,
                    datestamp=datestamp,
                    systolic=args.add[0],
                    diastolic=args.add[1],
                    pulse=args.add[2])

    #produce summary report regardless of any other actions
    generate_report(connection=con)

    #close database
    con.close()

-=-=-

	Designed as an IMPORTable module so other programs could import it,
call the database open, add entry, and report generation functions (though
the latter should be configured to take an output destination so the report
could be sent to a file, not just blatted out on the console).

-=-=-
C:\Users\Wulfraed\Documents\_Hg-Repositories\Python
Progs\LH_BP_tracker>bp_tracker.py -a 123 98 69



*************************************

        Summary for 5 readings

          Systolic    Diastolic     Pulse
          =========   =========   =========
MAX             136          98          70
MIN             115          65          65
AVERAGE       126.8        86.4        68.4

        ******

        Maximum SYSTOLIC event(s)
   ID               Datestamp                Systolic    Diastolic Pulse
=========   =============================    =========   =========
=========
        1   2022-05-09 14:14:37.494260             136          92 70
        2   2022-05-09 14:20:40.620195             136          88 68

        Maximum DIASTOLIC event(s)
   ID               Datestamp                Systolic    Diastolic Pulse
=========   =============================    =========   =========
=========
        5   2022-05-09 14:34:50.873347             123          98 69

        Maximum PULSE event(s)
   ID               Datestamp                Systolic    Diastolic Pulse
=========   =============================    =========   =========
=========
        1   2022-05-09 14:14:37.494260             136          92 70
        3   2022-05-09 14:21:19.428237             124          89 70

        Maximum DATESTAMP event
   ID               Datestamp                Systolic    Diastolic Pulse
=========   =============================    =========   =========
=========
        5   2022-05-09 14:34:50.873347             123          98 69



*************************************

-=-=-
C:\Users\Wulfraed\Documents\_Hg-Repositories\Python
Progs\LH_BP_tracker>sqlite3 bp_numbers.sqlite
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .mode box
sqlite> select * from reading;
+----------------------------------------------------------------+
? ID ?        reading_date        ? systolic ? diastolic ? pulse ?
+----+----------------------------+----------+-----------+-------?
? 1  ? 2022-05-09 14:14:37.494260 ? 136      ? 92        ? 70    ?
? 2  ? 2022-05-09 14:20:40.620195 ? 136      ? 88        ? 68    ?
? 3  ? 2022-05-09 14:21:19.428237 ? 124      ? 89        ? 70    ?
? 4  ? 2022-05-09 14:32:31.471217 ? 115      ? 65        ? 65    ?
? 5  ? 2022-05-09 14:34:50.873347 ? 123      ? 98        ? 69    ?
+----------------------------------------------------------------+
sqlite>
-=-=-


-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
	wlfraed at ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/


From alexkleider at gmail.com  Mon May  9 15:51:27 2022
From: alexkleider at gmail.com (Alex Kleider)
Date: Mon, 9 May 2022 12:51:27 -0700
Subject: [Tutor] Feedback on coding style
In-Reply-To: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
Message-ID: <CAMCEyD5VJGfPP4mwoX+8vhJGBhLnJRPdGWFAJg8EN+_BG8kx3g@mail.gmail.com>

Would you consider putting this code (or Dennis' version of it) on github?
I've played around with some python code to keep track of blood
pressure but haven't used it for a long time.
(https://github.com/alexKleider/blood-pressure-record)
My "data base" is a simple text file.
When an entry is ready to be made I have an alias command defined within .bashrc
as follows:
export BPS=$HOME/Git/BP/bps.txt
alias bpdate="date +'%a %b %d %H:%M %Y' >> $BPS && vim $BPS"
So all that is necessary is to issue the
$ bpdate
command.  One then finds oneself in vim poised to make a BP reading entry.
That solves data entry without any python;
Then one can use python for analysis.

I've never used sqlite but would be interested in continuing to follow
this thread as well as learning how to use it and how to collaborate
using git.


On Mon, May 9, 2022 at 5:03 AM Leam Hall <leamhall at gmail.com> wrote:
>
> Hey all,
>
> I'm looking for general Python code critique, feel free to share snarky comments.  :)  The parameters are:
>   1. Code to Python 3.6 or so.
>   2. Use only the standard library or locally created modules.
>   3. Keep it clean and simple so new Pythonistas can understand and contribute.
>
> Let me know how to make this better.
>
> Leam
> --
> Automation Engineer        (reuel.net/resume)
> Scribe: The Domici War     (domiciwar.net)
> General Ne'er-do-well      (github.com/LeamHall)
>
> ###
>
> #!/usr/bin/env python3
>
> # name:     bp_tracker.py
> # version:  0.0.1
> # date:     20220509
> # author:   Leam Hall
> # desc:     Track and report on blood pressure numbers.
>
> # Notes:
> #  Datafile expects three ints and one float, in order.
>
> # TODO
> #   Add statistical analysis for standard deviation.
> #   Report based on time of day (early, midmorning, afternoon, evening)
> #   (?) Add current distance from goal?
>
> import argparse
> from datetime import datetime
> import os.path
>
> def array_from_file(report_file):
>    data = []
>    with open(report_file, 'r') as file:
>      for line in file:
>        line.strip()
>        datum = line.split()
>        if len(datum) == 4:
>          data.append(datum)
>        else:
>          continue
>    return data
>
> def report(report_data):
>    highest_systolic  = 0
>    highest_diastolic = 0
>    highest_pulse     = 0
>    latest            = -1.0
>    for datum in report_data:
>      systolic  = int(datum[0])
>      diastolic = int(datum[1])
>      pulse     = int(datum[2])
>      date      = float(datum[3])
>      if systolic > highest_systolic:
>        highest_systolic = systolic
>        highest_systolic_event = datum
>      if diastolic > highest_diastolic:
>        highest_diastolic = diastolic
>        highest_diastolic_event = datum
>      if pulse > highest_pulse:
>        highest_pulse = pulse
>        highest_pulse_event = datum
>      if date > latest:
>        latest_record = datum
>
>    print("Highest Systolic: {}/{} {} {}".format(*highest_systolic_event))
>    print("Highest Diastolic: {}/{} {} {}".format(*highest_diastolic_event))
>    print("Highest Pulse: {}/{} {} {}".format(*highest_pulse_event))
>    print("Latest Record: {}/{} {} {}".format(*latest_record))
>
> def result_string(report_list):
>    return "{} {} {} {}".format(*report_list)
>
> report_file = "bp_numbers.txt"
>
> parser = argparse.ArgumentParser()
> parser.add_argument("-a", "--add", nargs=3,
>    help = "Add in the order of systolic, diastolic, pulse")
> parser.add_argument("-f", "--file", help = "Report file")
> args    = parser.parse_args()
>
> if args.file:
>    report_file = args.file
>
> if args.add:
>    # This format allows sequencing now and parsing later.
>    timestamp   = datetime.now().strftime("%Y%m%d.%H%M")
>    this_report = args.add
>    this_report.append(timestamp)
>    with open(report_file, 'a') as file:
>      file.write(result_string(this_report) + "\n")
> else:
>    # Default behavior is to report.
>    if os.path.exists(report_file):
>      try:
>        report_data = array_from_file(report_file)
>        report(report_data)
>      except:
>        print("Error processing report data")
>    else:
>      print("Cannot find ", report_file)
>
> ###
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor



--
alex at kleider.ca  (sent from my current gizmo)

From leamhall at gmail.com  Mon May  9 16:11:16 2022
From: leamhall at gmail.com (Leam Hall)
Date: Mon, 9 May 2022 15:11:16 -0500
Subject: [Tutor] Feedback on coding style
In-Reply-To: <CAMCEyD5VJGfPP4mwoX+8vhJGBhLnJRPdGWFAJg8EN+_BG8kx3g@mail.gmail.com>
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
 <CAMCEyD5VJGfPP4mwoX+8vhJGBhLnJRPdGWFAJg8EN+_BG8kx3g@mail.gmail.com>
Message-ID: <fd81ccd1-5ddc-174f-c0c7-425fb8ecfc56@gmail.com>

Alex,

Because the code was getting a bit long, I asked Alan if it should be in the body of the message or if I should just give the GitHub URL.

	https://github.com/LeamHall/admin_tools/blob/master/bp_tracker.py

For simple stuff like this, plain text is best. I love SQLite but using it here adds complexity and code that isn't really needed. The use case here is simple; "How bad has it been?" and "Am I making progress?" The former pushes me to maintain health changes and the latter reminds me that I still have a ways to go. The later statistical stuff is just to push me to play with statistics; I'm not at the point of needing a math library.

If you want a short demo of SQLite code:

	https://github.com/makhidkarun/Names/blob/master/getName.py

If you're interested in trying out stuff, either python or collaboration, let me know. I'm not the best or the brightest, but I enjoy helping people. Lots of people have helped me.

Leam


On 5/9/22 14:51, Alex Kleider wrote:
> Would you consider putting this code (or Dennis' version of it) on github?
> I've played around with some python code to keep track of blood
> pressure but haven't used it for a long time.
> (https://github.com/alexKleider/blood-pressure-record)
> My "data base" is a simple text file.
> When an entry is ready to be made I have an alias command defined within .bashrc
> as follows:
> export BPS=$HOME/Git/BP/bps.txt
> alias bpdate="date +'%a %b %d %H:%M %Y' >> $BPS && vim $BPS"
> So all that is necessary is to issue the
> $ bpdate
> command.  One then finds oneself in vim poised to make a BP reading entry.
> That solves data entry without any python;
> Then one can use python for analysis.
> 
> I've never used sqlite but would be interested in continuing to follow
> this thread as well as learning how to use it and how to collaborate
> using git.
> 
> 
> On Mon, May 9, 2022 at 5:03 AM Leam Hall <leamhall at gmail.com> wrote:
>>
>> Hey all,
>>
>> I'm looking for general Python code critique, feel free to share snarky comments.  :)  The parameters are:
>>    1. Code to Python 3.6 or so.
>>    2. Use only the standard library or locally created modules.
>>    3. Keep it clean and simple so new Pythonistas can understand and contribute.
>>
>> Let me know how to make this better.
>>
>> Leam
>> --
>> Automation Engineer        (reuel.net/resume)
>> Scribe: The Domici War     (domiciwar.net)
>> General Ne'er-do-well      (github.com/LeamHall)
>>
>> ###
>>
>> #!/usr/bin/env python3
>>
>> # name:     bp_tracker.py
>> # version:  0.0.1
>> # date:     20220509
>> # author:   Leam Hall
>> # desc:     Track and report on blood pressure numbers.
>>
>> # Notes:
>> #  Datafile expects three ints and one float, in order.
>>
>> # TODO
>> #   Add statistical analysis for standard deviation.
>> #   Report based on time of day (early, midmorning, afternoon, evening)
>> #   (?) Add current distance from goal?
>>
>> import argparse
>> from datetime import datetime
>> import os.path
>>
>> def array_from_file(report_file):
>>     data = []
>>     with open(report_file, 'r') as file:
>>       for line in file:
>>         line.strip()
>>         datum = line.split()
>>         if len(datum) == 4:
>>           data.append(datum)
>>         else:
>>           continue
>>     return data
>>
>> def report(report_data):
>>     highest_systolic  = 0
>>     highest_diastolic = 0
>>     highest_pulse     = 0
>>     latest            = -1.0
>>     for datum in report_data:
>>       systolic  = int(datum[0])
>>       diastolic = int(datum[1])
>>       pulse     = int(datum[2])
>>       date      = float(datum[3])
>>       if systolic > highest_systolic:
>>         highest_systolic = systolic
>>         highest_systolic_event = datum
>>       if diastolic > highest_diastolic:
>>         highest_diastolic = diastolic
>>         highest_diastolic_event = datum
>>       if pulse > highest_pulse:
>>         highest_pulse = pulse
>>         highest_pulse_event = datum
>>       if date > latest:
>>         latest_record = datum
>>
>>     print("Highest Systolic: {}/{} {} {}".format(*highest_systolic_event))
>>     print("Highest Diastolic: {}/{} {} {}".format(*highest_diastolic_event))
>>     print("Highest Pulse: {}/{} {} {}".format(*highest_pulse_event))
>>     print("Latest Record: {}/{} {} {}".format(*latest_record))
>>
>> def result_string(report_list):
>>     return "{} {} {} {}".format(*report_list)
>>
>> report_file = "bp_numbers.txt"
>>
>> parser = argparse.ArgumentParser()
>> parser.add_argument("-a", "--add", nargs=3,
>>     help = "Add in the order of systolic, diastolic, pulse")
>> parser.add_argument("-f", "--file", help = "Report file")
>> args    = parser.parse_args()
>>
>> if args.file:
>>     report_file = args.file
>>
>> if args.add:
>>     # This format allows sequencing now and parsing later.
>>     timestamp   = datetime.now().strftime("%Y%m%d.%H%M")
>>     this_report = args.add
>>     this_report.append(timestamp)
>>     with open(report_file, 'a') as file:
>>       file.write(result_string(this_report) + "\n")
>> else:
>>     # Default behavior is to report.
>>     if os.path.exists(report_file):
>>       try:
>>         report_data = array_from_file(report_file)
>>         report(report_data)
>>       except:
>>         print("Error processing report data")
>>     else:
>>       print("Cannot find ", report_file)
>>
>> ###
>> _______________________________________________
>> Tutor maillist  -  Tutor at python.org
>> To unsubscribe or change subscription options:
>> https://mail.python.org/mailman/listinfo/tutor
> 
> 
> 
> --
> alex at kleider.ca  (sent from my current gizmo)

-- 
Automation Engineer        (reuel.net/resume)
Scribe: The Domici War     (domiciwar.net)
General Ne'er-do-well      (github.com/LeamHall)

From wlfraed at ix.netcom.com  Mon May  9 17:51:00 2022
From: wlfraed at ix.netcom.com (Dennis Lee Bieber)
Date: Mon, 09 May 2022 17:51:00 -0400
Subject: [Tutor] Feedback on coding style
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
 <CAMCEyD5VJGfPP4mwoX+8vhJGBhLnJRPdGWFAJg8EN+_BG8kx3g@mail.gmail.com>
Message-ID: <2n0j7hd3uvkd0m33cavosd5nics0091gm7@4ax.com>

On Mon, 9 May 2022 12:51:27 -0700, Alex Kleider <alexkleider at gmail.com>
declaimed the following:

>Would you consider putting this code (or Dennis' version of it) on github?

	I don't "do" git/github. I do have Mercurial installed locally just to
allow me to track history of my fiddling around. As long as attribution is
maintained I have no concerns of what others may do with stuff I post on
this forum (how better to experiment than to start with something
functional and modify it?).

	I'd probably recommend that the first thing to change would be to
create that helper function so that 
-=-=-
    print("\tMaximum %s event(s)" % "SYSTOLIC")
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("   ID", "        Datestamp", "Systolic", "Diastolic", "
Pulse"))
    print("%-9s   %-30s   %-9s   %-9s   %-9s" %
          ("=========", "=============================",
           "=========", "=========", "========="))
    for record in systolic:
        print("%9s   %-30s   %9s   %9s   %9s" %
              (record[0], record[1], record[2], record[3], record[4]))
    print()
-=-=-
becomes a simple

	generate_section("SYSTOLIC", systolic)

(and repeat for diastolic, pulse, date [latest event]). Second change would
then be to pass the output destination, changing all the print() calls into
dest.write() calls (with applicable addition of \n to the formats).

{Heh -- I just did create the helper in my version}



>
>I've never used sqlite but would be interested in continuing to follow
>this thread as well as learning how to use it and how to collaborate
>using git.
>

	Things to read: the Python DB-API PEP (for the general goals of the API
and some of the variations you may encounter if using other RDBMs), the
Python sqlite3 module documentation (for the features specific to SQLite3),
and the SQLite3 documentation itself https://www.sqlite.org/docs.html for
the underlying engine. 

	In particular you will want to read about SQLite3's data model
https://www.sqlite.org/datatype3.html (there are only TEXT, INTEGER, REAL,
and BLOB data types -- the DDL parser accepts most any RDBM data
declaration and maps them to the closest (char, varchar => TEXT, int,
integer, <anything with "int" in it> => INTEGER, float, double => REAL).
BUT it also does not throw an error if one provides something "invalid" --
it will freely accept "one" in an integer field, though obviously one can't
do things like avg() a column with such a value.

	And, of course, for anything significant, you'll want to study some
guide to database normalization (at least to 3rd Normal Form) along with a
good description of general SQL statements (unique/duplicate allowed
indices, foreign keys, constraints).



	SQLite3 is a "file server" engine -- every application links to the
engine and hence is accessing database files using the privileges of the
logged in user (vs "client/server" where the clients send commands to a
separately running server process, and only the server accesses database
files).


-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
	wlfraed at ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/


From alan.gauld at yahoo.co.uk  Mon May  9 19:18:05 2022
From: alan.gauld at yahoo.co.uk (Alan Gauld)
Date: Tue, 10 May 2022 00:18:05 +0100
Subject: [Tutor] Feedback on coding style
In-Reply-To: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
Message-ID: <t5c7fd$101q$1@ciao.gmane.io>

On 09/05/2022 13:01, Leam Hall wrote:

> import argparse
> from datetime import datetime
> import os.path
> 
> def array_from_file(report_file):
>    data = []
>    with open(report_file, 'r') as file:
>      for line in file:
>        line.strip()
>        datum = line.split()
>        if len(datum) == 4:
>          data.append(datum)
>        else:
>          continue

else: continue is not needed.
The loop will restart without it.

> def report(report_data):
>    highest_systolic  = 0
>    highest_diastolic = 0
>    highest_pulse     = 0
>    latest            = -1.0
>    for datum in report_data:
>      systolic  = int(datum[0])
>      diastolic = int(datum[1])
>      pulse     = int(datum[2])
>      date      = float(datum[3])
>      if systolic > highest_systolic:
>        highest_systolic = systolic
>        highest_systolic_event = datum
>      if diastolic > highest_diastolic:
>        highest_diastolic = diastolic
>        highest_diastolic_event = datum
>      if pulse > highest_pulse:
>        highest_pulse = pulse
>        highest_pulse_event = datum
>      if date > latest:
>        latest_record = datum

OK down to here, but you should return the calculated values as a tuple

> 
>    print("Highest Systolic: {}/{} {} {}".format(*highest_systolic_event))
>    print("Highest Diastolic: {}/{} {} {}".format(*highest_diastolic_event))
>    print("Highest Pulse: {}/{} {} {}".format(*highest_pulse_event))
>    print("Latest Record: {}/{} {} {}".format(*latest_record))

The print calls should be outside the function.
Keep logic and presentation separate in case you
decide to change UI to a GUI, Web page, curses etc.

> def result_string(report_list):
>    return "{} {} {} {}".format(*report_list)

Seems overkill to have this inside a function.

> if args.add:
>    # This format allows sequencing now and parsing later.
>    timestamp   = datetime.now().strftime("%Y%m%d.%H%M")
>    this_report = args.add
>    this_report.append(timestamp)

This is extremely fragile. You really should check the format
and data in add before adding it to your precious daa, it
could end up corrupting the file if badly formed - for example
a float rather than an int value?.

>    with open(report_file, 'a') as file:
>      file.write(result_string(this_report) + "\n")

Is that really much clearer than:

      file.write("{} {} {} {}\n.format(*this_report))

> else:
>    # Default behavior is to report.
>    if os.path.exists(report_file):
>      try:
>        report_data = array_from_file(report_file)
>        report(report_data)
>      except:
>        print("Error processing report data")
>    else:
>      print("Cannot find ", report_file)


-- 
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 May  9 19:29:49 2022
From: alan.gauld at yahoo.co.uk (Alan Gauld)
Date: Tue, 10 May 2022 00:29:49 +0100
Subject: [Tutor] Feedback on coding style
In-Reply-To: <2n0j7hd3uvkd0m33cavosd5nics0091gm7@4ax.com>
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
 <CAMCEyD5VJGfPP4mwoX+8vhJGBhLnJRPdGWFAJg8EN+_BG8kx3g@mail.gmail.com>
 <2n0j7hd3uvkd0m33cavosd5nics0091gm7@4ax.com>
Message-ID: <t5c85e$6vu$1@ciao.gmane.io>

On 09/05/2022 22:51, Dennis Lee Bieber wrote:

> 	In particular you will want to read about SQLite3's data model
> https://www.sqlite.org/datatype3.html (there are only TEXT, INTEGER, REAL,
> and BLOB data types -- the DDL parser accepts most any RDBM data
> declaration and maps them to the closest 

In case anyone is worrying about an obvious gap, sqlite has
a set of functions for manipulating/comparing dates/times
which are stored internally as formatted strings.

So although there is no DATETIME type you can still do the
usual date/time manipulations you'd expect with a database.

-- 
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 leamhall at gmail.com  Mon May  9 20:54:28 2022
From: leamhall at gmail.com (Leam Hall)
Date: Mon, 9 May 2022 19:54:28 -0500
Subject: [Tutor] Feedback on coding style
In-Reply-To: <t5c7fd$101q$1@ciao.gmane.io>
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
 <t5c7fd$101q$1@ciao.gmane.io>
Message-ID: <1db68454-160d-086e-ed63-0e8edae299a5@gmail.com>

On 5/9/22 18:18, Alan Gauld via Tutor wrote:
> On 09/05/2022 13:01, Leam Hall wrote:

>>         if len(datum) == 4:
>>           data.append(datum)
>>         else:
>>           continue
> 
> else: continue is not needed.
> The loop will restart without it.

Good point. The else had been set up for some error checking, but I fixed that with nargs in the parser.

> The print calls should be outside the function.
> Keep logic and presentation separate in case you
> decide to change UI to a GUI, Web page, curses etc.

Fixed.

> 
>> def result_string(report_list):
>>     return "{} {} {} {}".format(*report_list)
> 
> Seems overkill to have this inside a function.

Removed.

>> if args.add:
>>     # This format allows sequencing now and parsing later.
>>     timestamp   = datetime.now().strftime("%Y%m%d.%H%M")
>>     this_report = args.add
>>     this_report.append(timestamp)
> 
> This is extremely fragile. You really should check the format
> and data in add before adding it to your precious daa, it
> could end up corrupting the file if badly formed - for example
> a float rather than an int value?.

Yeah, there's no real error checking. I plead "coding too early in the morning". No real tests, either, which needs to be fixed.

> 
>>     with open(report_file, 'a') as file:
>>       file.write(result_string(this_report) + "\n")
> 
> Is that really much clearer than:
> 
>        file.write("{} {} {} {}\n.format(*this_report))

Nope, so it's fixed.

Thanks!

Leam

-- 
Automation Engineer        (reuel.net/resume)
Scribe: The Domici War     (domiciwar.net)
General Ne'er-do-well      (github.com/LeamHall)

From wlfraed at ix.netcom.com  Mon May  9 21:09:06 2022
From: wlfraed at ix.netcom.com (Dennis Lee Bieber)
Date: Mon, 09 May 2022 21:09:06 -0400
Subject: [Tutor] Feedback on coding style
References: <e4d22e97-e341-ace7-722e-ce33cfeb46f1@gmail.com>
 <CAMCEyD5VJGfPP4mwoX+8vhJGBhLnJRPdGWFAJg8EN+_BG8kx3g@mail.gmail.com>
 <2n0j7hd3uvkd0m33cavosd5nics0091gm7@4ax.com> <t5c85e$6vu$1@ciao.gmane.io>
Message-ID: <iodj7hpoaofa87ldgjnc8jo64kjdavssp9@4ax.com>

On Tue, 10 May 2022 00:29:49 +0100, Alan Gauld via Tutor <tutor at python.org>
declaimed the following:


>In case anyone is worrying about an obvious gap, sqlite has
>a set of functions for manipulating/comparing dates/times
>which are stored internally as formatted strings.
>
	If the data is a TEXT type... REAL favors using them as Julian Day
number, and INTEGER as Seconds since Jan 1 1970. See section 2.2 of the
linked documentation.

>So although there is no DATETIME type you can still do the
>usual date/time manipulations you'd expect with a database.

	However, the Python module, with proper options, is supposed to handle
conversion of Python datetime.date and datetime.datetime structures in/out
SQLite3 when using "non-supported" "date" and "timestamp" as the data types
in the table definition. Note that I did NOT create a formatted datetime
when providing the timestamp in the INSERT DML (I did not check if the
converters worked on the SELECT DML, all I know is the printed reported had
a formatted datetime and not something like <datetime object ID xxx>)

	Per section 3.1, "date" and "timestamp" both fall under rule #5 and get
the generic NUMERIC affinity (which probably translates to the formatted
string from the converters since such aren't "well-formed" INT or REAL
values <G>)



-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
	wlfraed at ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/


From juliushamilton100 at gmail.com  Wed May 11 17:13:36 2022
From: juliushamilton100 at gmail.com (Julius Hamilton)
Date: Wed, 11 May 2022 23:13:36 +0200
Subject: [Tutor] Search package for path to class
Message-ID: <CAEsMKX1awx6jb9ojzJ-BnUC7wFmNQiRP7XN2OLsnHOLSwRxLWw@mail.gmail.com>

Hey,

In a Python shell I would like to find the path to a class in a package in
order to import it.

So if class ?ServiceRunner? is imported via yapapi.services.service_runner
but I don?t know that, I would like to do something like:

find(?ServiceRunner?)

and return the result

?yapapi.services.service_runner? - assuming I already imported yapapi.

How would this be possible?

Thanks very much,
Julius

From joel.goldstick at gmail.com  Wed May 11 17:22:14 2022
From: joel.goldstick at gmail.com (Joel Goldstick)
Date: Wed, 11 May 2022 17:22:14 -0400
Subject: [Tutor] Search package for path to class
In-Reply-To: <CAEsMKX1awx6jb9ojzJ-BnUC7wFmNQiRP7XN2OLsnHOLSwRxLWw@mail.gmail.com>
References: <CAEsMKX1awx6jb9ojzJ-BnUC7wFmNQiRP7XN2OLsnHOLSwRxLWw@mail.gmail.com>
Message-ID: <CAPM-O+y5xDjK51skXMZZZcH6zYqgS-K8gAKp1dZpu_+o6wXJcw@mail.gmail.com>

On Wed, May 11, 2022 at 5:15 PM Julius Hamilton
<juliushamilton100 at gmail.com> wrote:
>
> Hey,
>
> In a Python shell I would like to find the path to a class in a package in
> order to import it.
>
> So if class ?ServiceRunner? is imported via yapapi.services.service_runner
> but I don?t know that, I would like to do something like:
>
> find(?ServiceRunner?)
>
> and return the result
>
> ?yapapi.services.service_runner? - assuming I already imported yapapi.
>
> How would this be possible?
>
> Thanks very much,
> Julius
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor

I'm not sure if pydoc would do what you want to do, but you could play
around with it.  It's a great tool.  I think it comes with a standard
python install.

-- 
Joel Goldstick

From cs at cskk.id.au  Wed May 11 17:32:31 2022
From: cs at cskk.id.au (Cameron Simpson)
Date: Thu, 12 May 2022 07:32:31 +1000
Subject: [Tutor] Search package for path to class
In-Reply-To: <CAEsMKX1awx6jb9ojzJ-BnUC7wFmNQiRP7XN2OLsnHOLSwRxLWw@mail.gmail.com>
References: <CAEsMKX1awx6jb9ojzJ-BnUC7wFmNQiRP7XN2OLsnHOLSwRxLWw@mail.gmail.com>
Message-ID: <Ynwrb1OnyVEj+P8H@cskk.homeip.net>

On 11May2022 23:13, Julius Hamilton <juliushamilton100 at gmail.com> wrote:
>So if class ?ServiceRunner? is imported via 
>yapapi.services.service_runner
>but I don?t know that, I would like to do something like:
>
>find(?ServiceRunner?)

ServiceRunner.__module__ maybe?

Cheers,
Cameron Simpson <cs at cskk.id.au>

From rex.oni at gmail.com  Wed May 11 11:27:22 2022
From: rex.oni at gmail.com (Rex Oni)
Date: Wed, 11 May 2022 16:27:22 +0100
Subject: [Tutor] Project Question: HELP ME SIR
Message-ID: <CAGBOPB7mBVBwYAC4verXACA6fmKe_bnBcZLk8DnMYsUc1EcGmA@mail.gmail.com>

We need to import two modules for myPythonFunctions.py: the
random module and the os module.
We?ll be using the randint() function from the random module. The
randint() function generates a random integer within the range
provided by us. We?ll use that to generate numbers for our questions
later.
>From the os module, we?ll be using the remove() and rename()
functions.
Help me Try importing these two modules.


*Rex Oni (OCP)*
*Oracle Programmer | Oracle Financials Support Engineer | Database
Administrator | Data Analyst | Worship Leader | Vocal Artist | Song Writer
| Pastor | General Security*
*MOBILE: +2349080519328*

From simon.n.connah at protonmail.com  Wed May 11 15:57:38 2022
From: simon.n.connah at protonmail.com (Simon Connah)
Date: Wed, 11 May 2022 19:57:38 +0000
Subject: [Tutor] Python, Poetry, PyCharm Pro and macOS
Message-ID: <zxkXt6P5jFuhieqkIwjTaXgx0uecWgcoaYpwrGwgsci-SCwCswJuSNbZzZWcEt7CWBVEEqluY-lpeFerWjV3872_CX-lpjnJ2186DMUIc64=@protonmail.com>

Hi,

I have installed Python 3.10.4 on my Apple Silicon Mac. I then downloaded PyCharm Pro version 2022.1 and started a new project. When asked what package manager I wanted to use (the options were pipenv, virtualenv, poetry and conda) I chose Poetry because I hadn't tried it before and after reading the website a bit it sounded better than using virtualenv which is what I have used in the past.

Anyway, things seemed to work correctly but then I tried to install scrypt and the build failed. I wanted to delete the poetry environment so I could start again from scratch but I then realised I had no idea where poetry on macOS stored the environment.

Can someone point me in the right direction, please?

Simon.

From learn2program at gmail.com  Wed May 11 18:41:14 2022
From: learn2program at gmail.com (Alan Gauld)
Date: Wed, 11 May 2022 23:41:14 +0100
Subject: [Tutor] Python, Poetry, PyCharm Pro and macOS
In-Reply-To: <zxkXt6P5jFuhieqkIwjTaXgx0uecWgcoaYpwrGwgsci-SCwCswJuSNbZzZWcEt7CWBVEEqluY-lpeFerWjV3872_CX-lpjnJ2186DMUIc64=@protonmail.com>
References: <zxkXt6P5jFuhieqkIwjTaXgx0uecWgcoaYpwrGwgsci-SCwCswJuSNbZzZWcEt7CWBVEEqluY-lpeFerWjV3872_CX-lpjnJ2186DMUIc64=@protonmail.com>
Message-ID: <2b1ddf43-4cc4-b889-7d5d-139c618b73ac@yahoo.co.uk>

On 11/05/2022 20:57, Simon Connah via Tutor wrote:
> to delete the poetry environment so I could start again
>  from scratch but I then realised I had no idea where 
> poetry on macOS stored the environment.
>
> Can someone point me in the right direction, please?

This is really off topic for this list which is about the Python
language and its standard library.

However there are some Mac users here (including myself, except I don't
use virtual
envs of any variety) so someone might know.

Failing that, you should ask on the poetry support forum. If there is no
support
forum I'd take that as a very good reason not to use it!

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


From learn2program at gmail.com  Wed May 11 18:47:00 2022
From: learn2program at gmail.com (Alan Gauld)
Date: Wed, 11 May 2022 23:47:00 +0100
Subject: [Tutor] Project Question: HELP ME SIR
In-Reply-To: <CAGBOPB7mBVBwYAC4verXACA6fmKe_bnBcZLk8DnMYsUc1EcGmA@mail.gmail.com>
References: <CAGBOPB7mBVBwYAC4verXACA6fmKe_bnBcZLk8DnMYsUc1EcGmA@mail.gmail.com>
Message-ID: <0910e52d-43f2-6604-3cd1-7f06e5f60531@yahoo.co.uk>

On 11/05/2022 16:27, Rex Oni wrote:
> We need to import two modules for myPythonFunctions.py: the
> random module and the os module.

This sounds like a homework? We won't do your homework.

How to import modules should be covered very early in any Python tutorial.

You basically can't do much without importing things...

> We?ll be using the randint() function from the random module. 
> From the os module, we?ll be using the remove() and rename()
> functions.

What have you tried?

There are several different import options, each has value depending
on what you need.

import MODULE [as alias]

from MODULE import NAME [ as alias]

import * from MODULE????? # usually only at the >>> prompt

Read the docs to decide which is most appropriate for you.

-- 
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  Wed May 11 18:47:14 2022
From: wlfraed at ix.netcom.com (Dennis Lee Bieber)
Date: Wed, 11 May 2022 18:47:14 -0400
Subject: [Tutor] Search package for path to class
References: <CAEsMKX1awx6jb9ojzJ-BnUC7wFmNQiRP7XN2OLsnHOLSwRxLWw@mail.gmail.com>
Message-ID: <rido7hpfrlgonf59vajvsvq77vms3njbvv@4ax.com>

On Wed, 11 May 2022 23:13:36 +0200, Julius Hamilton
<juliushamilton100 at gmail.com> declaimed the following:

>find(?ServiceRunner?)
>
>and return the result
>
>?yapapi.services.service_runner? - assuming I already imported yapapi.
>
>How would this be possible?
>

	I suspect you would have to perform a recursive walk of each visible
module, keeping track of the levels (parent modules), checking if the
target is in the dir(modulepath), and return the module path for each match
(I say each as I'd recommend not bailing out on the first find -- you may
discover names are used in multiple modules, or a parent did a "from xyz
import name").

	Problem: dir() returns strings, and type() just says it is a string.

	Instead, use sys.modules (which appears to obviate the need for
recursion)

	Unfortunately, that is a long list even for an empty "shell"

C:\Users\Wulfraed>python
Python ActivePython 3.8.2 (ActiveState Software Inc.) based on
 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> for item in sys.modules:
...   print(item)
...
sys
builtins
_frozen_importlib
_imp
_warnings
_frozen_importlib_external
_io
marshal
nt
_thread
_weakref
winreg
time
zipimport
_codecs
codecs
encodings.aliases
encodings
encodings.utf_8
encodings.cp1252
_signal
__main__
encodings.latin_1
_abc
abc
io
_stat
stat
_collections_abc
genericpath
ntpath
os.path
os
_sitebuiltins
_locale
_bootlocale
types
importlib._bootstrap
importlib._bootstrap_external
warnings
importlib
importlib.machinery
importlib.abc
_operator
operator
keyword
_heapq
heapq
itertools
reprlib
_collections
collections
_functools
functools
contextlib
importlib.util
paste
google
google.cloud
google.logging
google.iam
logilab
mpl_toolkits
pywin32_bootstrap
repoze
sphinxcontrib
zope
site
__future__
pyreadline.py3k_compat
pyreadline.unicode_helper
_socket
collections.abc
math
select
selectors
enum
errno
socket
_sre
sre_constants
sre_parse
sre_compile
copyreg
re
token
tokenize
linecache
traceback
_weakrefset
weakref
_string
string
threading
atexit
logging
_struct
struct
_compat_pickle
_pickle
pickle
_queue
queue
copy
logging.handlers
pyreadline.logger
_ctypes
ctypes._endian
ctypes
ctypes.wintypes
pyreadline.keysyms.winconstants
pyreadline.keysyms.common
pyreadline.keysyms.keysyms
pyreadline.keysyms
pyreadline.clipboard.win32_clipboard
pyreadline.clipboard
pyreadline.lineeditor
pyreadline.lineeditor.wordmatcher
pyreadline.lineeditor.lineobj
pyreadline.lineeditor.history
posixpath
fnmatch
glob
pyreadline.error
pyreadline.modes.basemode
pyreadline.modes.emacs
pyreadline.modes.notemacs
pyreadline.modes.vi
pyreadline.modes
pyreadline.console.ansi
zlib
_compression
_bz2
bz2
_lzma
lzma
shutil
signal
msvcrt
_winapi
subprocess
ctypes.util
pyreadline.console.event
pyreadline.console.console
pyreadline.console
pyreadline.release
pyreadline.rlmain
pyreadline
readline
rlcompleter
>>>

	For each, check if dir() has the sought after name... (I just did a
loop over some -- you probably want something like
		if findname in dir(item):
			print(item, findname)
)

>>> for item in sys.modules:
... 	if item.startswith("google"):
... 		for names in dir(item):
... 			print(item, names)
... 			
google __add__
google __class__
google __contains__
google __delattr__
google __dir__
google __doc__
google __eq__
google __format__
google __ge__
google __getattribute__
google __getitem__
google __getnewargs__
google __gt__
google __hash__
google __init__
google __init_subclass__
google __iter__
google __le__
google __len__
google __lt__
google __mod__
google __mul__
google __ne__
google __new__
google __reduce__
google __reduce_ex__
google __repr__
google __rmod__
google __rmul__
google __setattr__
google __sizeof__
google __str__
google __subclasshook__
google capitalize
google casefold
google center
google count
google encode
google endswith
google expandtabs
google find
google format
google format_map
google index
google isalnum
google isalpha
google isascii
google isdecimal
google isdigit
google isidentifier
google islower
google isnumeric
google isprintable
google isspace
google istitle
google isupper
google join
google ljust
google lower
google lstrip
google maketrans
google partition
google replace
google rfind
google rindex
google rjust
google rpartition
google rsplit
google rstrip
google split
google splitlines
google startswith
google strip
google swapcase
google title
google translate
google upper
google zfill
google.cloud __add__
google.cloud __class__
google.cloud __contains__
google.cloud __delattr__
google.cloud __dir__
google.cloud __doc__
google.cloud __eq__
google.cloud __format__
google.cloud __ge__
google.cloud __getattribute__
google.cloud __getitem__
google.cloud __getnewargs__
google.cloud __gt__
google.cloud __hash__
google.cloud __init__
google.cloud __init_subclass__
google.cloud __iter__
google.cloud __le__
google.cloud __len__
google.cloud __lt__
google.cloud __mod__
google.cloud __mul__
google.cloud __ne__
google.cloud __new__
google.cloud __reduce__
google.cloud __reduce_ex__
google.cloud __repr__
google.cloud __rmod__
google.cloud __rmul__
google.cloud __setattr__
google.cloud __sizeof__
google.cloud __str__


-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
	wlfraed at ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/


From simon.n.connah at protonmail.com  Wed May 11 22:36:36 2022
From: simon.n.connah at protonmail.com (Simon Connah)
Date: Thu, 12 May 2022 02:36:36 +0000
Subject: [Tutor] Python, Poetry, PyCharm Pro and macOS
In-Reply-To: <2b1ddf43-4cc4-b889-7d5d-139c618b73ac@yahoo.co.uk>
References: <zxkXt6P5jFuhieqkIwjTaXgx0uecWgcoaYpwrGwgsci-SCwCswJuSNbZzZWcEt7CWBVEEqluY-lpeFerWjV3872_CX-lpjnJ2186DMUIc64=@protonmail.com>
 <2b1ddf43-4cc4-b889-7d5d-139c618b73ac@yahoo.co.uk>
Message-ID: <OHj_BEp6LK10tnL5sgm8tRsKYf3WIp6mm_m4SjuEST1eLVInWOoTbfr-NuVkQ05bqc6Zdz0FGQ5YxZum5mU1yKbIx9Tm2nxylvE7PUA85DA=@protonmail.com>

------- Original Message -------
On Wednesday, May 11th, 2022 at 23:41, Alan Gauld <learn2program at gmail.com> wrote:


> 

> 

> On 11/05/2022 20:57, Simon Connah via Tutor wrote:
> 

> > to delete the poetry environment so I could start again
> > from scratch but I then realised I had no idea where
> > poetry on macOS stored the environment.
> > 

> > Can someone point me in the right direction, please?
> 

> 

> This is really off topic for this list which is about the Python
> language and its standard library.
> 

> However there are some Mac users here (including myself, except I don't
> use virtual
> envs of any variety) so someone might know.
> 

> Failing that, you should ask on the poetry support forum. If there is no
> support
> forum I'd take that as a very good reason not to use it!
> 

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

> _______________________________________________
> Tutor maillist - Tutor at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor

I apologise. I didn't realise it was off-topic for the mailing list. I'll only use it for the language and the standard library in the future.

Sorry.

Simon.

From mats at wichmann.us  Thu May 12 14:25:58 2022
From: mats at wichmann.us (Mats Wichmann)
Date: Thu, 12 May 2022 12:25:58 -0600
Subject: [Tutor] Python, Poetry, PyCharm Pro and macOS
In-Reply-To: <zxkXt6P5jFuhieqkIwjTaXgx0uecWgcoaYpwrGwgsci-SCwCswJuSNbZzZWcEt7CWBVEEqluY-lpeFerWjV3872_CX-lpjnJ2186DMUIc64=@protonmail.com>
References: <zxkXt6P5jFuhieqkIwjTaXgx0uecWgcoaYpwrGwgsci-SCwCswJuSNbZzZWcEt7CWBVEEqluY-lpeFerWjV3872_CX-lpjnJ2186DMUIc64=@protonmail.com>
Message-ID: <91e16f73-8531-ac97-d4d7-9db4b912c6e4@wichmann.us>

On 5/11/22 13:57, Simon Connah via Tutor wrote:
> Hi,
> 
> I have installed Python 3.10.4 on my Apple Silicon Mac. I then downloaded PyCharm Pro version 2022.1 and started a new project. When asked what package manager I wanted to use (the options were pipenv, virtualenv, poetry and conda) I chose Poetry because I hadn't tried it before and after reading the website a bit it sounded better than using virtualenv which is what I have used in the past.
> 
> Anyway, things seemed to work correctly but then I tried to install scrypt and the build failed. I wanted to delete the poetry environment so I could start again from scratch but I then realised I had no idea where poetry on macOS stored the environment.

Since you did that *through* PyCharm, PyCharm should know where it is.

From nathan-tech at hotmail.com  Mon May 16 22:05:12 2022
From: nathan-tech at hotmail.com (Nathan Smith)
Date: Tue, 17 May 2022 03:05:12 +0100
Subject: [Tutor] subprocess figuring out what is in stdout
Message-ID: <DB7PR07MB5093AD6202E4C23DBB4F9F25E4CE9@DB7PR07MB5093.eurprd07.prod.outlook.com>

I'm working with a program which requires interactive shell like reading 
of the output, EG user types input in response to various outputs 
displayed to them.

Unfortunately, in some cases, the program being run is not either 
printing an "\n" at the end of particular lines, or is not flushing? it.

This only happens with specific points and is easily responsed. 
Unfortunately, the unflushed data does not appear until the question 
being asked is answered (rather unhelpful to the user)


My current implementation uses readline which is why I suspect the 
problem may be that there is no "\n" character at the end, hence why 
readline is not flushing it until more output is added.


One possible solution I found is to do stdout.read(1) and do it 
character by character, but the efficiency seems low and this also 
presents some other problems with other parts of my program.


To that end I was wondering, perhaps is there a way to tell how many 
characters/bytes are in a stdout buffer before it is read? That way 
read(x) could give the exact number.


I've seen some references talk about a way of making non blocking ports, 
but these seem to be linux solutions and don't play nice with windows so 
this doesn't really seem to be an option.



thanks in advance for any tips.


-- 

Best Wishes,

Nathan Smith, BSC


My Website: https://nathantech.net



From mats at wichmann.us  Tue May 17 10:34:26 2022
From: mats at wichmann.us (Mats Wichmann)
Date: Tue, 17 May 2022 08:34:26 -0600
Subject: [Tutor] subprocess figuring out what is in stdout
In-Reply-To: <DB7PR07MB5093AD6202E4C23DBB4F9F25E4CE9@DB7PR07MB5093.eurprd07.prod.outlook.com>
References: <DB7PR07MB5093AD6202E4C23DBB4F9F25E4CE9@DB7PR07MB5093.eurprd07.prod.outlook.com>
Message-ID: <8360ae62-3422-1c1a-fd74-4fe06f467ab9@wichmann.us>

On 5/16/22 20:05, Nathan Smith wrote:
> I'm working with a program which requires interactive shell like reading
> of the output, EG user types input in response to various outputs
> displayed to them.
> 
> Unfortunately, in some cases, the program being run is not either
> printing an "\n" at the end of particular lines, or is not flushing? it.

It's not entirely clear what you're trying to accomplish, so this is
guessing...

Getting this to work well depends on how much control you have over the
external program. If you don't have control over it, you might look at
how the Python version of expect works
(https://pexpect.readthedocs.io/en/stable) - or maybe even be able to
use it?

It's normally up to the external process to decide how to handle its own
stdout/stderr. You can sometimes make things work by wiring up the
plumbing appropriately.  On Linux, the standard library does buffering
if the output is a pipe, so if you can "make it look like a terminal"
(i.e. plug in a pseudo-tty), it might work better for you - see
pexpect's FAQ on this topic:

https://pexpect.readthedocs.io/en/stable/FAQ.html?highlight=unbuffer


From nathan-tech at hotmail.com  Tue May 17 14:32:42 2022
From: nathan-tech at hotmail.com (Nathan Smith)
Date: Tue, 17 May 2022 19:32:42 +0100
Subject: [Tutor] subprocess figuring out what is in stdout
In-Reply-To: <8360ae62-3422-1c1a-fd74-4fe06f467ab9@wichmann.us>
References: <DB7PR07MB5093AD6202E4C23DBB4F9F25E4CE9@DB7PR07MB5093.eurprd07.prod.outlook.com>
 <8360ae62-3422-1c1a-fd74-4fe06f467ab9@wichmann.us>
Message-ID: <DB7PR07MB50932ECA7F555C093FA19678E4CE9@DB7PR07MB5093.eurprd07.prod.outlook.com>

Hi Mats,


thanks for that. I did end up going down pexpect 's path, though I went 
with winpexpect for windows.


Worked a treat.

Nathan


On 17/05/2022 15:34, Mats Wichmann wrote:
> On 5/16/22 20:05, Nathan Smith wrote:
>> I'm working with a program which requires interactive shell like reading
>> of the output, EG user types input in response to various outputs
>> displayed to them.
>>
>> Unfortunately, in some cases, the program being run is not either
>> printing an "\n" at the end of particular lines, or is not flushing? it.
> It's not entirely clear what you're trying to accomplish, so this is
> guessing...
>
> Getting this to work well depends on how much control you have over the
> external program. If you don't have control over it, you might look at
> how the Python version of expect works
> (https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpexpect.readthedocs.io%2Fen%2Fstable&amp;data=05%7C01%7C%7C4da2f22d48ae40ee40f108da3812996b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637883949776396684%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=W73FQxzF2dbAvQlozmavHMTAbIlqCluU0QuWXC%2FZIQA%3D&amp;reserved=0) - or maybe even be able to
> use it?
>
> It's normally up to the external process to decide how to handle its own
> stdout/stderr. You can sometimes make things work by wiring up the
> plumbing appropriately.  On Linux, the standard library does buffering
> if the output is a pipe, so if you can "make it look like a terminal"
> (i.e. plug in a pseudo-tty), it might work better for you - see
> pexpect's FAQ on this topic:
>
> https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpexpect.readthedocs.io%2Fen%2Fstable%2FFAQ.html%3Fhighlight%3Dunbuffer&amp;data=05%7C01%7C%7C4da2f22d48ae40ee40f108da3812996b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637883949776396684%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=Uo1LVcpOBi%2BJuNtugMuPCfJuGQfjHvVEyrNfYMKVf2k%3D&amp;reserved=0
>
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.python.org%2Fmailman%2Flistinfo%2Ftutor&amp;data=05%7C01%7C%7C4da2f22d48ae40ee40f108da3812996b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637883949776396684%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=OJc5sIX0ctguw5CeTBdXXwSuNYSldv3tNO24KyBhCoc%3D&amp;reserved=0
-- 

Best Wishes,

Nathan Smith, BSC


My Website: https://nathantech.net



From rjwilcox at gmail.com  Thu May 19 08:28:47 2022
From: rjwilcox at gmail.com (Robert Wilcox)
Date: Thu, 19 May 2022 08:28:47 -0400
Subject: [Tutor] Count Within a G on
Message-ID: <CAKJK9KnUci=z3DPg6rjB-7Y_w3pmW2=OZfWZz+ojiSsi0DfNQA@mail.gmail.com>

Cheers,
I have a dataframe of IDs that has a specific order. I need a rolling count
of IDs for each ID.
Below my example has 2 IDs with 2 other data fields in order. The Count
field is what I need to add to the dataframe:
ID       Value_1        Value_2     Count
1234    x                    a                1
1234    x                    b                2
1234    y                    c                3
2222    s                    a                1
2222    t                     b                2

Thanks,
Bob

From leamhall at gmail.com  Thu May 19 14:19:03 2022
From: leamhall at gmail.com (Leam Hall)
Date: Thu, 19 May 2022 13:19:03 -0500
Subject: [Tutor] Count Within a G on
In-Reply-To: <CAKJK9KnUci=z3DPg6rjB-7Y_w3pmW2=OZfWZz+ojiSsi0DfNQA@mail.gmail.com>
References: <CAKJK9KnUci=z3DPg6rjB-7Y_w3pmW2=OZfWZz+ojiSsi0DfNQA@mail.gmail.com>
Message-ID: <CACv9p5pvLpF9n5LYcHWNqanFbko8bD0YAtwZ=HV-rPnicK+WBg@mail.gmail.com>

What code have you tried so far?

On Thu, May 19, 2022, 12:57 PM Robert Wilcox <rjwilcox at gmail.com> wrote:

> Cheers,
> I have a dataframe of IDs that has a specific order. I need a rolling count
> of IDs for each ID.
> Below my example has 2 IDs with 2 other data fields in order. The Count
> field is what I need to add to the dataframe:
> ID       Value_1        Value_2     Count
> 1234    x                    a                1
> 1234    x                    b                2
> 1234    y                    c                3
> 2222    s                    a                1
> 2222    t                     b                2
>
> Thanks,
> Bob
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
>

From learn2program at gmail.com  Mon May 23 03:46:15 2022
From: learn2program at gmail.com (Alan Gauld)
Date: Mon, 23 May 2022 08:46:15 +0100
Subject: [Tutor] Fwd: Request to mailing list Tutor rejected
In-Reply-To: <DU0P189MB2275E6D0D0FE1401785EF52BE8D49@DU0P189MB2275.EURP189.PROD.OUTLOOK.COM>
References: <DU0P189MB2275E6D0D0FE1401785EF52BE8D49@DU0P189MB2275.EURP189.PROD.OUTLOOK.COM>
Message-ID: <6ef17780-f87f-89a6-ac07-2e16f7b999ea@yahoo.co.uk>


Forwarding to group from owner...

-------------------

Hello

Thankyou for replying.?

e.g. t = t['physical square shape'1]

the text in single quotes is actually the shape of a square not text.?

It was a question in my python asssessment last week. There were three
of them like this. I can't get any answers from the course instructors
but I would like to be able to know for my own information at least if
these questions where of an incorrect format.?

Any information is greatly appreciated.?

Kr
Belinda Chisholm
------------------------------------------------------------------------


From dimitarxivanov at gmail.com  Tue May 24 09:27:17 2022
From: dimitarxivanov at gmail.com (Dimitar Ivanov)
Date: Tue, 24 May 2022 14:27:17 +0100
Subject: [Tutor] Object destruction
Message-ID: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>

Hi all,

I'm trying to come up with a (working) design of tracking an object
throughout its lifecycle but I'm unable to find quite what I'm looking for,
so I hope you folks will be able to give me some tips.

I have an object that is being created and I want to "trace" that object in
a separate static class that other threads and objects will be able to
access if necessary:

class TestClass:

    def __init__(self, name):
        self.name = name
        self._finalizer = weakref.finalize(self, self.finalize)

    def do_stuff(self):
        print(f"Object {self.name} doing stuff")
        Tracer.trace(self.name)
        time.sleep(5)

    def finalize(self):
        print(f"Entered finalize method for object {self.name}")
        Tracer.untrace(self.name)

Here, I have the static object that "traces" those objects:

class Tracer:

    traced_objects = []

    @staticmethod
    def trace(some_object):
        Tracer.traced_objects.append(some_object)

    @staticmethod
    def untrace(some_object):
        Tracer.traced_objects.remove(some_object)

And here's my main method where I test creating the objects, kicking off
their do_stuff methods in a thread and then deleting them and checking if
they've been removed from the traced_objects list in the Tracer class:

if __name__ == '__main__':
    objectOne = TestClass("Object1")
    objectTwo = TestClass("Object2")
    thrd1 = Thread(target=objectOne.do_stuff)
    thrd2 = Thread(target=objectTwo.do_stuff)
    thrd1.start()
    thrd2.start()
    thrd1.join()
    thrd2.join()
    del objectOne
    del objectTwo
    print("Threads are done, checking Tracer's dict")
    print(f"Tracer's dict: {Tracer.traced_objects}")

It seems like the Finalizer is only kicking off the objects' finalize
methods once the program exits, which, if I'm reading the documentation
right, is the correct behaviour; however, I need that finalize method
kicked off as soon as any reference to those objects is removed. Can I
achieve that in any way?

Thanks a lot in advance and please, do let me know if my explanation is
vague and/or unclear!

Regards,
Dimitar

From alan.gauld at yahoo.co.uk  Tue May 24 19:07:11 2022
From: alan.gauld at yahoo.co.uk (Alan Gauld)
Date: Wed, 25 May 2022 00:07:11 +0100
Subject: [Tutor] Object destruction
In-Reply-To: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
References: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
Message-ID: <t6jof0$gu0$1@ciao.gmane.io>

On 24/05/2022 14:27, Dimitar Ivanov wrote:

> I'm trying to come up with a (working) design of tracking an object
> throughout its lifecycle but I'm unable to find quite what I'm looking for,

Thats because I think you are looking for the wrong thing.
I think you ae getting confused between objects and names.

>     def __init__(self, name):
>         self.name = name
>         self._finalizer = weakref.finalize(self, self.finalize)

This only registers a metjod that will be called when the object
is garbage collected. That in turn will be "some time"
after the last strong reference is broken.

> Here, I have the static object that "traces" those objects:
> 
> class Tracer:
> 
>     traced_objects = []
> 
>     @staticmethod
>     def trace(some_object):
>         Tracer.traced_objects.append(some_object)
> 
>     @staticmethod
>     def untrace(some_object):
>         Tracer.traced_objects.remove(some_object)

This seems like a pointless class. It could (should?)
just be two functions.

> if __name__ == '__main__':
>     objectOne = TestClass("Object1")
>     objectTwo = TestClass("Object2")
>     thrd1 = Thread(target=objectOne.do_stuff)
>     thrd2 = Thread(target=objectTwo.do_stuff)
>     thrd1.start()
>     thrd2.start()
>     thrd1.join()
>     thrd2.join()
>     del objectOne
>     del objectTwo

But these del statements don't delete the object, they simply
delete the name from the namespace. Doing so reduces the reference
count but does not delete the object. (As you've discovered)

So which are you really interested in? Is it the lifecycle of
the object? The lifecycle of the name? Or the references?

> It seems like the Finalizer is only kicking off the objects' finalize
> methods once the program exits, which, if I'm reading the documentation
> right, is the correct behaviour

Only because that's the point that the object is destroyed.
You are correctly monitoring the object lifecycle.

; however, I need that finalize method
> kicked off as soon as any reference to those objects is removed. 

Any reference?

a = MyClass()
b = a
objects = [a,b]
del a
del b
del objects

Should that call the finalizer once? or twice? or three times? or four?
And when?

> Can I achieve that in any way?

You need to be clearer what you want.
You are already tracking object lifecycles successfully.
You are not tracking references, that would need a different
approach entirely.

-- 
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  Tue May 24 19:10:03 2022
From: cs at cskk.id.au (Cameron Simpson)
Date: Wed, 25 May 2022 09:10:03 +1000
Subject: [Tutor] Object destruction
In-Reply-To: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
References: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
Message-ID: <Yo1ly99mMVa68Qph@cskk.homeip.net>

On 24May2022 14:27, Dimitar Ivanov <dimitarxivanov at gmail.com> wrote:
>I'm trying to come up with a (working) design of tracking an object
>throughout its lifecycle but I'm unable to find quite what I'm looking for,
>so I hope you folks will be able to give me some tips.
>
>I have an object that is being created and I want to "trace" that object in
>a separate static class that other threads and objects will be able to
>access if necessary:
[...]
>It seems like the Finalizer is only kicking off the objects' finalize
>methods once the program exits, which, if I'm reading the documentation
>right, is the correct behaviour; however, I need that finalize method
>kicked off as soon as any reference to those objects is removed. Can I
>achieve that in any way?

_Any_ reference, or when the final-reference-except-for-your-tracer 
reference is removed? For the latter, see the weakref module:
https://docs.python.org/3/library/weakref.html#module-weakref

For making references to objects which do not contribute to their 
reference count.

My SingletonMixin class uses a WeakValueDictionary to keep references to 
existing instances without extending their lifespan. A 
WeakValueDictionary may well fit your use case.

Cheers,
Cameron Simpson <cs at cskk.id.au>

From cs at cskk.id.au  Tue May 24 19:59:34 2022
From: cs at cskk.id.au (Cameron Simpson)
Date: Wed, 25 May 2022 09:59:34 +1000
Subject: [Tutor] Object destruction
In-Reply-To: <Yo1ly99mMVa68Qph@cskk.homeip.net>
References: <Yo1ly99mMVa68Qph@cskk.homeip.net>
Message-ID: <Yo1xZtSpH01tZ8KL@cskk.homeip.net>

On 25May2022 09:10, Cameron Simpson <cs at cskk.id.au> wrote:
>On 24May2022 14:27, Dimitar Ivanov <dimitarxivanov at gmail.com> wrote:
>>I'm trying to come up with a (working) design of tracking an object
>>throughout its lifecycle but I'm unable to find quite what I'm looking for,
>>so I hope you folks will be able to give me some tips.
[...]
>
>_Any_ reference, or when the final-reference-except-for-your-tracer
>reference is removed? For the latter, see the weakref module:
>https://docs.python.org/3/library/weakref.html#module-weakref
>For making references to objects which do not contribute to their
>reference count.

I did not notice that you are already using weakref. Apologies,
Cameron Simpson <cs at cskk.id.au>

From roel at roelschroeven.net  Tue May 24 19:41:05 2022
From: roel at roelschroeven.net (Roel Schroeven)
Date: Wed, 25 May 2022 01:41:05 +0200
Subject: [Tutor] Object destruction
In-Reply-To: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
References: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
Message-ID: <b173b513-3a8d-445b-4b38-46a71732afa9@roelschroeven.net>

Dimitar Ivanov schreef op 24/05/2022 om 15:27:
> It seems like the Finalizer is only kicking off the objects' finalize
> methods once the program exits, which, if I'm reading the documentation
> right, is the correct behaviour; however, I need that finalize method
> kicked off as soon as any reference to those objects is removed. Can I
> achieve that in any way?

I get the feeling that you are trying to achieve something like RAII 
("Resource acquisition is initialization") in C++. Is that correct? In 
C++ it's indeed possible, and the recommend way of doing things, to do 
finalization in the destructor which in C++ is closely tied to the 
object's scope.

In Python things work differently. The recommended way to handle 
resource management is through context managers (with 'with' 
statements). Many of the built-in classes provide context managers, as 
in this example:

 ??? with open('hello.txt', 'r') as f:
 ??????? for line in f:
 ??????????? ...

The file is automatically closed after the 'with' statement, or when an 
exception is raised. You can create context managers for your own 
classes, too. See e.g. 
https://betterprogramming.pub/everything-you-need-to-know-about-context-managers-in-python-f83556fbdfb

-- 

"Your scientists were so preoccupied with whether they could, they didn't
stop to think if they should"
         -- Dr. Ian Malcolm


From marcus.luetolf at bluewin.ch  Wed May 25 13:19:07 2022
From: marcus.luetolf at bluewin.ch (marcus.luetolf at bluewin.ch)
Date: Wed, 25 May 2022 19:19:07 +0200
Subject: [Tutor] problem solving with lists: preliminary solution
Message-ID: <000001d8705b$8acbb350$a06319f0$@bluewin.ch>

....sorry, forgott correct e_mail address.....

Hello Experts, hello dn,
In resuming my task to write code for a special constellation of the 
"SocialGolferProblem" (SGP) :
number_of_days  (number_of_weeks) = 5
number_of_flights (number_of_groupings)  = 4
seize of flight (players_per_grouping) = 4

and given the solution below (draws for week 1 through 5) I've come up with 
a code of which I only post part of it (for it gets to big):
for day 1( hardcoded)  and for day 2 (as a function to be used through day 
5).
The crucial part of my functions for day 2 to 5 are the conditional 
statements. These conditional statements do not follow a consistent pattern.
This inconsistency casts some doubt on the effectiveness of my code below 
and I'd appreciate critical comments.

My code so far:
def startlist():
    all_players = list('abcdefghijklmnop')
    n = 4 # n = seize of flight

    a_flight_day_1 = []
    b_flight_day_1 = []
    c_flight_day_1 = []
    d_flight_day_1 = []
    a_flight_day_2 = []
    b_flight_day_2 = []
    c_flight_day_2 = []
    d_flight_day_2 = []
    a_flight_day_3 = []
    b_flight_day_3 = []
    c_flight_day_3 = []
    d_flight_day_3 = []
    a_flight_day_4 = []
    b_flight_day_4 = []
    c_flight_day_4 = []
    d_flight_day_4 = []
    a_flight_day_5 = []
    b_flight_day_5 = []
    c_flight_day_5 = []
    d_flight_day_5 = []

    history = {'a':[], 
'b':[],'c':[],'d':[],'e':[],'f':[],'g':[],'h':[],'i':[],'j':[],'k':[],'l':[],'m':[],'n':[],'o':[],'p':[]}
    [a_flight_day_1.append(player)for player in all_players[0:n]]
    print('a_flight_day_1: ', a_flight_day_1)
    del all_players[0:n]
    [b_flight_day_1.append(player)for player in all_players[0:n]]
    print('b_flight_day_1: ', b_flight_day_1)
    del all_players[0:n]
    [c_flight_day_1.append(player)for player in all_players[0:n]]
    print('c_flight_day_1: ', c_flight_day_1)
    del all_players[0:n]
    [d_flight_day_1.append(player)for player in all_players[0:n]]
    print('d_flight_day_1: ', d_flight_day_1)

    history['a'].extend(a_flight_day_1)
    history['b'].extend(a_flight_day_1)
    history['c'].extend(a_flight_day_1)
    history['d'].extend(a_flight_day_1)
    history['e'].extend(b_flight_day_1)
    history['f'].extend(b_flight_day_1)
    history['g'].extend(b_flight_day_1)
    history['h'].extend(b_flight_day_1)
    history['i'].extend(c_flight_day_1)
    history['j'].extend(c_flight_day_1)
    history['k'].extend(c_flight_day_1)
    history['l'].extend(c_flight_day_1)
    history['m'].extend(d_flight_day_1)
    history['n'].extend(d_flight_day_1)
    history['o'].extend(d_flight_day_1)
    history['p'].extend(d_flight_day_1)

    def day_2_flights():
        lead_player = 'a'
        temp = history[lead_player][:]
        for i in range(n-1):
            for player in all_players:
                if player not in temp:
                    a_flight_day_2.extend(player)
                    temp.extend(history[player])
                    break
            history[lead_player].append(player)
            history[lead_player] = list(set(history[lead_player])) 
#eliminate duplicates
        a_flight_day_2.extend(lead_player)
        a_flight_day_2.sort()
        print('a_flight_day_2: ', a_flight_day_2)
        [history[pl].extend(a_flight_day_2) for pl in a_flight_day_2[1:]]

        lead_player = 'b'
        temp = history['a'][:]
        for i in range(n-1):
            for player in all_players:
                if player not in temp:
                    b_flight_day_2.extend(player)
                    temp.extend(history[player])
                    break
            history[lead_player].append(player)
            history[lead_player] = list(set(history[lead_player])) 
#eliminate duplicates
        b_flight_day_2.extend(lead_player)
        b_flight_day_2.sort()
        print('b_flight_day_2: ', b_flight_day_2)
        [history[pl].extend(b_flight_day_2) for pl in b_flight_day_2[1:]]

        lead_player = 'c'
        temp = history['a'][:]
        for i in range(n-1):
            for player in all_players:
                if player not in temp and player not in history['b']:
                    c_flight_day_2.extend(player)
                    temp.extend(history[player])
                    break
            history[lead_player].append(player)
            history[lead_player] = list(set(history[lead_player])) 
#eliminate duplicates
        c_flight_day_2.extend(lead_player)
        c_flight_day_2.sort()
        print('c_flight_day_2: ', c_flight_day_2)
        [history[pl].extend(c_flight_day_2) for pl in c_flight_day_2[1:]]

        lead_player = 'd'
        temp = history['a'][:]
        for i in range(n-1):
            for player in all_players:
                if player not in temp and player not in history['b'] and 
player not in history['c']:
                    d_flight_day_2.extend(player)
                    temp.extend(history[player])
                    break
            history[lead_player].append(player)
            history[lead_player] = list(set(history[lead_player])) 
#eliminate duplicates
        d_flight_day_2.extend(lead_player)
        d_flight_day_2.sort()
        print('d_flight_day_2: ', d_flight_day_2)
        [history[pl].extend(d_flight_day_2) for pl in d_flight_day_2[1:]]

    all_players = list('abcdefghijklmnop')
    n = 4    #n = seize of flight
    day_2_flights()

startlist()

Regards, Marcus.

-----Urspr?ngliche Nachricht-----
Von: Tutor <tutor-bounces+marcus.luetolf=bluewin.ch at python.org> Im Auftrag 
von dn
Gesendet: Montag, 21. M?rz 2022 06:35
An: tutor at python.org
Betreff: Re: [Tutor] problem solving with lists

Have I managed to understand the problem, this time?

On 21/03/2022 05.05, Dennis Lee Bieber wrote:
> On Sun, 20 Mar 2022 15:55:27 +0100, <marcus.luetolf at bluewin.ch>
> declaimed the following:
>
>>> all_letters = ?abcdefghijklmnop?
>>> number_of_lists = 5
>>> number_of_sublists per list = 4
>>> number_per_sublist = 4
>> to
>>> all_letters = ?abcdefghi?
>>> number_of_lists = 4
>>> number_of_sublists per list = 3
>>> number_per_sublist = 3
>> to
>>> all_letters = 'abcdef'.

> 	The discussion would be much easier if you gave real names to all
> those... (since you later confirm this is the SGP)
>
> 	number of weeks
> 	number of groupings
> 	players per grouping
>
> This avoids all confusion over lists, sublists, etc... "week 1",
> "group 3", "player 2".

How about these as 'meaningful names':

players = "abcdefghijklmnop"
number_of_weeks = 5
number_of_groupings = 4
players_per_grouping = 4


>> seems rather straightforward.  But for now I cannot see yet how to use it 
>> to remove all non-uniques sublists/teams.

> 	You don't "remove" them! "Remove" implies you already placed a
> grouping into the solution and now are checking for if they meet the 
> constraints.
> Instead you check the constraints for a candidate grouping BEFORE
ADDING it
> to the solution.

Yes, the more 'constructivist' approach is probably easier - it is easier to 
'see' as things are added to the solution, than it is to keep track of what 
was there 'before' when subtracting/pruning. YMMV!


This solution keeps track of each player's history, ie if player-a is 
grouped with players 'b', 'c', and 'd' in the first game, then:

player_history['a'] == {'b','c','d'}

and in the second game (s)he is grouped with players 'e', 'i', 'm'; then it 
becomes:

player_history['a'] == {'b','c','d', 'e', 'i', 'm'}

and so-on (see output-report, below)

The player's history is used whenever (s)he is 'selected' to ensure that 
there is no repetition of anyone who has participated in that player's 
previous groupings.


>> SPG exactly describes my goal.
>
> 	The SGP is not a week-end programming exercise. It is the subject of
> multiple university research papers in developing/optimizing
> constraint-based solver algorithms.
>
> 	A one-time pass over the output of .combinations() will not be
> sufficient (especially as the criteria per week that no player appears
> more than once means going from "abcd" [the first foursome
> .combinations() spits out] has to skip all other groups that begin
> with "a", "b", "c" or "d" -- and you can't get them back later. At a
> minimum you need to restart the
> .combinations() on each week. You'll really need to implement some way
> of backtracking ("we ran out of groupings without being able to place
> one ink 'this' position, back up and change the previously placed
> grouping and start over on this position") -- it is possible you might
> have to back all the way up to the first grouping and try a different 
> value for it.

Which all serves to make me think that I have yet to grasp the full-measure 
of the problem!


Herewith the output-report.

The "draw" for each week (who is playing whom during that week) appears as 
four Python-lists under the week's sub-title. (the 'real' output)

Inspecting the "draw", one can visually-check that each player only plays 
against everyone else, once - and only once - and exactly once (per @Dennis' 
comment about byes - no, none of them necessary with this combination of 
definitions)!

The 16-player listing (debug-print) underneath each week's draw, shows how 
the program[me] keeps a record of which players each player has played 
to-date - thus after each week's game it extends by another three players' 
names/letters.

Finally, by the end of the league/tournament (whatever you want to call the 
five-weeks of play, per definitions), every player is shown to have played 
against every other player:-


/usr/bin/python3 /home/dn/Projects/Experiments/marcus.py

Draw for 5 week league

Draw for week 1
['a', 'b', 'c', 'd']
['e', 'f', 'g', 'h']
['i', 'j', 'k', 'l']
['m', 'n', 'o', 'p']


a ['a', 'b', 'c', 'd']
b ['a', 'b', 'c', 'd']
c ['a', 'b', 'c', 'd']
d ['a', 'b', 'c', 'd']
e ['e', 'f', 'g', 'h']
f ['e', 'f', 'g', 'h']
g ['e', 'f', 'g', 'h']
h ['e', 'f', 'g', 'h']
i ['i', 'j', 'k', 'l']
j ['i', 'j', 'k', 'l']
k ['i', 'j', 'k', 'l']
l ['i', 'j', 'k', 'l']
m ['m', 'n', 'o', 'p']
n ['m', 'n', 'o', 'p']
o ['m', 'n', 'o', 'p']
p ['m', 'n', 'o', 'p']


Draw for week 2
['a', 'e', 'i', 'm']
['b', 'f', 'j', 'n']
['c', 'g', 'k', 'o']
['d', 'h', 'l', 'p']


a ['a', 'b', 'c', 'd', 'e', 'i', 'm']
b ['a', 'b', 'c', 'd', 'f', 'j', 'n']
c ['a', 'b', 'c', 'd', 'g', 'k', 'o']
d ['a', 'b', 'c', 'd', 'h', 'l', 'p']
e ['a', 'e', 'f', 'g', 'h', 'i', 'm']
f ['b', 'e', 'f', 'g', 'h', 'j', 'n']
g ['c', 'e', 'f', 'g', 'h', 'k', 'o']
h ['d', 'e', 'f', 'g', 'h', 'l', 'p']
i ['a', 'e', 'i', 'j', 'k', 'l', 'm']
j ['b', 'f', 'i', 'j', 'k', 'l', 'n']
k ['c', 'g', 'i', 'j', 'k', 'l', 'o']
l ['d', 'h', 'i', 'j', 'k', 'l', 'p']
m ['a', 'e', 'i', 'm', 'n', 'o', 'p']
n ['b', 'f', 'j', 'm', 'n', 'o', 'p']
o ['c', 'g', 'k', 'm', 'n', 'o', 'p']
p ['d', 'h', 'l', 'm', 'n', 'o', 'p']


Draw for week 3
['a', 'f', 'k', 'p']
['b', 'e', 'l', 'o']
['c', 'h', 'i', 'n']
['d', 'g', 'j', 'm']


a ['a', 'b', 'c', 'd', 'e', 'f', 'i', 'k', 'm', 'p'] b ['a', 'b', 'c', 'd', 
'e', 'f', 'j', 'l', 'n', 'o'] c ['a', 'b', 'c', 'd', 'g', 'h', 'i', 'k', 
'n', 'o'] d ['a', 'b', 'c', 'd', 'g', 'h', 'j', 'l', 'm', 'p'] e ['a', 'b', 
'e', 'f', 'g', 'h', 'i', 'l', 'm', 'o'] f ['a', 'b', 'e', 'f', 'g', 'h', 
'j', 'k', 'n', 'p'] g ['c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'o'] h 
['c', 'd', 'e', 'f', 'g', 'h', 'i', 'l', 'n', 'p'] i ['a', 'c', 'e', 'h', 
'i', 'j', 'k', 'l', 'm', 'n'] j ['b', 'd', 'f', 'g', 'i', 'j', 'k', 'l', 
'm', 'n'] k ['a', 'c', 'f', 'g', 'i', 'j', 'k', 'l', 'o', 'p'] l ['b', 'd', 
'e', 'h', 'i', 'j', 'k', 'l', 'o', 'p'] m ['a', 'd', 'e', 'g', 'i', 'j', 
'm', 'n', 'o', 'p'] n ['b', 'c', 'f', 'h', 'i', 'j', 'm', 'n', 'o', 'p'] o 
['b', 'c', 'e', 'g', 'k', 'l', 'm', 'n', 'o', 'p'] p ['a', 'd', 'f', 'h', 
'k', 'l', 'm', 'n', 'o', 'p']


Draw for week 4
['a', 'g', 'l', 'n']
['b', 'h', 'k', 'm']
['c', 'e', 'j', 'p']
['d', 'f', 'i', 'o']


a ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'i', 'k', 'l', 'm', 'n', 'p'] b ['a', 
'b', 'c', 'd', 'e', 'f', 'h', 'j', 'k', 'l', 'm', 'n', 'o'] c ['a', 'b', 
'c', 'd', 'e', 'g', 'h', 'i', 'j', 'k', 'n', 'o', 'p'] d ['a', 'b', 'c', 
'd', 'f', 'g', 'h', 'i', 'j', 'l', 'm', 'o', 'p'] e ['a', 'b', 'c', 'e', 
'f', 'g', 'h', 'i', 'j', 'l', 'm', 'o', 'p'] f ['a', 'b', 'd', 'e', 'f', 
'g', 'h', 'i', 'j', 'k', 'n', 'o', 'p'] g ['a', 'c', 'd', 'e', 'f', 'g', 
'h', 'j', 'k', 'l', 'm', 'n', 'o'] h ['b', 'c', 'd', 'e', 'f', 'g', 'h', 
'i', 'k', 'l', 'm', 'n', 'p'] i ['a', 'c', 'd', 'e', 'f', 'h', 'i', 'j', 
'k', 'l', 'm', 'n', 'o'] j ['b', 'c', 'd', 'e', 'f', 'g', 'i', 'j', 'k', 
'l', 'm', 'n', 'p'] k ['a', 'b', 'c', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 
'm', 'o', 'p'] l ['a', 'b', 'd', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 
'o', 'p'] m ['a', 'b', 'd', 'e', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'o', 
'p'] n ['a', 'b', 'c', 'f', 'g', 'h', 'i', 'j', 'l', 'm', 'n', 'o', 'p'] o 
['b', 'c', 'd', 'e', 'f', 'g', 'i', 'k', 'l', 'm', 'n', 'o', 'p'] p ['a', 
'c', 'd', 'e', 'f', 'h', 'j', 'k', 'l', 'm', 'n', 'o', 'p']


Draw for week 5
['a', 'h', 'j', 'o']
['b', 'g', 'i', 'p']
['c', 'f', 'l', 'm']
['d', 'e', 'k', 'n']


a ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
'o', 'p'] b ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 
'm', 'n', 'o', 'p'] c ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 
'k', 'l', 'm', 'n', 'o', 'p'] d ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] e ['a', 'b', 'c', 'd', 'e', 'f', 
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] f ['a', 'b', 'c', 'd', 
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] g ['a', 'b', 
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] h 
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
'p'] i ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
'n', 'o', 'p'] j ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 
'l', 'm', 'n', 'o', 'p'] k ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 
'j', 'k', 'l', 'm', 'n', 'o', 'p'] l ['a', 'b', 'c', 'd', 'e', 'f', 'g', 
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] m ['a', 'b', 'c', 'd', 'e', 
'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] n ['a', 'b', 'c', 
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] o ['a', 
'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'] p 
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
'p']


Process finished with exit code 0


Is this what we're trying to achieve?
--
Regards,
=dn
_______________________________________________
Tutor maillist  -  Tutor at python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


From breamoreboy at gmail.com  Wed May 25 13:35:38 2022
From: breamoreboy at gmail.com (Mark Lawrence)
Date: Wed, 25 May 2022 18:35:38 +0100
Subject: [Tutor] problem solving with lists: preliminary solution
In-Reply-To: <000001d8705b$8acbb350$a06319f0$@bluewin.ch>
References: <000001d8705b$8acbb350$a06319f0$@bluewin.ch>
Message-ID: <b75d99d6-1f7f-bc2b-2f7f-400335cff2de@gmail.com>


> 
> Which all serves to make me think that I have yet to grasp the full-measure
> of the problem!
> 
Yes. You've been told that on multiple occasions. So why do you keep 
bothering us?

-- 
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence


From PythonList at DancesWithMice.info  Wed May 25 16:01:56 2022
From: PythonList at DancesWithMice.info (dn)
Date: Thu, 26 May 2022 08:01:56 +1200
Subject: [Tutor] problem solving with lists: preliminary solution
In-Reply-To: <000001d8705b$8acbb350$a06319f0$@bluewin.ch>
References: <000001d8705b$8acbb350$a06319f0$@bluewin.ch>
Message-ID: <542f45fa-5af2-7676-d53b-87eb9bab0ff0@DancesWithMice.info>

Hi Marcus,

This is a bit of 'blast from the past' - and I haven't gone 'back' to
look at our previous correspondence/this message hasn't "threaded".

May I offer a few criticisms and suggestions:-


On 26/05/2022 05.19, marcus.luetolf at bluewin.ch wrote:
> ....sorry, forgott correct e_mail address.....
> 
> Hello Experts, hello dn,

and not forgetting @wlfraed who mentioned university research papers
dealing with the intricacies (and insolubles) of the "SGP". Did you try
following-up on any of those?


> In resuming my task to write code for a special constellation of the 
> "SocialGolferProblem" (SGP) :
> number_of_days  (number_of_weeks) = 5
> number_of_flights (number_of_groupings)  = 4
> seize of flight (players_per_grouping) = 4

Criticism: although each of these terms is perfectly understandable, and
easily converted into a 'readable' pythonic identifier, later the code
uses "n" (for example) - which of the two values commencing "*n*umber_"
is it?


> and given the solution below (draws for week 1 through 5) I've come up with 
> a code of which I only post part of it (for it gets to big):
> for day 1( hardcoded)  and for day 2 (as a function to be used through day 
> 5).

The word "hardcoded" immediately stopped me in my tracks!

The whole point of using the computer is to find 'repetition' and have
the machine/software save us from such boredom (or nit-picking detail in
which we might make an error/become bored).

That said, may I suggest that you grab a whiteboard/blackboard, or a
convenient wall and a bunch of Post-It notes, and try to solve the
problem manually - first for week-one (which is largely trivial), but
then recording the 'history' and moving-on to the extra decision-layer
for week-two. If your brain doesn't go 'click'*, go on to deal with
week-three and see how the algorithm develops...

* keeping my 'pocket handkerchief' lawn tidy doesn't require 'heavy
machinery', but after many years of service my little electric mower
'died'. I bought a new Bosch model which needed to be partly assembled
from the box. The accompanying manual featured explanations in dozens of
languages. However, the (single/all-language) diagram showing where the
back-wheels were to be joined to the axles featured (only) the English
explanation: "Click". Perhaps other languages do use the word
(?spelling), but the term "going click in your mind" has long been used
for the idea explained in today's idiom as an "ahah! moment".


> The crucial part of my functions for day 2 to 5 are the conditional 
> statements. These conditional statements do not follow a consistent pattern.
> This inconsistency casts some doubt on the effectiveness of my code below 
> and I'd appreciate critical comments.
> 
...
>     a_flight_day_1 = []
>     b_flight_day_1 = []
>     c_flight_day_1 = []
...
>     history = {'a':[],
>
'b':[],'c':[],'d':[],'e':[],'f':[],'g':[],'h':[],'i':[],'j':[],'k':[],'l':[],'m':[],'n':[],'o':[],'p':[]}
...

Following-on from talking about looping (repetition), yes we need to use
multiple conditional expressions to ensure that history is taken into
account (consistently).

The other 'side' of both of these code-constructs is the data-construct.
Code-loops require data-collections! The hard-coded "a" and "day_1" made
me shudder.
(not a pretty sight - the code, nor me shuddering!)

Would you benefit from spending a little time, putting aside the SGP for
a moment, and looking at a tutorial on "nesting" lists (and
dictionaries) in Python?


Again, the above-mentioned 'whiteboard' exercise may help 'reveal' the
patterns in the data, as well as identifying an algorithm.


Sadly, the 'hard-coded' parts may 'help' sort-out week-one, but (IMHO)
have made things impossibly-difficult to proceed into week-two (etc).

(and I'm back to mentioning that I haven't searched for our previous
discussions - specifically whether we talked-about a data-construct for
'history', and also to referring-back to the 'whiteboard' suggestion)


We were warned!

The SGP can be made to work for this particular combination of
weeks/flights/players. Which gives credence to the first-thought: "it
looks so easy". However, the wider problem is more complex than one
at-first imagines!

This is the reason why 'combinatorial problems' are so interesting. Did
I say "interesting"? Perhaps "frustrating" and/or "frustratingly
difficult to solve" - or even: 'cannot (always) be solved'!

This is no 'little kitten' or some old piece of rope, you have "a tiger
by the tail"...
-- 
Regards,
=dn

From PythonList at DancesWithMice.info  Wed May 25 18:39:57 2022
From: PythonList at DancesWithMice.info (dn)
Date: Thu, 26 May 2022 10:39:57 +1200
Subject: [Tutor] problem solving with lists: preliminary solution
In-Reply-To: <542f45fa-5af2-7676-d53b-87eb9bab0ff0@DancesWithMice.info>
References: <000001d8705b$8acbb350$a06319f0$@bluewin.ch>
 <542f45fa-5af2-7676-d53b-87eb9bab0ff0@DancesWithMice.info>
Message-ID: <c1ef2c90-c917-a7ed-b940-02bf80b909b9@DancesWithMice.info>

Marcus,
(with apologies for apparently 'answering' my own post)


In one of those serendipitous quirks of life, shortly after posting
'here', someone walked-in and asked for help solving a (wait for it...)
'combinatorial problem'. So, with our discussion fresh in-mind, I
suggested to him that we stand around a white-board and puzzle-through*.
-- 
Regards,
=dn

From outlook_A315F2BB3FCE4786 at outlook.com  Sat May 28 23:53:50 2022
From: outlook_A315F2BB3FCE4786 at outlook.com (Piyush Tomar)
Date: Sun, 29 May 2022 03:53:50 +0000
Subject: [Tutor] Python Learner - Piyush
Message-ID: <VI1PR07MB5119E408E27B7A2585FAB100B6DA9@VI1PR07MB5119.eurprd07.prod.outlook.com>

Hi,

I am looking forward to learn Python and I am super excited to join this community.




Thanks
Piyush

Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows


From __peter__ at web.de  Sun May 29 12:02:30 2022
From: __peter__ at web.de (Peter Otten)
Date: Sun, 29 May 2022 18:02:30 +0200
Subject: [Tutor] Object destruction
In-Reply-To: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
References: <CAKXTG0hS3x7z-v0rAs-+nJ=z9Jv3pFyO0uxvysdA0ZsU-rZuXQ@mail.gmail.com>
Message-ID: <93fb1d90-cb7b-7e28-f001-af1fbb075f4f@web.de>

On 24/05/2022 15:27, Dimitar Ivanov wrote:
> Hi all,

Hi Dimitar!

>
> I'm trying to come up with a (working) design of tracking an object
> throughout its lifecycle but I'm unable to find quite what I'm looking for,
> so I hope you folks will be able to give me some tips.
>
> I have an object that is being created and I want to "trace" that object in
> a separate static class that other threads and objects will be able to
> access if necessary:
>
> class TestClass:
>
>      def __init__(self, name):
>          self.name = name
>          self._finalizer = weakref.finalize(self, self.finalize)

A bound method keeps a reference to an instance of the class. Therefore
this instance is never garbage-collected unless the method object is
released. In your case this method object is kept alive until the
instance is released...

>
>      def do_stuff(self):
>          print(f"Object {self.name} doing stuff")
>          Tracer.trace(self.name)
>          time.sleep(5)
>
>      def finalize(self):
>          print(f"Entered finalize method for object {self.name}")
>          Tracer.untrace(self.name)


An easy fix would be to turn the finalize() method into a function:

def finalize(name):
     print("finalizing", name)
     Tracer.untrace(name)


class TestClass:

     def __init__(self, name):
         self.name = name
         self._finalizer = weakref.finalize(self, lambda: finalize(name))

     def do_stuff(self):
         print(f"Object {self.name} doing stuff")
         Tracer.trace(self.name)
         time.sleep(5)

> Here, I have the static object that "traces" those objects:
>
> class Tracer:
>
>      traced_objects = []
>
>      @staticmethod
>      def trace(some_object):
>          Tracer.traced_objects.append(some_object)
>
>      @staticmethod
>      def untrace(some_object):
>          Tracer.traced_objects.remove(some_object)
>
> And here's my main method where I test creating the objects, kicking off
> their do_stuff methods in a thread and then deleting them and checking if
> they've been removed from the traced_objects list in the Tracer class:
>
> if __name__ == '__main__':
>      objectOne = TestClass("Object1")
>      objectTwo = TestClass("Object2")
>      thrd1 = Thread(target=objectOne.do_stuff)
>      thrd2 = Thread(target=objectTwo.do_stuff)
>      thrd1.start()
>      thrd2.start()
>      thrd1.join()
>      thrd2.join()
>      del objectOne
>      del objectTwo
>      print("Threads are done, checking Tracer's dict")
>      print(f"Tracer's dict: {Tracer.traced_objects}")
>
> It seems like the Finalizer is only kicking off the objects' finalize
> methods once the program exits, which, if I'm reading the documentation
> right, is the correct behaviour; however, I need that finalize method
> kicked off as soon as any reference to those objects is removed. Can I
> achieve that in any way?
>
> Thanks a lot in advance and please, do let me know if my explanation is
> vague and/or unclear!
>
> Regards,
> Dimitar
> _______________________________________________
> 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 May 29 21:26:49 2022
From: cs at cskk.id.au (Cameron Simpson)
Date: Mon, 30 May 2022 11:26:49 +1000
Subject: [Tutor] Python Learner - Piyush
In-Reply-To: <VI1PR07MB5119E408E27B7A2585FAB100B6DA9@VI1PR07MB5119.eurprd07.prod.outlook.com>
References: <VI1PR07MB5119E408E27B7A2585FAB100B6DA9@VI1PR07MB5119.eurprd07.prod.outlook.com>
Message-ID: <YpQdWXsWjwd3nTMJ@cskk.homeip.net>

On 29May2022 03:53, Piyush Tomar <outlook_A315F2BB3FCE4786 at outlook.com> wrote:
>I am looking forward to learn Python and I am super excited to join 
>this community.

Welcome! - Cameron Simpson <cs at cskk.id.au>