2022-10-21, Del A#
A1#
import math
class Rectangle():
def __init__(self, side):
self.height = 2 * side
self.width = 3 * side
self.area = self.height * self.width
self.angles = [90., 90., 90., 90.]
self.diag1, self.diag2 = 2 * [math.sqrt(self.height ** 2
+ self.width ** 2)]
def to_parallelogram(self, ang):
self.angles = [ang, 180. - ang, ang, 180. - ang]
self.diag1 = math.sqrt(self.height ** 2 + self.width ** 2
+ 2 * self.height * self.width * math.cos(ang))
self.diag2 = math.sqrt(self.height ** 2 + self.width ** 2
- 2 * self.height * self.width * math.cos(ang))
def __str__(self):
return f'Quadrilateral with angles = {self.angles}'
Betrakta koden ovan. Vilket alternativ använder korrekt terminologi för de olika komponenterna i koden?
Alternativ 1:
moduler:
math
,self
klasser:
Rectangle
funktioner:
sqrt
,cos
metoder:
__init__()
,__str__()
,diag1
,diag2
,angles
attribut:
height
,width
,area
Alternativ 2:
moduler:
math
klasser:
Rectangle
funktioner:
sqrt
,cos
,area
,to_parallelogram()
metoder:
__init__()
,to_parallelogram()
,__str__()
attribut:
height
,width
,area
,angles
,diag1
,diag2
Alternativ 3:
moduler:
math
klasser:
Rectangle
funktioner:
sqrt
,cos
metoder:
__init__()
,to_parallelogram()
,__str__()
attribut:
height
,width
,area
,angles
,diag1
,diag2
Alternativ 4:
moduler:
math
klasser:
Rectangle
funktioner:
sqrt
,cos
,to_parallelogram()
metoder:
__init__()
,__str__()
attribut:
height
,width
,area
,angles
,diag1
,diag2
,self
Rätt svar
Alternativ 3
A2#
import random as rd
import matplotlib.pyplot as plt
from math import sqrt
from turtle import Turtle
Betrakta koden ovanför. För varje påstående nedanför, avgör om det är sant eller falskt.
sqrt
ochTurtle
är en funktion respektive en klass från modulernamath
respektiveturtle
.sqrt
ochTurtle
är funktioner från modulernamath
respektiveturtle
.rd
ochplt
är funktioner från modulernarandom
respektivematplotlib.pyplot
.rd
ochplt
är import-alias till modulernarandom
respektivematplotlib.pyplot
Du måste svara rätt på alla 4 påståenden för att få 1 poäng på den här uppgiften.
Rätt svar
1: sant. 2: falskt. 3: falskt. 4: sant.
A3#
Skriv en funktion number_of_occurrences(interesting_words, text)
som undersöker hur många gånger orden i listan interesting_words
förekommer i strängen text
. Funktionen ska returnera ett lexikon med orden som nycklar och antalet förekomster som värden.
Tips: Anropet re.findall(r'[a-zA-ZåäöÅÄÖ]+', text)
returnerar en lista med alla ord i strängen text
.
Följande kod:
seasons = ['spring', 'summer', 'autumn', 'winter']
text = """The four seasons are spring, summer, autumn, and winter,
and although various areas of the United States experience drastically
different weather during these times, all portions of the country
recognize the seasons; winter in California may bring heat, and winter
in New York may bring blizzards, but both periods are nevertheless winter."""
word_to_count = number_of_occurrences(seasons, text)
print('The following seasons were mentioned in the text:')
for word, number in word_to_count.items():
print(f'\t{word} was mentioned {number} time(s)')
ska ge utskriften
The following seasons were mentioned in the text:
spring was mentioned 1 time(s)
summer was mentioned 1 time(s)
autumn was mentioned 1 time(s)
winter was mentioned 4 time(s)
Vill du testa om din lösning är korrekt? Spara din lösning i A3.py. Skapa sedan en ny fil i samma mapp, kopiera in koden nedan, spara filen som t.ex. A3_test.py och kör den.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A3']
function_name = 'number_of_occurrences'
correct_output_type = 'dictionary'
module_to_import = 're'
arg_names = ['interesting_words', 'text']
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
seasons = ['spring', 'summer', 'autumn', 'winter']
text = """The four seasons are spring, summer, autumn, and winter,
and although various areas of the United States experience drastically
different weather during these times, all portions of the country
recognize the seasons; winter in California may bring heat, and winter
in New York may bring blizzards, but both periods are nevertheless winter."""
visible_inputs = [[seasons, text]]
visible_correct_output = [{'spring': 1, 'summer': 1, 'autumn': 1, 'winter': 4}]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
interesting_words = ['how', 'you']
text = 'Hi, how are you? Are you okay?'
hidden_inputs = [[interesting_words, text]]
hidden_correct_output = [{'how': 1, 'you': 2}]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names)
Lösningsförslag
import re
def number_of_occurrences(interesting_words, text):
word2count = {}
words = re.findall(r'[a-zA-ZåäöÅÄÖ]+', text)
for word in words:
word = word.lower()
if word in interesting_words:
if word in word2count:
word2count[word] += 1
else:
word2count[word] = 1
return word2count
Vanliga fel
print
istället förreturn
Tar med alla ord, inte bara de i
interesting words
.
A4#
Skriv en funktion bounded_squares(n)
som tar ett heltal \( n > 0\) som parameter och returnerar en lista med alla positiva heltal \(p\) som uppfyller \(n^2 < p^2 < 3n^2\).
Följande kod:
print(bounded_squares(1))
print(bounded_squares(2))
print(bounded_squares(5))
ska ge utskriften
[]
[3]
[6, 7, 8]
Vill du testa om din lösning är korrekt? Spara din lösning i A4.py. Skapa sedan en ny fil i samma mapp, kopiera in koden nedan, spara filen som t.ex. A4_test.py och kör den.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A4']
function_name = 'bounded_squares'
correct_output_type = 'list'
module_to_import = 'math'
arg_names = ['n']
#-----------------------------------
def bounded_squares_correct(n):
outlist = []
p = n + 1
while p**2 < 3*n**2:
outlist.append(p)
p += 1
return outlist
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[1], [2], [5]]
visible_correct_output = [[], [3], [6, 7, 8]]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [[101], [1001]]
hidden_correct_output = [bounded_squares_correct(*n) for n in hidden_inputs]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names)
Lösningsförslag
# Förslag 1: med while-loop
def bounded_squares(n):
outlist = []
p = n + 1
while p**2 < 3*n**2:
outlist.append(p)
p += 1
return outlist
# Förslag 2: börjar med att bestämma minsta och största p som kan vara med.
def bounded_squares(n):
p_min = n+1
p_max = int(3**0.5 * n)
return [p for p in range(p_min, p_max + 1)]
Vanliga fel
print
istället förreturn
Hårdkodad övre gräns för
p
, t.ex.p=10
ellerp=100
A5#
Betrakta följande kod.
class Animal:
"""Representation of an animal.
self.species: string specifying what animal.
Examples: 'cat', 'dog', 'elephant'
self.position: tuple of x- and y-coordinates
Examples: (0, 0), (1, 0), (-4, 9)
"""
def __init__(self, species='cat', position=(0,0)):
self.species = species
self.position = position
def move_to(self, new_position):
self.position = new_position
dog = Animal('dog', (0, 1))
cat = Animal('cat', (3, 4))
print(dog)
print(cat)
cat.move_to((-1, 4))
print(cat)
När denna kod körs erhålls följande kryptiska utskrift:
<__main__.Animal object at 0x7f810813caf0>
<__main__.Animal object at 0x7f8108131be0>
<__main__.Animal object at 0x7f8108131be0>
Lägg till en metod i klassen Animal
så att samma print-satser istället producerar följande utskrift:
dog at position (0, 1)
cat at position (3, 4)
cat at position (-1, 4)
Notera att du endast får lägga till en metod i klassen. Du får INTE göra några andra ändringar i koden.
Lösningsförslag
def __str__(self):
return f'{self.species} at position {self.position}'
A6#
Skriv en funktion min_max_list()
som tar en lista med heltal som parameter och returnerar en tupel med listans minsta och största värden. Du får INTE använda något av följande:
de inbyggda funktionerna
sorted()
,min()
ochmax()
,list-metoden
sort()
,klasser eller funktioner från modulerna
collections
ellernumpy
.
Följande kod:
print(min_max_list([7, -90, 8, 34, 7, -56, 89]))
print(min_max_list([21, 17, 27, 4, 11, 15, 8, 13]))
ska ge utskriften
(-90, 89)
(4, 27)
Tips: Du kan använda följande algoritm för att ta fram listans största tal:
Tilldela listans första tal till en variabel, som vi här kallar x_max.
För nästa tal, x, i listan, om x är större än x_max, sätt x_max = x.
Upprepa steg 2 för resterande tal i listan.
När du är klar med steg 3 bör listans största värde finnas sparat i variabeln x_max. Du kan ta fram listans minsta värde på liknande sätt.
Vill du testa om din lösning är korrekt? Spara din lösning i A6.py. Skapa sedan en ny fil i samma mapp, kopiera in koden nedan, spara filen som t.ex. A6_test.py och kör den.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A6']
function_name = 'min_max_list'
correct_output_type = 'tuple'
module_to_import = 'math'
arg_names = ['lst']
forbidden_functions = ['sort', 'sorted', 'min', 'max']
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[[7, -90, 8, 34, 7, -56, 89]], [[21, 17, 27, 4, 11, 15, 8, 13]]]
visible_correct_output = [(-90, 89), (4, 27)]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [[[22, 17, 21, -3, -1, 15, -2, 13]]]
hidden_correct_output = [(-3, 22)]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
Lösningsförslag
def min_max_list(inlist):
vmin = inlist[0]
vmax = inlist[0]
for val in inlist:
if val < vmin:
vmin = val
if val > vmax:
vmax = val
return vmin, vmax
Vanliga fel
Returnerar fel datatyp, t.ex. en sträng eller en lista istället för en tupel
print
istället förreturn
A7#
Betrakta Fibonacci-talen: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …, där varje tal i följden (förutom de två första) är summan av de två föregående. Skriv en funktion fibonacci_numbers(limit)
som returnerar en lista med alla Fibonacci-tal som är mindre än eller lika med limit
.
Följande kod:
print(fibonacci_numbers(4))
print(fibonacci_numbers(13))
ska ge utskriften
[0, 1, 1, 2, 3]
[0, 1, 1, 2, 3, 5, 8, 13]
Observera att din funktion måste fungera för ett godtyckligt stort värde på parametern limit
.
Vill du testa om din lösning är korrekt? Spara din lösning i A7.py. Skapa sedan en ny fil i samma mapp, kopiera in koden nedan, spara filen som t.ex. A7_test.py och kör den.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A7']
function_name = 'fibonacci_numbers'
correct_output_type = 'list'
module_to_import = 'math'
arg_names = ['limit']
forbidden_functions = []
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[4], [13]]
visible_correct_output = [[0, 1, 1, 2, 3], [0, 1, 1, 2, 3, 5, 8, 13]]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [[1], [2], [3], [30], [1001]]
hidden_correct_output = [[0, 1, 1], [0, 1, 1, 2], [0, 1, 1, 2, 3],
[0, 1, 1, 2, 3, 5, 8, 13, 21],
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
Lösningsförslag
def fibonacci_numbers(limit):
numbers = [0]
next_number = 1
while next_number <= limit:
numbers.append(next_number)
next_number = numbers[-2] + numbers[-1]
return numbers
Vanliga fel
Hårdkodade lösningar som bara fungerar för t.ex.
limit
< 100.Antar att listan kommer innehålla som mest
limit
antal tal, vilket inte är sant förlimit
<= 3print
istället förreturn
A8#
Skriv en funktion sorted_dictionary(input_dict)
som tar ett lexikon, vars värden är listor med heltal, som parameter och returnerar ett nytt lexikon där listorna är sorterade. Det returnerade lexikonet ska ha samma nycklar som det ursprungliga lexikonet. Det ursprungliga lexikonet ska vara oförändrat efter funktionsanropet.
Följande kod:
d = {'a1': [21, 17, 22, 3], 'a2': [11, 15, 8, 13], 'a3': [7, 13, 2, 11]}
d_sorted = sorted_dictionary(d)
print('Original: ', d)
print('Sorted: ', d_sorted)
ska ge utskriften
Original: {'a1': [21, 17, 22, 3], 'a2': [11, 15, 8, 13], 'a3': [7, 13, 2, 11]}
Sorted: {'a1': [3, 17, 21, 22], 'a2': [8, 11, 13, 15], 'a3': [2, 7, 11, 13]}
Notera att din funktion ska fungera för lexikon med andra nycklar och listor än i exemplet.
Vill du testa om din lösning är korrekt? Spara din lösning i A8.py. Skapa sedan en ny fil i samma mapp, kopiera in koden nedan, spara filen som t.ex. A8_test.py och kör den.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A8']
function_name = 'sorted_dictionary'
correct_output_type = 'dictionary'
module_to_import = 'math'
arg_names = ['d']
forbidden_functions = []
err_msg_mod = 'modifies_input_dict_with_lists'
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
d = {'a1': [21, 17, 22, 3], 'a2': [11, 15, 8, 13], 'a3': [7, 13, 2, 11]}
d_sorted = {'a1': [3, 17, 21, 22], 'a2': [8, 11, 13, 15], 'a3': [2, 7, 11, 13]}
visible_inputs = [[d]]
visible_correct_output = [d_sorted]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
d2 = {'b1': [7, -90, 8], 'b2': [ 34, 7, -56, 89]}
d2_sorted = {'b1': [-90, 7, 8], 'b2': [-56, 7, 34, 89]}
hidden_inputs = [[d2]]
hidden_correct_output = [d2_sorted]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions, error_msg_if_modifies_input=err_msg_mod)
Lösningsförslag
def sorted_dictionary(input_dict):
sorted_dict = {}
for key, value in input_dict.items():
sorted_dict[key] = sorted(value)
return sorted_dict
Vanliga fel
Felaktigt användande av globala variabler
Använder list-metoden
sort()
för att sortera de befintliga listorna (istället för att skapa nya listor) och sorterar därmed även i det ursprungliga lexikonetprint
istället förreturn
A9#
Skriv en funktion alternative_splicing(seq)
som delar upp en lista seq
på ett speciellt sätt. Funktionen ska returnera en tupel med 3 listor, där varje lista innehåller vart tredje element i seq
. Närmare bestämt:
Den första returnerade listan innehåller det 1:a, 4:e, 7:e, osv., elementen i
seq
,Den andra returnerade listan innehåller det 2:a, 5:e, 8:e, osv., elementen i
seq
,Den tredje returnerade listan innehåller det 3:e, 6:e, 9:e, osv., elementen i
seq
.
Följande kod:
u = [10, 9, -2, 16, 18, -13, 12, 18, -14]
v = [-14, -20, -12, 3, -20, 7, -6, 13]
print(alternative_splicing(u))
print(alternative_splicing(v))
ska ge utskriften
([10, 16, 12], [9, 18, 18], [-2, -13, -14])
([-14, 3, -6], [-20, -20, 13], [-12, 7])
Notera att de tre returnerade listorna inte blir lika långa om antalet element i seq
inte är delbart med 3 (se körexemplet med listan v
ovan).
Vill du testa om din lösning är korrekt? Spara din lösning i A9.py. Skapa sedan en ny fil i samma mapp, kopiera in koden nedan, spara filen som t.ex. A9_test.py och kör den.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A9']
function_name = 'alternative_splicing'
correct_output_type = 'tuple'
module_to_import = 'math'
arg_names = ['u']
forbidden_functions = []
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[[10, 9, -2, 16, 18, -13, 12, 18, -14]], [[-14, -20, -12, 3, -20, 7, -6, 13]]]
visible_correct_output = [([10, 16, 12], [9, 18, 18], [-2, -13, -14]), ([-14, 3, -6], [-20, -20, 13], [-12, 7])]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
w = list(range(11, 11+13))
hidden_inputs = [[w]]
hidden_correct_output = [([11, 14, 17, 20, 23], [12, 15, 18, 21], [13, 16, 19, 22])]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
Lösningsförslag
# Förslag 1: med list-skivning
def alternative_splicing(seq):
lst1 = seq[::3]
lst2 = seq[1::3]
lst3 = seq[2::3]
return lst1, lst2, lst3
# Förslag 2: med loop och modulo-operatorn
def alternative_splicing(seq):
lst1 = []
lst2 = []
lst3 = []
for i in range(len(seq)):
if i % 3 == 0:
lst1.append(seq[i])
elif i % 3 == 1:
lst2.append(seq[i])
else:
lst3.append(seq[i])
return lst1, lst2, lst3
Vanliga fel
Hårdkodade lösningar som bara fungerar för listor med 8 eller 9 element
Returnerar fel datatyp, t.ex. en sträng eller en lista istället för en tupel
print
istället förreturn
A10#
Skriv en funktion dna_to_rna(dna)
som transkriberar en sträng som representerar en DNA-sekvens till RNA och returnerar RNA-strängen. Du kan anta att DNA-strängen endast innehåller tecknen ’A’, ’C’, ’G’ och ’T’. För enkelhets skull antar vi också att basen tymin (T) alltid transkriberas till uracil (U) medan de andra baserna (A, C, G) inte ändras.
Följande kod:
dna = 'GTCCCTAGGGGACTCACAATTGAAGTGGCA'
rna = dna_to_rna(dna)
print('DNA: ', dna)
print('RNA: ', rna)
ska ge utskriften
DNA: GTCCCTAGGGGACTCACAATTGAAGTGGCA
RNA: GUCCCUAGGGGACUCACAAUUGAAGUGGCA
Vill du testa om din lösning är korrekt? Spara din lösning i A10.py. Skapa sedan en ny fil i samma mapp, kopiera in koden nedan, spara filen som t.ex. A10_test.py och kör den.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A10']
function_name = 'dna_to_rna'
correct_output_type = 'string'
module_to_import = 'math'
arg_names = ['dna']
forbidden_functions = []
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [['GTCCCTAGGGGACTCACAATTGAAGTGGCA']]
visible_correct_output = ['GUCCCUAGGGGACUCACAAUUGAAGUGGCA']
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [['ACAATTGATG']]
hidden_correct_output = ['ACAAUUGAUG']
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
Lösningsförslag
# Förslag 1: med loop över alla tecken
def dna_to_rna(dna):
rna = ''
for char in dna:
if char == 'T':
rna += 'U'
else:
rna += char
return rna
# Förslag 2: med sträng-metoden replace
def dna_to_rna(dna):
rna = dna.replace('T', 'U')
return rna
Vanliga fel
Felaktigt användande av globala variabler
print
istället förreturn