Crash course on scope in functions

Not all objects are accessible everywhere in a script.

Scope – part ofthe program where an object or name may be accessible

  • Global scope – defined in the main body of a script
  • Local scope – defined inside a function
  • Built-in scope – names in the pre-defined built-ins module

In the example below new_val is defined only in the scope of the function and cannot be accessed outside of it

def square(value):
    """Returns the square of a number."""
    new_val = value ** 2
    return new_val
square(3)

In the example below it is defined globally and can be accessed. BUT in the function it is not changing the value of the new_val variable so if we ran the code below, the function will return 100 but the value of new_val will still be 10.

new_val = 10

def square(value):
    """Returns the square of a number."""
    new_val = value ** 2
    return new_val
square(3)

If we want to change the value of new_val in the function then we have to do :

new_val = 10
def square(value):
    """Returns the square of a number."""
    global new_val
    new_val = new_val ** 2
    return new_val
square(3)

We added a global definition of new_val in the function.

Nested functions

Nested functions are functions in functions :

def outer( ... ):
    """ ... """
    x = ...
    def inner( ... ):
    """ ... """
    y = x ** 2
    return ...

Instead of doing this :

def mod2plus5(x1, x2, x3):
    """Returns the remainder plus 5 of three values."""
    new_x1 = x1 % 2 + 5
    new_x2 = x2 % 2 + 5
    new_x3 = x3 % 2 + 5
    return (new_x1, new_x2, new_x3)

We can do this :

def mod2plus5(x1, x2, x3):
"""Returns the remainder plus 5 of three values."""
    def inner(x):
        """Returns the remainder plus 5 of a value."""
        return x % 2 + 5
    return (inner(x1), inner(x2), inner(x3))
    
print(mod2plus5(1,2,3))
->(6,5,6)

You can also return functions ! This is tricky so pay attention, in the return statement of the first function you will return the inner function :

def raise_val(n):
"""Return the inner function."""
        def inner(x):
        """Raise x to the power of n."""
        raised = x ** n
        return raised
    return inner

square = raise_val(2)
cube = raise_val(3)

print(square(2), cube(4))
->4,64

Using Nonlocal :

def outer():
"""Prints the value of n."""
    n = 1
    def inner():
        nonlocal n
        n = 2
        print(n)
    inner()
    print(n)

outer()
->2
->2

From the exercise :

# Define echo_shout()
def echo_shout(word):
    """Change the value of a nonlocal variable"""
    
    # Concatenate word with itself: echo_word
    echo_word=word+word
    
    # Print echo_word
    print(echo_word)
    
    # Define inner function shout()
    def shout():
        """Alter a variable in the enclosing scope"""    
        # Use echo_word in nonlocal scope
        nonlocal echo_word
        
        # Change echo_word to echo_word concatenated with '!!!'
        echo_word = echo_word+'!!!'
    
    # Call function shout()
    shout()
    
    # Print echo_word
    print(echo_word)

# Call function echo_shout() with argument 'hello'
echo_shout('hello')

Default and exible arguments

How to add default arguments :

def power(number, pow=1):
    """Raise number to the power of pow."""
    new_value = number ** pow
    return new_value

power(9, 2)
->81
power(9,1)
->9
power(9)
->9

if you put = something for a variable in the def, you assign a by default value to it, so even if you don’t pass it in the call the function will take the by default value.

Now for flexible args, you can pass *args in the definition of the function if you want to accept any number of variables passed :

def add_all(*args):
    """Sum all values in *args together."""
    # Initialize sum
    sum_all = 0
    # Accumulate the sum
    for num in args:
    sum_all += num
    return sum_all

add_all(1)
->1
add_all(5,10,15,20)
->50

You can also use **kwargs :

**kwargs is a dictionary of keyword arguments. The ** allows us to pass any number of keyword arguments. A keyword argument is basically a dictionary.

def print_all(**kwargs):
    """Print out key-value pairs in **kwargs."""
    # Print out the key-value pairs
    for key, value in kwargs.items():
        print(key + \": \" + value)

print_all(name="dumbledore", job="headmaster")

->
job: headmaster
name: dumbledore

From the exercises, take notice at the hodgepodge += word that concatenates in a loop new words in a string.

# Define gibberish
def gibberish(*args):
    """Concatenate strings in *args together."""

    # Initialize an empty string: hodgepodge
    hodgepodge=''

    # Concatenate the strings in args
    for word in args:
        hodgepodge += word

    # Return hodgepodge
    return(hodgepodge)

# Call gibberish() with one string: one_word
one_word = gibberish('luke')

# Call gibberish() with five strings: many_words
many_words = gibberish("luke", "leia", "han", "obi", "darth")

# Print one_word and many_words
print(one_word)
print(many_words)

->
luke
lukeleiahanobidarth

And another one with passing lines in the print 🙂

# Define report_status
def report_status(**kwargs):
    """Print out the status of a movie character."""

    print("\nBEGIN: REPORT\n")

    # Iterate over the key-value pairs of kwargs
    for key, value in kwargs.items():
        # Print out the keys and values, separated by a colon ':'
        print(key + ": " + value)

    print("\nEND REPORT")

# First call to report_status()
report_status(name='luke', affiliation='jedi', status='missing')

# Second call to report_status()
report_status(name='anakin', affiliation='sith lord', status='deceased')


-> 
BEGIN: REPORT

name: luke
affiliation: jedi
status: missing

END REPORT

BEGIN: REPORT 

name: anakin
affiliation: sith lord
status: deceased

BEGIN: REPORT 

And finally, we can see how it looks with the tweeter counter thing, we will try to pass as many columns as the user wants :

# Define count_entries()
def count_entries(df, *args):
    """Return a dictionary with counts of
    occurrences as value for each key."""
    
    #Initialize an empty dictionary: cols_count
    cols_count = {}
    
    # Iterate over column names in args
    for col_name in args:
    
        # Extract column from DataFrame: col
        col = df[col_name]
    
        # Iterate over the column in DataFrame
        for entry in col:
    
            # If entry is in cols_count, add 1
            if entry in cols_count.keys():
                cols_count[entry] += 1
    
            # Else add the entry to cols_count, set the value to 1
            else:
                cols_count[entry] = 1

    # Return the cols_count dictionary
    return cols_count

# Call count_entries(): result1
result1 = count_entries(tweets_df,'lang')

# Call count_entries(): result2
result2 = count_entries(tweets_df, 'lang', 'source')

# Print result1 and result2
print(result1)
print(result2)

Brax

Dude in his 30s starting his digital notepad