23 svar
284 visningar
Qetsiyah 6567 – Livehjälpare
Postad: 15 sep 2020 16:23

Felsökning, något fel med objekttyp när jag försöker upphöja tal


Hej, jag får errorn:

line 9, in kubfinder
b=(n-a^3)**(1/3)
TypeError: unsupported operand type(s) for ^: 'float' and 'int'

n är en float, a är ett heltal? Vad är problemet?

Laguna 30440
Postad: 15 sep 2020 16:26

^ gör inte det du tror. Upphöjt skriver man **

Qetsiyah 6567 – Livehjälpare
Postad: 15 sep 2020 16:29 Redigerad: 15 sep 2020 16:32

Nu ser min kod ut såhär:

Den tar in n och printar hej och avslutas. Betyder det att den inte går in i while loopen alls?

EDIT: när jag lägger till print(a) efter rad 9 så printar den alla talen upp till n, sedan "hej" och avslutas. Betyder det att "if" villkoret aldrig uppfylls?

Laguna 30440
Postad: 15 sep 2020 16:38

Det är nog så att flyttalsberäkningarna producerar tal som är väldigt nära heltal men inte exakt lika. Räkna med heltal hela vägen i stället, eller kolla om talens skillnad är mindre än 0,00001 eller nåt sånt.

Man ska aldrig jämföra flyttal med likhetsoperatorn. 

Lindehaven 820 – Lärare
Postad: 15 sep 2020 17:15

Som Laguna skriver så producerar flyttalsberäkningar inte alltid exakta decimala värden eftersom datorn räknar med binära värden. Vill man jämföra om två värden är tillräckligt lika, d v s lika med en viss tolerans, så kan man använda funktionen isclose() i modulen math.

Qetsiyah 6567 – Livehjälpare
Postad: 15 sep 2020 17:17

Det jag egentligen vill veta om b är ett heltal, hur kan jag testa det? med id()?

Lindehaven 820 – Lärare
Postad: 15 sep 2020 17:28

isinstance(b, int)

Lindehaven 820 – Lärare
Postad: 15 sep 2020 17:36 Redigerad: 15 sep 2020 17:36
Lindehaven skrev:

isinstance(b, int)

När jag nu tittar i din kod så inser jag att isinstance(b, int) antagligen aldrig kommer att bli True. Anledningen är att Python har s k dynamisk typning och i din kod kommer b att bli av typen float eftersom n är float och 1/3 är float. Du kanske vill prova jämföra b med int(b) istället, d v s if b == int(b)

Qetsiyah 6567 – Livehjälpare
Postad: 15 sep 2020 17:36

Såhär ser den ut nu, tycker ni att det är fint?

Lindehaven 820 – Lärare
Postad: 15 sep 2020 18:21

Vilka heltal har du testat koden med?

Qetsiyah 6567 – Livehjälpare
Postad: 15 sep 2020 18:58 Redigerad: 15 sep 2020 18:58

Ännu mer uppdaterad och ännu mer bra (helt färdig), vad tycker ni?:

Lindehaven 820 – Lärare
Postad: 15 sep 2020 19:26

Ok, nu är koden en bra bit på väg! Bra jobbat!

Några tips från en erfaren programmerare:

Ta bort alla anrop till input() och print() i dina funktioner. Låt funktionerna ta emot argument och returnera värden istället. Detta underlättar testning och återanvändning av koden. 

Samla alla anrop till input() och print() i en separat modul som får bli din applikation. Importera modulerna där funktionerna ligger från denna applikationsmodul.

Skriv testkod för att testa dina funktioner. Att automatisera tester är en kul programmeringsuppgift och ett måste om du ska kunna hålla god kvalitet. Dessutom sparar det massor av tid.

Du kan skapa en separat modul med testkod som anropar dina funktioner för att testa dem. När du börjar skriva fler moduler och funktioner så kan du börja dra nytta av testramverk som exempelvis pytest eller unittest.

Qetsiyah 6567 – Livehjälpare
Postad: 16 sep 2020 07:52
Lindehaven skrev:

Ok, nu är koden en bra bit på väg! Bra jobbat!

Tack

Ta bort alla anrop till input() och print() i dina funktioner. Låt funktionerna ta emot argument och returnera värden istället. Detta underlättar testning och återanvändning av koden. 

Samla alla anrop till input() och print() i en separat modul som får bli din applikation. Importera modulerna där funktionerna ligger från denna applikationsmodul.

Va? Jag förstår inte alls!

Skriv testkod för att testa dina funktioner. Att automatisera tester är en kul programmeringsuppgift och ett måste om du ska kunna hålla god kvalitet. Dessutom sparar det massor av tid.

Var då till exempel? Vadå test?

Aerius 504 – Fd. Medlem
Postad: 16 sep 2020 08:14

Gör en egen funktion som tar input() och returnerar giltiga användarvärden. Giltiga användarvärden skickas sedan in i kubfinder_avac_quick() som parameter. Då blir det snyggare

Lindehaven 820 – Lärare
Postad: 16 sep 2020 08:30

Funktionen kubfinder_avanc_quick() kan utökas med argument, t ex kubfinder_avanc_quick(n). Då kan funktionen anropas med det heltal man vill hitta kubsummor till. Låt sedan funktionen slutligen returnera lösningen. Ändra i funktionen så här och spara som exvis kubfinder.py:

det kubfinder_avanc_quick(n):

    a = 0

    soln = {}

    # mer kod utan input() eller print() ...

    return soln

Skriv en ny modul och kalla den exvis kubapp.py:

import kubfinder

n = int(input("Vilket positivt heltal..."))

soln = kubfinder.kubfinder_avanc_quick(n)

if soln:

    print("Jag hittade", len(soln), "lösningar: ", soln, ".")

else:

    print("Inga lösningar.")

Kör kubapp.py.

På liknande sätt som kubapp.py kan du skapa en testmodul, exvis kubfinder_test.py, och försäkra dig om att din kod gör vad du förväntar dig.  Början:

from kubfinder import *

assert kubfinder_avanc_quick(1) == []

# mer kod som testar att anrop till kubfinder_avanc_quick med olika värden på argumentet n get förväntat resultat...

Kör kubfinder_test.py som är dina automatiserade tester.

Aerius 504 – Fd. Medlem
Postad: 16 sep 2020 09:02

När du använder olika moduler i ditt program ett sätt att starta programmet är med din_python_interpreter -m program. program har inte .py på slutet. Om program ligger i en undermap blir det undermap.program, punkt istället för slash. -m är en flagga till python att program ska köras som en modul.

Qetsiyah 6567 – Livehjälpare
Postad: 17 sep 2020 11:04 Redigerad: 17 sep 2020 11:07

Okej... så du menar att jag ska ta bort alla input() och print() från kubfiner_avanc_quick och längst ner ha en "return soln" så att talet n man stoppar i förvandlas till listan soln?

Sedan en annan "modul" alltså en ny .py-fil som importerar kubfinder_avanc_quick och stoppar in n i den och sätter soln lika med listan som kubfinder ger.

Och en tredje modul som checkar svar från kubapp?


Men varför inte definiera dessa tre funktioner i samma .py fil?

Lindehaven 820 – Lärare
Postad: 17 sep 2020 11:33
Qetsiyah skrev:

Okej... så du menar att jag ska ta bort alla input() och print() från kubfiner_avanc_quick och längst ner ha en "return soln" så att talet n man stoppar i förvandlas till listan soln?

Sedan en annan "modul" alltså en ny .py-fil som importerar kubfinder_avanc_quick och stoppar in n i den och sätter soln lika med listan som kubfinder ger.

Och en tredje modul som checkar svar från kubapp?

Ja.

 

Men varför inte definiera dessa tre funktioner i samma .py fil?

För att förtydliga: moduler är inte detsamma som funktioner. Moduler är Python-filer som kan innehålla funktioner. Funktioner behöver definieras i moduler.

Syftet med att separera i olika moduler är att underlätta testning och återanvändning av funktioner.

Ett exempel:

Du vill/behöver en grafisk applikation istället för din konsolapplikation. Då kan du utveckla den grafiska applikationen utan att ändra dina funktioner eller din konsolapplikation.

När du kör tester så kanske du upptäcker fel i någon av dina funktioner. När du har rättat den så går dina tester igenom igen. Dessutom kommer både din konsolapplikation och grafiska applikation att fungera korrekt utan att du behövt ändra någon av dem.

Syftet med testmoduler är att automatisera tester. Testerna är inte en del av funktionerna eller applikationen, det som kallas "produktionskod". Det är därför lämpligt att hålla dessa tester i separata testmoduler.

Laguna 30440
Postad: 17 sep 2020 12:55

Några små synpunkter:

Vad händer om användaren matar in nåt som inte är ett tal? Är det det som ska hända?

Du kan skriva n <= 0 i stället för n<0 or n==0.

elif len(soln) > 0 är onödigt, för du vet att längden är större än 0 i det läget, så du kan använda bara else.

Laguna 30440
Postad: 17 sep 2020 20:50

En synpunkt till, och det här är inte bara estetiskt: jag provade din kod med n = 2662, och den hittade inte 113+113. Det beror på att 1331**(1/3) inte blir exakt 11, utan lite mindre. Det är vad man kan vänta sig av flyttal, så man ska inte göra så.

Lindehaven 820 – Lärare
Postad: 18 sep 2020 07:53

from kubfinder import *

assert kubfinder_avanc_quick(1) == []

assert qube_finder(2662) == [[11, 11]] # svaret på n = 2662 förväntas bli [[11, 11]]

# mer kod som testar att anrop till kubfinder_avanc_quick med olika värden på argumentet n get förväntat resultat...

Aerius 504 – Fd. Medlem
Postad: 18 sep 2020 09:46

Svårt med float eftersom float inte blir exakta decimaltal. Kanske går med en brute force metod. Testa alla kombinationer av tal mindre än n om kubsumman blir n. Behöver testa högst tredje roten ur n så det kanske inte blir för många tester.

Laguna 30440
Postad: 18 sep 2020 12:32

Att ersätta en linjär algoritm med en kvadratisk är oftast dåligt. Man får se till att hantera flyttalen på rätt sätt bara. 

Lindehaven 820 – Lärare
Postad: 18 sep 2020 16:16

Jag har kodat utan "brute force" och har sannolikt något som är korrekt. För vissa värden på n får man fler än ett par termer. Har testat så här:

assert kubfinder_avanc_quick(2) == [[1, 1]]
assert kubfinder_avanc_quick(9) == [[1, 2]]
assert kubfinder_avanc_quick(16) == [[2, 2]]
assert kubfinder_avanc_quick(28) == [[1, 3]]
assert kubfinder_avanc_quick(35) == [[2, 3]]
assert kubfinder_avanc_quick(54) == [[3, 3]]
assert kubfinder_avanc_quick(65) == [[1, 4]]
assert kubfinder_avanc_quick(72) == [[2, 4]]
assert kubfinder_avanc_quick(91) == [[3, 4]]
for n in range(92, 126):
    assert kubfinder_avanc_quick(n) == []
assert kubfinder_avanc_quick(126) == [[1, 5]]
assert kubfinder_avanc_quick(128) == [[4, 4]]
assert kubfinder_avanc_quick(1001) == [[1, 10]]
assert kubfinder_avanc_quick(1729) == [[1, 12], [9, 10]]
assert kubfinder_avanc_quick(2000) == [[10, 10]]
assert kubfinder_avanc_quick(2662) == [[11, 11]]
assert kubfinder_avanc_quick(4104) == [[2, 16], [9, 15]]
assert kubfinder_avanc_quick(9000) == [[10, 20]]
assert kubfinder_avanc_quick(9990) == [[9, 21]]

Svara
Close