## 6.1 Functions (2)

#### mutable vs. immutable

• For immutable objects (number, string, tuple), the assignment inside a function cannot change the value of the variable outside the function even if the variable name inside is the same with the variable name outside.


def ChangeInt(a):
a = 10

b = 2
ChangeInt(b)
print(b) #output: 2


def ChangeInt(b):
b = 10

b = 2
ChangeInt(b)
print(b) #output: 2


def ChangeInt(b):
b = 10
return b

b = 2
x = ChangeInt(b)
print(x, b) #output: 10 2


def ChangeInt(a):
b = 10
return b

b = 2
x = ChangeInt(b)
print(x, b) #output: 10 2


### Examples


def ChangeInt(a):
a = 10
print(a)
return a

b = 2
ChangeInt(b)
print(b)
print(ChangeInt(b))


10
2
10
10


def ChangeInt(a):
a = 10
print(a)

b = 2
ChangeInt(b)
print(b)
print(ChangeInt(b))


10
2
10
None

• For mutable objects (list, dictionary), the assignment inside a function cannot change the value of the variable outside the function. (the same with immutable objects!)


def changeme(mylist):
mylist = [1,2]
print("inside: ", mylist)

x = [10,20]
changeme(x)
print("outside: ", x)


inside:  [1, 2]
outside:  [10, 20]

• However, the modification can change the value of the variable outside.


def changeme(mylist):
mylist.extend([1,2])
print ("inside: ", mylist)

x = [10,20]
changeme(x)
print ("outside: ", x)


inside:  [10, 20, 1, 2]
outside:  [10, 20, 1, 2]


#### Variable Scope

In Python, module, class, def, lambda can introduce new variable scope, if/elif/else/, try/except, for/while will not introduce new variable scope.


if True:
msg = 'I am from Shanghai'
print(msg) # We can use msg.


def test():
msg = 'I am from Shanghai'
print(msg) # error

• Local$\rightarrow$Enclosing$\rightarrow$Global$\rightarrow$ Build-in



g_count = 0  # Global
def outer():
o_count = 1  # Enclosing
def inner():
i_count = 2  # Local


New Assignment


msg = 'I am from Shanghai'
def test():
msg = 'I am from Beijing'
print(msg)
test()
print(msg)


I am from Beijing
I am from Shanghai



Reference


msg = 'I am from Shanghai'
def test():
print(msg)
test()


I am from Shanghai



Modification


msg = 'I am from Shanghai'
def test():
print(msg)
msg = 'I am from Beijing'
test()


UnboundLocalError: local variable 'msg'
referenced before assignment


• How to modify the variable outside? The global keyword


num = 1
def fun():
global num
num = 123
print(num)
fun()
print(num)


123
123



num = 1
def fun():
print(num)
global num
num = 123
print(num)
fun()


SyntaxError: name 'num' is used
prior to global declaration



a = 10
def test():
a = a + 1
print(a)
test() # error


a = 10
def test():
global a
a = a + 1
print(a)
test() # output: 11


a = 10
def test():
a=10
a = a + 1
print(a)
test() # output: 11
print(a) # output: 10

##### The nonlocal keyword

def outer():
num = 10
def inner():
nonlocal num
# nonlocal keyword
num = 100
print(num)
inner()
print(num)
outer()


100
100



def outer():
num = 10
def inner():
global num
# global does not work
num = 100
print(num)
inner()
print(num)
outer()


100
10



def outer():
global num
num = 10
def inner():
global num
num = 100
print(num)
inner()
print(num)
outer()


100
100



def outer():
global num
num = 10
def inner():
num = 100
print(num)
inner()
print(num)
outer()


100
10



num = 3
def outer():
global num
num = 10
def inner():
num = 100
print(num)
inner()
print(num)
outer()
print(num)


100
10
10



num = 3
def outer():
num = 10
def inner():
global num
num = 100
print(num)
inner()
print(num)
outer()
print(num)


100
10
100



num = 3
def outer():
num = 10
def inner():
nonlocal num
num = 100
print(num)
inner()
print(num)
outer()
print(num)


100
100
3



num = 3
def outer():
global num
num = 10
def inner():
global num
num = 100
print(num)
inner()
print(num)
outer()
print(num)


100
100
100



## 6.2 Functions (3)

##### arbitrary arguments

def make_pizza(toppings):
print(toppings)

make_pizza('pepperoni')

• If we want to pass multiple arguments, we may create a list at first.


x=['mushrooms', 'green peppers', 'extra cheese']
def make_pizza(toppings):
for y in toppings:
print(y, end=', ')

make_pizza(x)


def make_pizza(topping_1, topping_2, topping_3):
print(topping_1, topping_2, topping_3)

make_pizza('mushrooms', 'green peppers', 'extra cheese')

• However, sometimes we are not sure about the exact number of the arguments.

• We can pass multiple arguments at once in the following way.


def make_pizza(*toppings):
print(toppings)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

• Note that Python packs the arguments into a tuple, even if the function receives only one value.


def make_pizza(*toppings):
for topping in toppings:
print("- " + topping)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')


- pepperoni
- mushrooms
- green peppers
- extra cheese

• Mixing Positional and Arbitrary Arguments

• If you want a function to accept several different kinds of arguments, the parameter that accepts an arbitrary number of arguments must be placed last in the function definition.


def make_pizza(size, *toppings):
print(str(size) + "-inch pizza:")
for topping in toppings:
print("- " + topping)

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')


16-inch pizza:
- pepperoni
12-inch pizza:
- mushrooms
- green peppers
- extra cheese

• Using Arbitrary Keyword Arguments


def build_profile(**user_info):
print(user_info)

build_profile(location='Shanghai',field='Management')


{'location': 'Shanghai', 'field': 'Management'}

• Using Arbitrary Keyword Arguments


def build_profile(first, last, **user_info):
for key, value in user_info.items():
profile[key] = value
return first, last, profile

user_profile = build_profile('albert', 'einstein',
location='princeton',
field='physics')
print(user_profile)


('albert', 'einstein',
{'location': 'princeton', 'field': 'physics'})


def build_profile(*name, **user_info):
print(name)
print(user_info)

build_profile('albert', 'einstein',
location='princeton',
field='physics')


('albert', 'einstein')
{'location': 'princeton', 'field': 'physics'}

• As a tradition, we often use *args and **kw

• Example


def test(x,y=1,*a,**b):
print(x,y,a,b)

test(1)
test(1,2)
test(1,2,3)
test(1,2,3,4)
test(x=1,y=2)
test(1,a=2)
test(1,2,3,a=4)
test(1,2,3,y=4)


• Result



1 1 () {}
1 2 () {}
1 2 (3,) {}
1 2 (3, 4) {}
1 2 () {}
1 1 () {'a': 2}
1 2 (3,) {'a': 4}
TypeError: test() got multiple values


## 6.3 Modules

• Module is a Python file, followed with .py

• Storing Your Functions in Modules


def make_pizza(size, *toppings):
print("\nMaking a " + str(size) +
"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)

• It is saved as "pizza.py" file.

• We make a separate file called making_pizzas.py in the same directory as pizza.py.


import pizza

pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

• Be Careful!


def make_pizza(size, *toppings):
print("\nMaking a " + str(size) +
"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)

make_pizza(16, 'pepperoni')

#saved as pizza.py


import pizza

#in another python file


Making a 16-inch pizza with the following toppings:
- pepperoni

• A way to solve it


def make_pizza(size, *toppings):
print("\nMaking a " + str(size) +
"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)

if __name__ == '__main__':
make_pizza(16, 'pepperoni')

#saved as pizza.py


import pizza

#in another python file


Importing Specific Functions


#from module_name import function_name
#from module_name import function_0, function_1, function_2

from pizza import make_pizza

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')


Using as to Give a Function an Alias


from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')


Using as to Give a Module an Alias


import pizza as p
p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

• Importing All Functions in a Module


from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

• The asterisk in the import statement tells Python to copy every function from the module pizza into this program file. Because every function is imported, you can call each function by name without using the dot notation.

• However, it's best not to use this approach when you're working with larger modules that you didn't write: if the module has a function name that matches an existing name in your project, you can get some unexpected results.

### Search for Modules

• When a module named is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named xxx.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:

• The directory containing the input script (or the current directory when no file is specified).

• PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).

• The installation-dependent default.


import sys
print(sys.path)


### Packages

• Packages are namespaces which contain multiple packages and modules themselves. They are simply directories, but with a twist.

• Each package in Python is a directory which MUST contain a special file called __init__.py. This file can be empty, and it indicates that the directory it contains is a Python package, so it can be imported the same way a module can be imported.


mycompany
├─ __init__.py
├─ abc.py
└─ xyz.py

• The name of modules are mycompany.abc and mycompany.xyz

• Be careful! The name of modules you created should not be in conflict with the name of any system module, or any build-in function.

### Third party modules


import urllib
dir(urllib)

• For Python only, open the cmd.

• 
pip install pillow

• For Anaconda only, open the anaconda prompt.

• 
conda install pillow
pip install pillow

• If you want to use both the Python and Anaconda, there might be some conflicts. Thus, please change file name in the Anaconda. D:\Anaconda\Scripts, pip.exe and pip-script.exe to condapip.exe and condapip-script.exe

• 
condapip install pillow #anaconda

##### Uninstall the package

pip uninstall pillow
conda uninstall pillow

##### List all the packages

pip list
conda list

##### Examples

conda install you-get
conda install pyperclip
conda install itchat


import pyperclip
pyperclip.copy("abc")


import itchat
itchat.auto_login()

friends = itchat.get_friends(update=True)[0:]



male = 0
female = 0
other = 0
for i in friends[1:]:
sex = i['Sex']
if sex == 1:
male = male + 1
elif sex ==2:
female = female + 1
else:
other = other + 1
total = len(friends[1:])

print('男性好友： {}%'.format(male/total*100))
print('女性好友： {}%'.format(female/total*100))
print('其他： {}%' .format(other/total*100))

itchat.logout()


## Summary

• Functions
• Reading: Python for Everybody, Chapter 4
• Reading: Python Crash Course, Chapter 8