Opciones de anotación
Hay algunas opciones que son comunes a todas las anotaciones. En el último módulo, aprendió las opciones success_message
y failure_message
. En este módulo, analizaremos tres opciones más que se pueden aplicar a las anotaciones y cómo se pueden usar.
name
La opción name
se usa para agrupar diferentes instancias de clases de anotación que representan la misma anotación. Esta opción se usa para evitar que los mensajes se muestren varias veces cuando no es necesario. Veamos este ejemplo: la función maximum
que se muestra a continuación simplemente llama a la función max
de Python para comprobar que el alumno ha identificado correctamente el máximo, pero se imprime un mensaje de resultado correcto para cada entrada en la que se prueba la función.
>>> max_ref = []
>>> def maximum(l, track=False):
... m = max(l)
... if track:
... max_ref.append(pybryt.Value(
... m,
... success_message="Found the max!",
... failure_message="Did not find the max",
... ))
... return m
>>> test_lists = [[1, 2, 3], [-1, 0, 1], [10, -4, 2, 0], [1]]
>>> for test_list in test_lists:
... maximum(test_list, track=True)
>>> max_ref = pybryt.ReferenceImplementation("maximum", max_ref)
>>> with pybryt.check(max_ref):
... for test_list in test_lists:
... maximum(test_list)
REFERENCE: maximum
SATISFIED: True
MESSAGES:
- Found the max!
- Found the max!
- Found the max!
- Found the max!
El problema con esta función es simple: la anotación que se crea en cada prueba está comprobando básicamente lo mismo: si el alumno devolvió el valor correcto. Tener el mismo mensaje impreso varias veces hace que parezca que las anotaciones están probando condiciones diferentes y sobrecarga el informe generado por PyBryt. Podemos contraer todos estos mensajes juntos asignando un nombre a la anotación creada en la función maximum
:
>>> max_ref = []
>>> def maximum(l, track=False):
... m = max(l)
... if track:
... max_ref.append(pybryt.Value(
... m,
... name="list-maximum",
... success_message="Found the max!",
... failure_message="Did not find the max",
... ))
... return m
>>> test_lists = [[1, 2, 3], [-1, 0, 1], [10, -4, 2, 0], [1]]
>>> for test_list in test_lists:
... maximum(test_list, track=True)
>>> max_ref = pybryt.ReferenceImplementation("maximum", max_ref)
>>> with pybryt.check(max_ref):
... for test_list in test_lists:
... maximum(test_list)
REFERENCE: maximum
SATISFIED: True
MESSAGES:
- Found the max!
Ahora, podemos ver que el mensaje solo se imprime una vez.
Cuando PyBryt contrae las anotaciones en un único mensaje, solo muestra el mensaje correcto si se cumplen todas las anotaciones del grupo de nombres. Si se produce un error en alguna prueba del grupo, se mostrará el mensaje de error. Vamos a introducir un error en maximum
para mostrarlo:
>>> def maximum(l):
... if len(l) % 2 == 0:
... m = min(l)
... else:
... m = max(l)
... return m
>>> with pybryt.check(max_ref):
... for test_list in test_lists:
... maximum(test_list)
REFERENCE: maximum
SATISFIED: False
MESSAGES:
- Did not find the max
limit
La opción limit
permite controlar cuántas copias de anotaciones con nombre se incluyen en la implementación de referencia. Esta opción ayuda a los casos en los que las funciones que generan las anotaciones se reutilizan muchas veces a lo largo de una asignación. Los casos en los que unas pocas pruebas iniciales son suficientes para comprobar la validez de la implementación al reducir el tamaño de la propia implementación de referencia.
Lo vamos a ilustrar con la función maximum
. Aquí, usaremos una implementación similar a la de referencia anterior, pero estableceremos limit
en cinco anotaciones y lo probaremos en varias listas de entrada.
>>> max_ref = []
>>> def maximum(l, track=False):
... m = max(l)
... if track:
... max_ref.append(pybryt.Value(
... m,
... name="list-maximum",
... limit=5,
... success_message="Found the max!",
... failure_message="Did not find the max",
... ))
... return m
>>> for _ in range(1000):
... test_list = np.random.normal(size=100)
... maximum(test_list, track=True)
>>> print(f"Annotations created: {len(max_ref)}")
>>> max_ref = pybryt.ReferenceImplementation("maximum", max_ref)
>>> print(f"Annotations in reference: {len(max_ref.annotations)}")
Annotations created: 1000
Annotations in reference: 5
Como puede ver, la longitud de max_ref.annotations
es 5 aunque se incluyeron 1000 anotaciones en la lista que se pasa al constructor.
group
La opción group
es parecida a la opción name
en que se usa para agrupar anotaciones, pero estas anotaciones no representan necesariamente la "misma anotación", sino que se agrupan en fragmentos significativos para que determinadas partes de las referencias se puedan comprobar de una en una en lugar de todas a la vez. Esta opción puede ser útil para construir asignaciones con varias preguntas en PyBryt.
Por ejemplo, considere una asignación sencilla que pide a los alumnos que implementen una función mean
y median
. Puede dividirlo en dos preguntas como se muestra a continuación:
# Question 1
mean_ref = []
def mean(l, track=False):
size = len(l)
if track:
mean_ref.append(pybryt.Value(
size,
name="len",
group="mean",
success_message="Determined the length of the list",
))
m = sum(l) / size
if track:
mean_ref.append(pybryt.Value(
m,
name="mean",
group="mean",
success_message="Calculated the correct mean of the list",
failure_message="Did not find the correct mean of the list",
))
return m
# Question 2
median_ref = []
def median(l, track=True):
sorted_l = sorted(l)
if track:
median_ref.append(pybryt.Value(
sorted_l,
name="sorted",
group="median",
success_message="Sorted the list",
))
size = len(l)
if track:
mean_ref.append(pybryt.Value(
size,
name="len",
group="median",
success_message="Determined the length of the list",
))
middle = size // 2
is_set_size_even = size % 2 == 0
if is_set_size_even:
m = (sorted_l[middle - 1] + sorted_l[middle]) / 2
else:
m = sorted_l[middle]
if track:
mean_ref.append(pybryt.Value(
m,
name="mean",
group="mean",
success_message="Calculated the correct mean of the list",
failure_message="Did not find the correct mean of the list",
))
return m
test_lists = [[1, 2, 3], [-1, 0, 1], [10, -4, 2, 0], [1]]
for test_list in test_lists:
mean(test_list, track=True)
median(test_list, track=True)
assignment_ref = pybryt.ReferenceImplementation("mean-median", [*mean_ref, *median_ref])
Con una referencia generada como la del ejemplo anterior, podemos ofrecer a los alumnos la oportunidad de comprobar su trabajo en cada pregunta antes de pasar a la siguiente, diciéndole a PyBryt qué grupo de anotaciones debe tenerse en cuenta:
>>> with pybryt.check(assignment_ref, group="mean"):
... for test_list in test_lists:
... mean(test_list)
REFERENCE: mean-median
SATISFIED: True
MESSAGES:
- Determined the length of the list
- Calculated the correct mean of the list
>>> with pybryt.check(assignment_ref, group="median"):
... for test_list in test_lists:
... median(test_list)
REFERENCE: mean-median
SATISFIED: True
MESSAGES:
- Determined the length of the list
- Sorted the list
>>> with pybryt.check(assignment_ref):
... for test_list in test_lists:
... mean(test_list)
... median(test_list)
REFERENCE: mean-median
SATISFIED: True
MESSAGES:
- Determined the length of the list
- Calculated the correct mean of the list
- Sorted the list