8.5. Function Scope¶
8.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
8.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]
8.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]
8.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]
8.5.5. Pure Function¶
>>> def add(a, b):
... return a + b
>>>
>>>
>>> add(1, 2)
3
>>> add(1, 2)
3
>>> add(1, 2)
3
8.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
8.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
8.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'}
8.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}
8.5.10. Assignments¶
"""
* Assignment: Function Scope Global
* Filename: function_scope_global.py
* 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')]
"""
* Assignment: Function Scope Roman to Int
* Filename: function_scope_romanint.py
* Complexity: hard
* Lines of code: 15 lines
* Time: 21 min
English:
1. Use data from "Given" section (see below)
2. Define function converting roman numerals to integer
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Zdefiniuj funkcję przeliczającą liczbę rzymską na całkowitą
Tests:
>>> from inspect import isfunction
>>> isfunction(roman_to_int)
True
>>> int_to_roman(1)
'I'
>>> int_to_roman(9)
'IX'
>>> int_to_roman(1550)
'MDL'
>>> int_to_roman(1540)
'MXDL'
>>> int_to_roman(14)
'XIV'
"""
# Given
CONVERSION = {
1: 'I',
2: 'II',
3: 'III',
4: 'IV',
5: 'V',
6: 'VI',
7: 'VII',
8: 'VIII',
9: 'IX',
10: 'X',
20: 'XX',
30: 'XXX',
40: 'XL',
50: 'L',
60: 'LX',
70: 'LXX',
80: 'LXXX',
90: 'XC',
100: 'C',
500: 'D',
1000: 'M'}
def int_to_roman(number):
...
""""
* Assignment: Function Scope Int To Roman
* Filename: function_scope_introman.py
* Complexity: hard
* Lines of code: 13 lines
* Time: 21 min
English:
1. Use data from "Given" section (see below)
2. Define function converting integer to roman numerals
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Zdefiniuj funkcję przeliczającą liczbę całkowitą na rzymską
Tests:
>>> from inspect import isfunction
>>> isfunction(roman_to_int)
True
>>> roman_to_int('I')
1
>>> roman_to_int('IX')
9
>>> roman_to_int('MDL')
1550
>>> roman_to_int('MXDL')
1540
>>> roman_to_int('XIV')
14
"""
# Given
ROMAN = {
'I': 1,
'II': 2,
'III': 3,
'IV': 4,
'V': 5,
'VI': 6,
'VII': 7,
'VIII': 8,
'IX': 9,
'X': 10,
'XX': 20,
'XXX': 30,
'XL': 40,
'L': 50,
'LX': 60,
'LXX': 70,
'LXXX': 80,
'XC': 90,
'C': 100,
'D': 500,
'M': 1000,
}
def roman_to_int(roman: str) -> int:
...