# Python Concepts/Built-in Functions

## Lesson

 The Python interpreter has a number of functions and types built into it that are always available. This means that you don't have to import anything so that you can access them. Some of the built-in functions are so powerful, simple, common or useful, such as `print()` that it's difficult to imagine a block of code without them. As we have advanced through this course, we've seen and used many built-in functions `(bin(), chr(), complex(), dict(), format(), hex(), input())` perhaps without realizing that they have their own place and classification in the Python documentation.

### class range(start,stop[,step])

 In the python documentation the `range()` type is included under "Built-in Functions." However, `range()` is actually an immutable sequence type similar to lists and tuples. Conceptually `range()` is similar to a tuple containing a well-defined sequence of values. Both the sequence and the values may be huge, but `range()` occupies only a small amount of memory and it is very fast. Of the three arguments `stop` must be included. If `start` is not included, it defaults to ${\displaystyle 0.}$ If `step` is not included, it defaults to ${\displaystyle 1.}$ ```>>> list (range(4)) [0, 1, 2, 3] # 'stop' is not included in the range. >>> >>> list (range(4,9)) [4, 5, 6, 7, 8] >>> >>> list (range(4,37,7)) [4, 11, 18, 25, 32] >>> >>> tuple(range(3,-7,-2)) (3, 1, -1, -3, -5) >>> >>> tuple(range(3,-7,0)) Traceback (most recent call last): File "", line 1, in ValueError: range() arg 3 must not be zero >>> >>> tuple(range(3,-7,4)) () # Empty range. >>> ``` `range()` supports indexing. ```>>> range(-2,29,5)[2:5] range(8, 23, 5) >>> >>> range(-2,29,5)[2:5][:2] range(8, 18, 5) >>> >>> range(-2,29,5)[17] Traceback (most recent call last): File "", line 1, in IndexError: range object index out of range >>> range(-2,29,5)[2] 8 >>> range(-2,29,5)[-1] 28 >>> ``` An operation on `range()` may return `True` or `False.` ```>>> 6 in range(-17,193 , 7) False >>> 4 in range(-17, 193 , 7) True >>> ``` Try it with big numbers. ```>>> 100_000_000_000_000 in range ( -5_123_456_789, 1_000_000_000_000_000, 7 ) False >>> 100_000_000_000_001 in range ( -5_123_456_789, 1_000_000_000_000_000, 7 ) False >>> 100_000_000_000_002 in range ( -5_123_456_789, 1_000_000_000_000_000, 7 ) True >>> >>> len(range ( -5_123_456_789, 1_000_000_000_000_000_456_789_123, 789_012_456 )) 1267407114292763 >>> >>> len(range ( 5_123_456_789, -1_000_000_000_000_000_456_789_123, -789_012_456 )) 1267407114292763 >>> >>> a,b,c = 5_123_456_789, -1_000_000_000_000_000_456_789_123, -789_012_456 >>> d,e,f = 407114292763 , 267407114292763 , 674071142 >>> range(a,b,c)[d:e:f] range(-321218248000514199139, -210987544000000514199139, -531850527268144752) >>> len(range(a,b,c)[d:e:f]) 396_101 >>> range(a,b,c)[d:e:f][763::2345][-3] -207760474949976816255955 >>> >>> # and it's very fast. ```

### globals()

This expression has the appearance of a function but it returns and behaves like a dictionary representing the current global symbol table.

Invoke python on Unix and display information about `globals():`

```\$ python3.6
Python 3.6.3 (v3.6.3:2c5fed86e0, Oct  3 2017, 00:32:08)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
>>>
>>> type(globals())
<class 'dict'>
>>>
>>> globals()
{
'__name__'       : '__main__',
'__doc__'        : None,
'__package__'    : None,
'__spec__'       : None,
'__annotations__': {},
'__builtins__'   : <module 'builtins' (built-in)>
}
# Braces '{}' enclose a dictionary.
# Each key '__name__', '__doc__', '__package__' .... is a string.
# Each value '__main__', None, None, .... may or may not be a string. Most values above are not strings.
>>> quit()
\$
```

When you invoke python interactively, the above global variables are predefined. Unless you really know what you are doing, it is recommended that you do not attempt to change any of the predefined global variables.

Python source file `tmp1.py:`

```\$ head -3 tmp1.py
# tmp1.py
print ('\n'.join([ ((v + (' '*30))[:20] + str(v1)) for v in globals() for v1 in (globals()[v],) ]))
exit (0)
\$
```

When python is invoked by executing python source file `tmp1.py` on Unix, the following global variables are predefined:

```\$ python3.6 tmp1.py
__name__            __main__
__doc__             None
__package__         None
__spec__            None
__annotations__     {}
__builtins__        <module 'builtins' (built-in)>
__file__            tmp1.py
__cached__          None
\$
```

The values `'__file__', '__cached__'` have been added and value `'__loader__'` has changed.

Within the listcomp above:

• Variables `v, v1` are local to the listcomp and do not appear in the list of global symbols. This is why a listcomp was used.
• Each value has been converted to `str` for printing. (Actually so that method `'\n'.join(....)` will work properly.)

#### Accessing dictionary globals()

```>>> v1 = 6 ; t1 = (1,2,3)
>>> globals()
{
'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'v1': 6,
't1': (1, 2, 3)
}
>>>
>>> v1 in globals()
False
>>> 'v1' in globals() # Each key is a string.
True
>>> 'v2' in globals() # Use this feature to determine whether or not global variable v2 exists.
False
>>>
>>> s1 = 'v1'
>>> globals()
{
# 7 predefined global variables as above.
'v1': 6,
't1': (1, 2, 3),
's1': 'v1'
}
>>> s1 in globals()
True
>>> globals()[s1]
6
>>> 's1' in globals()
True
>>> globals()['s1']
'v1'
>>>
```
 To delete a global variable: ```>>> v3 = [1,2,3] >>> v3 [1, 2, 3] >>> del globals()['v3'] >>> v3 Traceback (most recent call last): File "", line 1, in NameError: name 'v3' is not defined >>> ```

#### Creating global variables

 During execution of python code global variables that did not exist in the code may be created. ```>>> v3 Traceback (most recent call last): File "", line 1, in NameError: name 'v3' is not defined >>> >>> for num in range (3,7) : ... s1 = 'v' + str(num) ... globals()[s1] = num**2 ... >>> v3 9 >>> >>> globals() { # 7 predefined variables. 'num': 6, 's1' : 'v6', 'v3' : 9, 'v4' : 16, 'v5' : 25, 'v6' : 36 } >>> >>> v3,v4,v5,v6 (9, 16, 25, 36) >>> ```

#### Importing modules

```>>> import decimal
>>> globals()
{
# ....
'decimal': <module 'decimal' from '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/decimal.py'>
}
>>> d1 = Decimal(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'Decimal' is not defined
>>> d1 = decimal.Decimal(3);d1
Decimal('3')
>>> D = decimal.Decimal
>>> d2 = D(6);d2
Decimal('6')
>>>
```

Change syntax of `import` statement slightly and try again:

```>>> from decimal import *
>>>
>>> globals()
'__name__'            '__main__'
'__doc__'             None
'__package__'         None
'__spec__'            None
'__annotations__'     {}
'__builtins__'        <module 'builtins' (built-in)>
'getcontext'          <built-in function getcontext>
'setcontext'          <built-in function setcontext>
'localcontext'        <built-in function localcontext>
'Decimal'             <class 'decimal.Decimal'>
'Context'             <class 'decimal.Context'>
'DecimalTuple'        <class 'decimal.DecimalTuple'>
'DecimalException'    <class 'decimal.DecimalException'>
'Clamped'             <class 'decimal.Clamped'>
'Rounded'             <class 'decimal.Rounded'>
'Inexact'             <class 'decimal.Inexact'>
'Subnormal'           <class 'decimal.Subnormal'>
'Underflow'           <class 'decimal.Underflow'>
'Overflow'            <class 'decimal.Overflow'>
'DivisionByZero'      <class 'decimal.DivisionByZero'>
'FloatOperation'      <class 'decimal.FloatOperation'>
'InvalidOperation'    <class 'decimal.InvalidOperation'>
'ConversionSyntax'    <class 'decimal.ConversionSyntax'>
'DivisionImpossible'  <class 'decimal.DivisionImpossible'>
'DivisionUndefined'   <class 'decimal.DivisionUndefined'>
'InvalidContext'      <class 'decimal.InvalidContext'>
'DefaultContext'      Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0,
flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
'BasicContext'        Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0,
flags=[], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])
'ExtendedContext'     Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[])
'MAX_PREC'            999999999999999999
'MAX_EMAX'            999999999999999999
'MIN_EMIN'            -999999999999999999
'MIN_ETINY'           -1999999999999999997
'ROUND_UP'            'ROUND_UP'
'ROUND_DOWN'          'ROUND_DOWN'
'ROUND_CEILING'       'ROUND_CEILING'
'ROUND_FLOOR'         'ROUND_FLOOR'
'ROUND_HALF_UP'       'ROUND_HALF_UP'
'ROUND_HALF_DOWN'     'ROUND_HALF_DOWN'
'ROUND_HALF_EVEN'     'ROUND_HALF_EVEN'
'ROUND_05UP'          'ROUND_05UP'
>>>
>>> d1 = Decimal(6); d1 # With this syntax class Decimal is defined as global variable.
Decimal('6')
>>>
>>> type(globals()['MIN_ETINY'])
<class 'int'>
>>> type(globals()['DefaultContext'])
<class 'decimal.Context'>
>>> type(globals()['DivisionByZero'])
<class 'type'>
>>>
>>> type(globals()['ROUND_HALF_UP'])
<class 'str'>
>>> globals()['ROUND_HALF_UP'] == 'ROUND_HALF_UP' # In this case key and value are the same.
True
>>>
```
##### Printing globals()
 To view the contents of `globals(),` simply enter: ```>>> from decimal import * >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'getcontext': , 'setcontext': , 'localcontext': , 'Decimal': , 'Context': , 'DecimalTuple': , 'DecimalException': , 'Clamped': , 'Rounded': , 'Inexact': , 'Subnormal': , 'Underflow': , 'Overflow': , 'DivisionByZero': , 'FloatOperation': , 'InvalidOperation': , 'ConversionSyntax': , 'DivisionImpossible': , 'DivisionUndefined': , 'InvalidContext': , 'DefaultContext': Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow]), 'HAVE_THREADS': True, 'BasicContext': Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow]), 'ExtendedContext': Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[]), 'MAX_PREC': 999999999999999999, 'MAX_EMAX': 999999999999999999, 'MIN_EMIN': -999999999999999999, 'MIN_ETINY': -1999999999999999997, 'ROUND_UP': 'ROUND_UP', 'ROUND_DOWN': 'ROUND_DOWN', 'ROUND_CEILING': 'ROUND_CEILING', 'ROUND_FLOOR': 'ROUND_FLOOR', 'ROUND_HALF_UP': 'ROUND_HALF_UP', 'ROUND_HALF_DOWN': 'ROUND_HALF_DOWN', 'ROUND_HALF_EVEN': 'ROUND_HALF_EVEN', 'ROUND_05UP': 'ROUND_05UP'} >>> ``` While the above information is 100% accurate, without formatting it's not very helpful. Basic formatted output can be accomplished with: ```>>> from decimal import * >>> for v in globals() : print (v, ' ', globals()[v]) ... __name__ __main__ Traceback (most recent call last): File "", line 1, in RuntimeError: dictionary changed size during iteration >>> v = 0 >>> for v in globals() : print (v, ' ', globals()[v]) ... __name__ __main__ __doc__ None __package__ None __loader__ ............................... ROUND_HALF_UP ROUND_HALF_UP ROUND_HALF_DOWN ROUND_HALF_DOWN ROUND_HALF_EVEN ROUND_HALF_EVEN ROUND_05UP ROUND_05UP v v >>> ``` The above adds variable 'v' and it does not show which of the above values are strings. Try again using a listcomp: ```>>> from decimal import * >>> print ( '\n'.join( ... [ ... sx ... for keys in ([ v for v in globals() ], ) ... for len1 in ( sorted( [ len(k) for k in keys ] )[-1]+4, ) ... for k in keys ... for v in (globals()[k],) ... for k1 in ( ('**error**', "'" + k + "'")[isinstance(k,str)], ) ... for single_quote in ( ("", "'")[isinstance(v,str)], ) ... for v1 in ( single_quote + str(v) + single_quote, ) ... for sx in ( (k1 + (' '*len1))[:len1] + v1, ) ... ] ... )) '__name__' '__main__' '__doc__' None '__package__' None '__loader__' '__spec__' None '__annotations__' {} '__builtins__' ................................................ 'ROUND_DOWN' 'ROUND_DOWN' 'ROUND_CEILING' 'ROUND_CEILING' 'ROUND_FLOOR' 'ROUND_FLOOR' 'ROUND_HALF_UP' 'ROUND_HALF_UP' 'ROUND_HALF_DOWN' 'ROUND_HALF_DOWN' 'ROUND_HALF_EVEN' 'ROUND_HALF_EVEN' 'ROUND_05UP' 'ROUND_05UP' >>> ```

#### Global communication

 `globals()` may be used for communication between nested functions. In the code below, `function2()` is defined within `function1()` and `function3()` is defined within `function2().` By means of `globals(),` `function3()` can access the local variables of both `function1()` and `function2():` ```def function1() : def function2() : def function3() : I1 = 6 globals()['f3locals'] = dict(locals()) print (''' function3(): f1locals = {} f2locals = {} f3locals = {} '''.format( globals()['f1locals'], globals()['f2locals'], globals()['f3locals'], )) del globals()['f3locals'] I1 = 4 globals()['f2locals'] = dict(locals()) function3() del globals()['f2locals'] I1 = 2 globals()['f1locals'] = dict(locals()) function2() del globals()['f1locals'] function1() for v in ('f1locals', 'f2locals', 'f3locals') : print (v in globals()) ``` ```function3(): f1locals = {'I1': 2, 'function2': .function2 at 0x102164158>} f2locals = {'I1': 4, 'function3': .function2..function3 at 0x1021641e0>} f3locals = {'I1': 6} False False False ``` Notice that: `function2` is local to `function1,` and `function3` is local to `function2.`

### locals()

This expression has the appearance of a function but it returns and behaves like a dictionary representing the current local symbol table. At module level, globals and locals are the same dictionary. This means that, outside the body of a function, `globals()` and `locals()` are the same. Within the body of a function `locals()` assumes its individual identity.

#### Outside a function

 Invoke python on Unix and display `globals()` and `locals():` ```\$ python3.6 Python 3.6.3 (v3.6.3:2c5fed86e0, Oct 3 2017, 00:32:08) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': } >>> >>> locals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': } >>> >>> locals()['v1'] = 6 >>> >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'v1': 6} >>> >>> locals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'v1': 6} >>> >>> del globals()['v1'] >>> >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': } >>> >>> locals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': } >>> quit () \$ ``` The above operations show that, outside a function, a change in `globals()` is immediately reflected in `locals()` and vice-versa.

#### Inside a function

 The code below shows that, within a function, `locals()` is local. ```def function1 () : print ('1. globals() =', globals()) print ('1. locals() =', locals()) t1 = (1,2,3) print ('2. globals() =', globals()) print ('2. locals() =', locals()) globals()['v1'] = 1.5 print ('3. globals() =', globals()) print ('3. locals() =', locals()) function1() print ('4. globals() =', globals()) print ('4. locals() =', locals()) ``` ```1. globals() = { # Predefined globals. 'function1': } 1. locals() = {} # A change in locals() does not appear in globals(): 2. globals() = { # Predefined globals. 'function1': } 2. locals() = {'t1': (1, 2, 3)} # A change in globals() does not appear in locals(): 3. globals() = { # Predefined globals. 'function1': , 'v1': 1.5 } 3. locals() = {'t1': (1, 2, 3)} # Outside function1() globals() and locals() are the same, and globals() keeps the value 'v1': 4. globals() = { # Predefined globals. 'function1': , 'v1': 1.5 } 4. locals() = { # Predefined globals. 'function1': , 'v1': 1.5 } ```

#### Variable name duplicated

 The following code shows that it's possible to have the same variable name in both `globals()` and `locals()` with different values: ```L2 = [1,2,3] def function2 () : print ('1. globals() =', globals()) print ('1. locals() =', locals()) L2 = [4,5,6] print ('2. globals() =', globals()) print ('2. locals() =', locals()) function2() ``` ```1. globals() = { # Predefined variables. 'L2': [1, 2, 3], 'function2': } 1. locals() = {} 2. globals() = { # Predefined variables. 'L2': [1, 2, 3], 'function2': } 2. locals() = {'L2': [4, 5, 6]} ```

#### Inside a listcomp

 Within a listcomp `locals()` is local to the listcomp: ```>>> print ([ ... locals1 ... for l in 'x' ... for v1 in (1,) ... for L2 in ([4,5,6],) ... for locals1 in (locals(),) ... ]) [{ 'L2': [4, 5, 6], 'v1': 1, 'l': 'x', '.0': }] >>> ```

## Assignments

 In your python code experiment with statements like `import sys`, `from sys import *` and the same for `subprocess` or `re` instead of `sys.` Use `globals()` so that you can see exactly what you have imported.