dbwebb-se / python

Coursematerial for python
Other
34 stars 33 forks source link

Återkoppling på students kod för marvin3 och lab5 uppgift 2.4 #53

Closed AndreasArne closed 3 years ago

AndreasArne commented 3 years ago

Marvin3

Du har väldigt bra kod, det känns som att din lösningar är genomtänkta och du anpassar koden efter lösningen du vill ha.

Jag gillar att du använder dig av if-name-main i inventory.py för att testa dina funktioner.

if __name__ == "__main__":
    bag = ["blomma","pengar","mobil"]
    inventory(bag)

    print(bag)
    pick(bag, "karta")
    print(bag)
    pick(bag, "stövlar", 10)
    print(bag)
    pick(bag, "jacka", 4)
    print(bag)
    print()

    swap(bag,"pengar","mobil")
    print(bag)
    print()

    drop(bag, "blomma")
    print(bag)
    print()

inv kommandot

Väldigt bra lösning för att få in extrauppgiften i funktion inventory. Vi kan dock göra den lite bättre. Nu har vi följande kod:

def inventory(bag_list, start_str=None, stop_str=None):
    """ 
    visar innehållet i ryggsäcken
    """
    if start_str is None:
        start = 0
    else:
        start = int(start_str)

    if stop_str is None:
        stop = len(bag_list)
    else:
        stop = int(stop_str)

    temp_list = bag_list[start:stop]
    print(f"ryggsäcken innehåller {len(temp_list)} saker")
    print(temp_list)
    for item in temp_list:
        print(item)

I uppgiften står inget om vad vi måste ha som default värde på parametrarna för start och stop. Du använder None vilket är helt OK att använda och något jag föreslog på genomgången eller föreläsningen. Men med din lösning kan vi göra den bättre om vi ändrar default värdet.

Vi vill lyckas ta bort följande kod:

    if start_str is None:
        start = 0
    else:
        start = int(start_str)

Det är ingen dålig lösning i sig. Men om vi förutsätter att start_str argumentet som skickas in redan är ett heltal, det förutsätter att vi gör om till heltal i main.py, då kan vi sätta 0 som default värde istället. Då behövs inte if-satsen. start_str kommer då alltid innehålla ett heltal och det är 0 om inget argument skickas och om värdet skickas då kan vi använda det som det är. Jag förstår om det inte har varit självklart att ni får lösen uppgiften så här, det är inte helt tydligt i uppgiften om ni får göra om värdet till heltal i main.py eller om ni måste göra det i inventory funktionen.

För stop värdet är det inte lika självklart hur vi kan bli av med if-satsen. Om vi också här förutsätter att argumentet som skickas in redan är ett heltal kan vi åtminstone förkorta den till:

    if stop_str is None:
        stop_str = len(bag_list)

Notera att jag inte längre använder stop som variabelnamn utan ändrar värdet i stop_str. Jag planerade först att lämna lösningen så här men för skoj skull testade jag vad som hände om man använder None som stop värde i slice. Det visade sig att None får samma effekt som att inte skriva något värde, med andra ord inkluderas alla värden fram till och med sista. T.ex. [1, 2, 3, 4][1:None] returnerar [2, 3, 4].

Med denna kunskapen kan vi ta bort if-satsen helt. Då ser funktionen ut som följande (jag bytte namn på start_str till start och stop_str till stop då de inte längre ska hålla strängar.):

def inventory(bag_list, start=0, stop=None):
    """ 
    visar innehållet i ryggsäcken
    """
    temp_list = bag_list[start:stop]
    print(f"ryggsäcken innehåller {len(temp_list)} saker")
    print(temp_list)
    for item in temp_list:
        print(item)

och i main.py har vi:

    if len(choice_list) == 1:
        inventory.inventory(bag_list)
    else:
        start = int(choice_list[1])
        stop = int(choice_list[2])
        inventory.inventory(bag_list, start, stop)

pick kommandot

pick funktionen gillar jag också, det är smart att sätta i till längden av listan och på så sätt kunna använda insert för båda fallen. Men funktionen kanske är lite klurig att få en bra och snabb överblick utav. Jag vill minska antalet if-satser i koden och nu har du en if-sats som inte alltid behövs. Nu har vi funktionen:

    if x is None:
        i = len(bag_list)
    else:
        i = int(x)

    if i > len(bag_list):
        print(f"Error: index {i} är 'out of range'")
        return bag_list

    bag_list.insert(i, item)
    if x is None:
        print(f"{item} har lagts in i ryggsäcken")
    else:
        print(f"{item} har lagts in i ryggsäcken på position {i}")
    return bag_list

Vi noterar att koden:

    if i > len(bag_list):
        print(f"Error: index {i} är 'out of range'")
        return bag_list

Bara behövs om vi ska lägga in på specifikt index. Så vi börjar med att flytta in den i första else-blocket.

    if x is None:
        i = len(bag_list)
    else:
        i = int(x)
        if i > len(bag_list):
            print(f"Error: index {i} är 'out of range'")
            return bag_list

    # inte ändrat koden nedanför
    bag_list.insert(i, item)
    if x is None:
        print(f"{item} har lagts in i ryggsäcken")
    else:
        print(f"{item} har lagts in i ryggsäcken på position {i}")
    return bag_list

Nästa sak jag vill fokusera på är att du anropar len(bag_list) två gånger. Vid första anblick tänkte jag att vi bara ska flytta ut i = len(bag_list) ovanför if x is None: men sen insåg jag att då funkar det inte mer resten av koden och hur du använder i i insert(). Men jag föredrar att inte anropa len() två gånger och vi kan få till ett snygg flöde i koden. Då kan vi dock inte använda insert() för både fallet där användaren inte skickar med och skickar med index.

Nu kommer jag göra om koden så att den har tre tydliga flöden.

    if x is not None:
        i = int(x)
        if i > len(bag_list):
            print(f"Error: index {i} är 'out of range'")
            return bag_list

        bag_list.insert(i, item)
        print(f"{item} har lagts in i ryggsäcken på position {i}")
        return bag_list

    bag_list.append(item)
    print(f"{item} har lagts in i ryggsäcken")
    return bag_list

Så, nu har vi tre flöden som varje slutar med en return. Vi är inte vara lika smarta med att återanvända variabler men vi kan istället ta bort lite kod och få ett tydligare flöde.

swap kommandot

Jättebra lösning, jag gillar att du skapade en ny funktion och återanvänder den. Nu har vi följande kod:

def swap(bag_list, item1, item2):
    """
    byter plats på två element i ryggsäcken
    """
    i1 = find_index(bag_list, item1)
    i2 = find_index(bag_list, item2)
    if (i1 < 0) or (i2 < 0):
        return bag_list

    if item1 == item2:
        print(f"Error: {item1} och {item2} har samma värde")
        return bag_list

    temp = bag_list[i1]
    bag_list[i1] = bag_list[i2]
    bag_list[i2] = temp

    print(f"{item1} och {item2} har nu bytt plats")
    return bag_list

def find_index(bag_list, item):
    """
    returnerar position för angivet item
    """
    try:
        return bag_list.index(item)
    except ValueError:
        print(f"Error: {item} finns inte i ryggsäcken")
        return -1

Vi kan skriva om slutet med hjälp av tuple unpacking, vilket du inte visste om när du skrev uppgiften, men nu borde du veta vad det är.

Följande:

    temp = bag_list[i1]
    bag_list[i1] = bag_list[i2]
    bag_list[i2] = temp

Blir:

bag_list[i1], bag_list[i2] = bag_list[i2], bag_list[i1]

drop kommandot

Jättebra lösning, gör precis det den ska med minimal kod. Nu har vi följande kod:

def drop(bag, what):
    """inv drop command"""
    try:
        bag.remove(what)
        print(f"'{what}' has been dropped.")
    except ValueError:
        print(f"Error: There are no '{what}' in the bagpack.")

    return bag

Lab5 2.4

Här har vi uppgiften och koden. Jag pratade lite om denna uppgiften på en genomgång, i äldre python versioner kunde ni använt ",".join(tup24) för att bygga stränge. Men i någon python version tog de bort funktionaliteten att automatiskt göra om alla element till strängar innan de läggs till i strängen.

# """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
# Exercise 2.4 (1 points)
#
# Create a tuple with
#
# > (snake, 89, 9.63, bookshelf, 1)
#
# Convert it to a list and replace the second element with:
#
# > "green"
#
# Convert it back to a tuple and answer with the first three elements in a
# comma-separated string,  without an ending `,`.
#
# Write your code below and put the answer into the variable ANSWER.
#

tup24 = ("snake", 89, 9.63, "bookshelf", 1)
list24 = list(tup24)
list24[1] = "green"
tup24 = tuple(list24[0:3])

str24 = ""
for i in tup24:
    str24 += str(i) + ","

answer = str24[:-1]

ANSWER = answer

Med det jag skrev ovanför i åtanke är den löst på sånt sätt som vi kan förväntas i kursen med det vi har lärt er. Jag kommer bara på sätt vi kan snygga till den med saker som är lite överkurs och vi inte har lärt ut i kursen. Vi kollar hur det kan se ut. Jag kommer bara fokusera på följande del i koden, den översta är löst som man ska.

str24 = ""
for i in tup24:
    str24 += str(i) + ","

ANSWER = str24[:-1]

Vi vill skapa en sträng från en lista där vi måste göra om värden till strängar först. Då kan vi använda list comprehension eller map(). Båda dem används för att itterera igenom en sekvens, köra en funktion på varje element och returnera en ny lista med det som returneras från funktionen som körs på varje lista.

map()

map() tar två argument, första är funktionen som ska köras på varje element och andra är sekvensen vi kör det på.

string_list = map(str, tup24)
ANSWER = ','.join(string_list)

Vi använder map för att göra om alla element till strängar och returnerar det som en ny lista. Vi skickar in den som argument till join() funktionen.

List comprehension

List comprehension är lite mer dynamisk och kan användas till mer än map. Det är en konstruktion och inte en funktion. Det påminner om en for-loop.

Bild på strukturen

string_list = [str(value) for value in tup24]
answer = ','.join(string_list)

I vår list comprehension loopar igenom vår skevens med en for loop och anropar str() på varje element. Sen funkar det likadant som med map().

Slutsats

Vilken lösning är bäst? Jag vet inte, list comprehension är väl mer "pythonic". Om ni är intresserade kan ni läsa mer om list comprehension vs map.