註釋選項

已完成

所有注釋都有一些常見的選項;在最後一個課程模組中,您已瞭解 success_messagefailure_message 選項。 在本課程模組中,我們會討論可套用至註釋的三個選項,以及其使用方式。

name

選項 name 可用來將代表相同註釋的不同註釋類別執行個體分組在一起。 此選項主要用於防止訊息在不需要時顯示多次。 讓我們考慮下列範例:maximum 函式會呼叫 Python 的 max 函式,以檢查學生是否已正確識別出最大值,但會針對函式測試的每個輸入列印一次成功訊息。

>>> 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!

這個函式的問題很簡單:每個測試中建立的註釋基本上都會檢查相同的項目,無論學生是否傳回正確的值。 若列印相同的訊息多次,看起來會像是註釋在測試不同的條件,也會讓 PyBryt 所產生的報表雜亂。 我們可以藉由命名在 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!

現在,我們可以看到訊息只會列印一次。

若是 PyBryt 將註釋摺疊成單一訊息,則只有在滿足名稱群組中的所有註釋時,才會顯示成功訊息。 如果群組中有任何測試失敗,則會改為顯示失敗訊息。 讓我們將一個 Bug 導入 maximum 來示範:

>>> 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

選項 limit 可讓您控制參考實作中包含多少個具名註釋複本。 此選項可協助建構註釋的函式在整個指派中重複使用多次的情況。 一些初始測試就足以藉由減少參考實作本身的大小,來檢查實作的有效性。

讓我們使用 maximum 函式來說明這一點。 我們在此將使用與之前參考類似的實作,但設定 limit 為五個註釋,並在數個輸入清單上進行測試。

>>> 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

如您所見,即使傳遞至建構函式的清單中包含 1,000 個註釋,max_ref.annotations 的長度仍為 5。

group

選項 group 類似於 name 選項,因為該選項用來將註釋分組在一起,但這些註釋不一定代表「相同的註釋」;相反地,這些註釋會分組成有意義的區塊,以便一次檢查一個特定的參考部分,而不是同時檢查全部參考。 此選項對於在 PyBryt 中建構有複數問題的指派時很有用。

例如,請考慮一個作業,要求學生實作 meanmedian 函式。 您可以將該作業分成兩個問題,如下所示:

# 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])

透過上述範例建構的參考,我們可以讓學生有機會先檢查每個個別問題的工作,再繼續進行下一個問題,方法是告訴 PyBryt 要考慮的註釋群組:

>>> 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

檢定您的知識

1.

下列哪一個呼叫會限制追蹤 lst 變數的次數為五次?

2.

group 選項的用途為何?

3.

假設我們使用下列程式碼建立參考:

for i in range(10):
    pybryt.Value(foo(i), name="foo", limit=5)
    pybryt.Value(bar(i), group="foo")
    pybryt.Value(baz(i), name="baz")

參考中有多少註釋?