Python Programming

Lecture 7 Variable Scope, Arbitrary Arguments

7.1 Variable Scope

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

Global and Local


msg_loc = "Shanghai" # Global
def test():
    msg = 'I am from Shanghai' # 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()

UnboundLocalError: local variable 'a' 
referenced before assignment



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

11





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

11
10




mutable vs. immutable


a = [1,2,3]
def test():
    print(a)
    a = [1,2,3,4]
test() 

UnboundLocalError: local variable 'a' 
referenced before assignment



a = [1,2,3]
def test():
    print(a)
    a.append(4)
test() 
print(a)

[1, 2, 3]
[1, 2, 3, 4]




a = {"color": "green"}
def test():
    print(a)
    a["color"] = "red"
    a["position"] = "left"
test() 
print(a)

{'color': 'green'}
{'color': 'red', 'position': 'left'}




Parameter and Argument

  • 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
    return a

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

10
2





def ChangeInt(b):
    b = 10
    return b

b = 2
x = ChangeInt(b)
print(x, b) 

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]




7.2 Nested Functions

Nested Functions


def outer():
    def inner():
        print(100)
    inner()
outer()

def outer():
    def inner():
        num = 100
        print(num)
    inner()
outer()
  • Local$\rightarrow$Enclosing$\rightarrow$Global$\rightarrow$ Build-in (LEGB)


num = 3           # Global
def outer():
    num = 10      # Enclosing
    def inner():
        num = 100 # Local
        print(num)
    inner()
    print(num)
outer()
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








7.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):
    profile = {}
    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

Summary

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