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)