Back to the topic

The mark scheme starts on a new page, so you can print pages one and two for a student and keep the last page yourself. Everything prints in black on white whichever theme you are using.

Python · Advanced 3

Closures, Decorators and Context Managers

Section A: 10 multiple choice questions, 10 marks. Section B: 5 written questions, 21 marks. Total 31 marks.

Name

Class

Date

Mark  / 31

Section A

Circle the letter of the correct answer. One mark each.

  1. 1What is a closure?1
    1. AA function that closes a file
    2. BA function that remembers variables from the scope it was defined in
    3. CA function that has no return value
    4. DA function with no parameters
  2. 2What does nonlocal count do inside an inner function?1
    1. APrevents count being used
    2. BMakes count a global variable
    3. CMakes assignments to count change the enclosing function's variable
    4. DDeletes count
  3. 3A function make_counter() returns a closure with its own count. If you call it twice, what do the two closures share?1
    1. AThe same count
    2. BNothing: each has its own count
    3. COnly the function name
    4. DThe return value
  4. 4What does @my_decorator above def f(): mean?1
    1. Amy_decorator = f(my_decorator)
    2. Bf is deleted
    3. Cf runs my_decorator once
    4. Df = my_decorator(f)
  5. 5Why is functools.wraps used in a decorator?1
    1. ATo keep the original function's name and docstring on the wrapper
    2. BTo make the function run faster
    3. CTo make the function private
    4. DTo catch exceptions
  6. 6What does *args, **kwargs allow a wrapper function to do?1
    1. AAccept and pass on any arguments
    2. BAccept only keyword arguments
    3. CSkip the original function
    4. DReturn several values
  7. 7What is the extra layer needed for a decorator such as @repeat(3)?1
    1. AA global variable to hold the 3
    2. BA second @ line
    3. CA function that takes the argument and returns the actual decorator
    4. DA class to wrap the function
  8. 8When does the __exit__ method of a context manager run?1
    1. AOnly if no exception occurred
    2. BWhen the program ends
    3. CAt the end of the with block, even if an exception occurred
    4. DAt the start of the block
  9. 9In a database transaction used in a with block, what happens if an exception is raised inside the block?1
    1. AThe changes are rolled back
    2. BThe changes are committed
    3. CThe changes are kept but not saved
    4. DThe database is deleted
  10. 10What is a callback?1
    1. AAn error handler for files
    2. BA function that returns its arguments
    3. CA function that calls itself
    4. DA function passed to other code, to be called later

Section B

Answer in the space provided. The number of marks tells you how many separate points to make.

  1. 1ExplainExplain what is meant by a closure in Python.3
  2. 2StateThe program shown is run. State the output and explain why the second and third values printed are different.4
    Python
    def make_counter():
        count = 0
        def counter():
            nonlocal count
            count += 1
            return count
        return counter
    
    a = make_counter()
    b = make_counter()
    print(a(), a(), b())
  3. 3WriteWrite a decorator called log_calls that prints the name of the function it decorates each time that function is called, and then calls the function and returns its result. The decorated function may have any parameters.5
  4. 4ExplainExplain why using a with statement to open a file is better than calling open and close separately.3
  5. 5DiscussDiscuss the advantages and disadvantages of using decorators in a large program.6

Mark scheme

Section A

1B2C3B4D5A6A7C8C9A10D

Section B

1 Explain what is meant by a closure in Python. 3

One mark for each of the following, to a maximum of 3.

  • A closure is a function defined inside another function
  • It uses variables from the enclosing function
  • It keeps (remembers) those variables even after the outer function has finished

Model answer. A closure is a function that is defined inside another function and uses variables from the enclosing function. It keeps, or remembers, those variables even after the outer function has finished running, so the inner function can carry on using them when it is called later.

2 The program shown is run. State the output and explain why the second and third values printed are different. 4

One mark for each of the following, to a maximum of 4.

  • The output is 1 2 1
  • Each call of make_counter creates a new function with its own variable count
  • a() adds 1 to the count that belongs to a, so the second call gives 2
  • b has its own count, which starts again at 0, so its first call gives 1

Model answer. The output is 1 2 1. Each call of make_counter creates a new inner function with its own variable count. The first two calls to a each add 1 to the count that belongs to a, giving 1 and then 2. The function b has its own separate count, which starts at 0, so its first call gives 1.

3 Write a decorator called log_calls that prints the name of the function it decorates each time that function is called, and then calls the function and returns its result. The decorated function may have any parameters. 5

One mark for each of the following, to a maximum of 5.

  • A function that takes a function as a parameter
  • Defines a wrapper function inside it which accepts any arguments (*args, **kwargs)
  • The wrapper prints the name of the function
  • The wrapper calls the original function with the arguments and returns its result
  • The decorator returns the wrapper

Model answer.

Python
def log_calls(func):
    def wrapper(*args, **kwargs):
        print("calling", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@log_calls
def add(a, b):
    return a + b

print(add(2, 3))

log_calls takes the function as a parameter and defines a wrapper inside it. The wrapper accepts any arguments, prints the function's name, calls the original function with those arguments and returns what it returns. log_calls then returns the wrapper, which replaces the original function when the @ syntax is used.

4 Explain why using a with statement to open a file is better than calling open and close separately. 3

One mark for each of the following, to a maximum of 3.

  • The file is closed automatically at the end of the with block
  • This happens even if an error (exception) occurs inside the block
  • With separate open and close, the close call could be skipped, leaving the file open and data possibly not saved

Model answer. With a with statement the file is closed automatically as soon as the block ends, and this happens even if an error occurs inside the block. If open and close are called separately, an error between them can mean the close call is never reached, or the programmer may forget it, leaving the file open so that data may not be saved and the file may stay locked.

5 Discuss the advantages and disadvantages of using decorators in a large program. 6

One mark for each of the following, to a maximum of 6.

  • A decorator lets the same extra behaviour, such as logging, timing or checking, be added to many functions without repeating the code
  • The original function stays simple and focused on its own job (separation of concerns)
  • The behaviour can be added or removed by adding or removing one line
  • Disadvantage: the behaviour is hidden above the function so a reader may not realise it is there, and it can be harder to follow
  • Disadvantage: the extra layer of function calls can make debugging more difficult and slightly slower, and a poorly written one can lose the function's name
  • A reasoned conclusion: worthwhile for behaviour shared across many functions, but should be kept small and documented

Model answer. A decorator lets the same extra behaviour, such as logging, timing, checking arguments or caching, be added to many functions without repeating the code in each, so the program is shorter and the code is easier to maintain. Each original function stays simple and concentrates on its own job, and the extra behaviour can be added or removed by adding or removing a single line. There are disadvantages. The behaviour is hidden above the function, so a reader may not realise it is there, which can make the code harder to follow. The extra layer of function calls can also make debugging more difficult and slightly slower, and a badly written decorator can lose the original function's name and documentation. Decorators are therefore worthwhile for behaviour shared by many functions, provided each is kept small and documented.