9.5. Function Scope

9.5.1. Rationale

  • Functions has access to global values

>>> def add(a, b=1):
...     c = 0
>>>
>>>
>>> print(a)
Traceback (most recent call last):
NameError: name 'a' is not defined
>>> print(b)
Traceback (most recent call last):
NameError: name 'b' is not defined
>>> print(c)
Traceback (most recent call last):
NameError: name 'c' is not defined
>>>
>>> add(1)
>>>
>>> print(a)
Traceback (most recent call last):
NameError: name 'a' is not defined
>>> print(b)
Traceback (most recent call last):
NameError: name 'b' is not defined
>>> print(c)
Traceback (most recent call last):
NameError: name 'c' is not defined

9.5.2. Outer Scope

  • Functions has access to global values

>>> data = [1, 2, 3]
>>>
>>>
>>> def add():
...     return sum(data)
>>>
>>>
>>> add()
6
>>> print(data)
[1, 2, 3]

9.5.3. Shadowing

>>> data = [1, 2, 3]
>>>
>>>
>>> def add():
...     data = [10, 20, 30]  # Shadows name 'data' from outer scope
...     return sum(data)
>>>
>>>
>>> add()
60
>>> print(data)
[1, 2, 3]

9.5.4. Global

  • Allows modification of global variable

  • BAD PRACTICE!!

>>> data = [1, 2, 3]
>>>
>>>
>>> def add():
...     global data
...     data = [10, 20, 30]
...     return sum(data)
>>>
>>>
>>> add()
60
>>> print(data)
[10, 20, 30]

9.5.5. Pure Function

>>> def add(a, b):
...     return a + b
>>>
>>>
>>> add(1, 2)
3
>>> add(1, 2)
3
>>> add(1, 2)
3

9.5.6. Impure Function

>>> c = 3
>>>
>>>
>>> def add(a, b):
...     return a + b + c
>>>
>>>
>>> add(1, 2)
6
>>> add(1, 2)
6
>>> add(1, 2)
6
>>>
>>> c = 4
>>>
>>> add(1, 2)
7
>>> add(1, 2)
7
>>> add(1, 2)
7

9.5.7. Impure to Pure Function

>>> c = 3
>>>
>>>
>>> def add(a, b, c):
...     return a + b + c
>>>
>>>
>>> add(1, 2, c)
6
>>> add(1, 2, c)
6
>>> add(1, 2, c)
6
>>>
>>> c = 4
>>>
>>> add(1, 2, c)
7
>>> add(1, 2, c)
7
>>> add(1, 2, c)
7

9.5.8. Global Scope

>>> # doctest: +SKIP
... globals()
{'__name__': '__main__',
 '__doc__': None,
 '__package__': None,
 '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
 '__spec__': None,
 '__annotations__': {},
 '__builtins__': <module 'builtins' (built-in)>}
>>> # doctest: +SKIP
... dir(globals()['__builtins__'])
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning',
 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning',
 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError',
 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError',
 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError',
 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError',
 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError',
 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError',
 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError',
 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError',
 '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__',
 '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin',
 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod',
 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir',
 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format',
 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id',
 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list',
 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open',
 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed',
 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum',
 'super', 'tuple', 'type', 'vars', 'zip']
>>> firstname = 'Mark'
>>> lastname = 'Watney'
>>>
>>> # doctest: +SKIP
... globals()
{'__name__': '__main__',
 '__doc__': None,
 '__package__': None,
 '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
 '__spec__': None,
 '__annotations__': {},
 '__builtins__': <module 'builtins' (built-in)>,
 'firstname': 'Mark',
 'lastname': 'Watney'}

9.5.9. Local Scope

  • Variables defined inside function

  • Variables are not available from outside

  • If outside the function, will return the same as globals()

>>> # doctest: +SKIP
... locals()
{'__name__': '__main__',
 '__doc__': None,
 '__package__': None,
 '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
 '__spec__': None,
 '__annotations__': {},
 '__builtins__': <module 'builtins' (built-in)>}
>>> def echo():
...     a = 1
...     print(locals())
>>>
>>>
>>> echo()
{'a': 1}
>>> def echo(a, b=2):
...     c = 3
...     print(locals())
>>>
>>>
>>> echo(1)
{'a': 1, 'b': 2, 'c': 3}

9.5.10. Shadowing Global Scope

  • Stubs in tests

>>> def input(__prompt):
...     """Stub user input, for testing purpose only"""
...     return '44'
>>>
>>>
>>> age = input('Type your age: ')
>>> age
'44'
>>> float(age)
44.0

9.5.11. Assignments

Code 9.12. Solution
"""
* Assignment: Function Scope Global
* Complexity: easy
* Lines of code: 5 lines
* Time: 8 min

English:
    1. Use data from "Given" section (see below)
    2. Define in global scope `SELECT: set[str]` with values: `'setosa', 'versicolor'`
    3. Define function `sumif(features, label)`
    4. Function sums `features`, only when `label` is in `SELECT`
    5. When `label` is not in `select` return `0` (zero)
    6. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Zdefiniuj w przestrzeni globalnej `SELECT: set[str]` z wartościami: `'setosa', 'versicolor'`
    3. Zdefiniuj funkcję `sumif(features, label)`
    4. Funkcja sumuje `features`, tylko gdy `label` jest w `SELECT`
    5. Gdy `label` nie występuje w `select` zwróć `0` (zero)
    6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> from inspect import isfunction
    >>> isfunction(sumif)
    True
    >>> sum(sumif(X,y) for *X, y in DATA[1:])
    49.1
"""


# Given
DATA = [
    ('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
    (5.8, 2.7, 5.1, 1.9, 'virginica'),
    (5.1, 3.5, 1.4, 0.2, 'setosa'),
    (5.7, 2.8, 4.1, 1.3, 'versicolor'),
    (6.3, 2.9, 5.6, 1.8, 'virginica'),
    (6.4, 3.2, 4.5, 1.5, 'versicolor'),
    (4.7, 3.2, 1.3, 0.2, 'setosa'),
]