本週作業與進度:
1. 研讀functools模組
2. 研究itertools模組



Python functions

local、nonlocal與global變數

local:局部變數

def fact(n):
    '''回傳參數n的階乘值'''
    r = 1
    while n > 0:
        r = r * n
        n = n - 1
    return r
fact(5)
## 120
  • 函數內部參數r與n屬於fact()函數的 local 變數
  • 函數內部對local變數的任何操作,對函數外部其他變數無任何影響(除非傳入的參數是 可變(mutable)物件)

global:全域變數

  • 若函數內部需要存取外部變數,可在函數內部宣告其為 global 變數
def f():
    global x
    x = 1
    y = 2
x = 'one'
y = 'two'
f()
x   # 因為x已被宣告為global
## 1
y   # y值沒有影響
## 'two'

nonlocal:非局部變數

  • nonlocal 宣告為 上一層 變數
  • global 是指定 最上層 的變數
g_var = 0
nl_var = 0
print("top level-> g_var: {0} nl_var: {1}".format(g_var, nl_var)) 
## top level-> g_var: 0 nl_var: 0
def test():
    nl_var = 2
    print("in test-> g_var: {0} nl_var: {1}".format(g_var, nl_var)) 
    def inner_test():
        global g_var
        nonlocal nl_var
        g_var = 1
        nl_var = 4
        print("in inner_test-> g_var: {0} nl_var: {1}".format(g_var, nl_var))
    inner_test()
    print("in test-> g_var: {0} nl_var: {1}".format(g_var, nl_var))
test()
## in test-> g_var: 0 nl_var: 2
## in inner_test-> g_var: 1 nl_var: 4
## in test-> g_var: 1 nl_var: 4
print("top level-> g_var: {0} nl_var: {1}".format(g_var, nl_var))
## top level-> g_var: 1 nl_var: 0
  • 只要在函數內部對某變數進行賦值,無論前後,則該變數即為 local 變數
  • 以下範例,a變數有賦值動作,故a為local變數
  • 以下範例為「區域變數a」發生『未賦值先使用』,會引發錯誤訊息
a = 'one'
def func():
    print(a)
    a = 1

# func()
# UnboundLocalError: local variable 'a' referenced before assignment
  • 『讀取』與『修改』不同:如果要『修改』函數外部的變數,需明確使用global或nonlocal敘述
  • 但如果要『讀取』函數外部變數,則不需要使用global或nonlocal敘述
  • 當在函數內部找不到某一變數時,則程式會逐層往上查找該變數
a = 'one'
def func():
    print(a)

func()
## one
  • local變數會在函數執行完畢後刪除
  • nonlocal變數則是在上一層函數執行完畢後刪除
  • global變數則會持續存在,直到整個程式執行完畢

模組 (Module)

# 語法:
# import 模組名稱
# 模組名稱.物件名

import math    # 匯入math模組
math.pi        # 呼叫使用math模組pi物件   
## 3.141592653589793
import cmath 
cmath.pi
## 3.141592653589793
'''mymath - our example math module'''
PI = 3.14159
def area(r):
    '''area(r): return the area of a circle with radius r.'''
    global PI
    return PI * r * r
# Python沒有內建的pi物件與area()函數
# PI
# NameError: name 'PI' is not defined

# area(2)
# NameError: name 'area' is not defined
import os
os.getcwd()
## '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF'
import mymath
# PI
# NameError: name 'PI' is not defined
mymath.PI
## 3.14159
mymath.area(2)
## 12.56636
mymath.__doc__     # 模組的文件字串
## 'mymath - our example math module'
mymath.area.__doc__  # 函數的文件字串
## 'area(r): return the area of a circle with radius r.'
from mymath import PI, area
PI
## 3.14159
area(2)
## 12.56636
import mymath, importlib
importlib.reload(mymath)
## <module 'mymath' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/mymath.py'>

import敘述

有三種方法:
1. import 模組名稱
2. from 模組名稱 impport 物件1, 物件2, 物件3, …
3. from 模組名稱 import *
4. import … as …:匯入並同時更改名稱

import mymath as my
my.PI
## 3.14159
from mymath import area as my_area
my_area(2)
## 12.56636

模組內的私有名稱

"""modtest: our test module"""
def f(x):
    return x
def _g(x):
    return x 

a = 4
_b = 2
from modtest import *

f(100)
## 100
# _g(100)
# NameError: name '_g' is not defined
a
## 4
# _b
# NameError: name '_b' is not defined
import modtest
modtest._b
## 2
modtest._g(100)
## 100

Namespace (命名空間)

Scoping Rule:命名空間的搜尋順序

檢查命名空間與尋找名稱順序

locals()與globals()

  • locals()將回傳『區域命名空間』的資訊,並以dict表示
  • globals()將回傳『全域命名空間』的資訊,並以dict表示

  • 在交談模式輸入locals()與globals():此時尚無『區域命名空間』


  • 上述結果:
    1.__builtins__: 內建模組
    2.__name__: 此模組的名稱
    3.__doc__: 此模組的文件字串(docstring),其中,『主程式』與『交談模式』不會有文件字串

  • EX:

def power(exponent):
    print(locals())
    def f(x):
        print(locals())
        return x ** exponent
    print(locals())
    return f
square = power(2)
## {'exponent': 2}
## {'f': <function power.<locals>.f at 0x114acd510>, 'exponent': 2}
square(5)
## {'x': 5, 'exponent': 2}
## 25
z = 2
import math
from cmath import cos
globals()
## {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, 'R': <class '__main__.R'>, 'r': <__main__.R object at 0x114ad14e0>, 'fact': <function fact at 0x114b46268>, 'f': <function f at 0x114aca730>, 'x': 1, 'y': 'two', 'g_var': 1, 'nl_var': 0, 'test': <function test at 0x114abf9d8>, 'a': 4, 'func': <function func at 0x114ab81e0>, 'math': <module 'math' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/lib-dynload/math.cpython-37m-darwin.so'>, 'cmath': <module 'cmath' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/lib-dynload/cmath.cpython-37m-darwin.so'>, 'os': <module 'os' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/os.py'>, 'mymath': <module 'mymath' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/mymath.py'>, 'PI': 3.14159, 'area': <function area at 0x114ab8ae8>, 'importlib': <module 'importlib' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/importlib/__init__.py'>, 'my': <module 'mymath' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/mymath.py'>, 'my_area': <function area at 0x114aca048>, 'modtest': <module 'modtest' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/modtest.py'>, 'power': <function power at 0x114acd268>, 'square': <function power.<locals>.f at 0x114acd510>, 'z': 2, 'cos': <built-in function cos>}
locals()
## {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, 'R': <class '__main__.R'>, 'r': <__main__.R object at 0x114ad14e0>, 'fact': <function fact at 0x114b46268>, 'f': <function f at 0x114aca730>, 'x': 1, 'y': 'two', 'g_var': 1, 'nl_var': 0, 'test': <function test at 0x114abf9d8>, 'a': 4, 'func': <function func at 0x114ab81e0>, 'math': <module 'math' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/lib-dynload/math.cpython-37m-darwin.so'>, 'cmath': <module 'cmath' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/lib-dynload/cmath.cpython-37m-darwin.so'>, 'os': <module 'os' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/os.py'>, 'mymath': <module 'mymath' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/mymath.py'>, 'PI': 3.14159, 'area': <function area at 0x114ab8ae8>, 'importlib': <module 'importlib' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/importlib/__init__.py'>, 'my': <module 'mymath' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/mymath.py'>, 'my_area': <function area at 0x114aca048>, 'modtest': <module 'modtest' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/modtest.py'>, 'power': <function power at 0x114acd268>, 'square': <function power.<locals>.f at 0x114acd510>, 'z': 2, 'cos': <built-in function cos>}
del z, math, cos
globals()
## {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, 'R': <class '__main__.R'>, 'r': <__main__.R object at 0x114ad14e0>, 'fact': <function fact at 0x114b46268>, 'f': <function f at 0x114aca730>, 'x': 1, 'y': 'two', 'g_var': 1, 'nl_var': 0, 'test': <function test at 0x114abf9d8>, 'a': 4, 'func': <function func at 0x114ab81e0>, 'cmath': <module 'cmath' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/lib-dynload/cmath.cpython-37m-darwin.so'>, 'os': <module 'os' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/os.py'>, 'mymath': <module 'mymath' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/mymath.py'>, 'PI': 3.14159, 'area': <function area at 0x114ab8ae8>, 'importlib': <module 'importlib' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/importlib/__init__.py'>, 'my': <module 'mymath' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/mymath.py'>, 'my_area': <function area at 0x114aca048>, 'modtest': <module 'modtest' from '/Users/chenzhenghui/Dropbox/NCCU/QF/R_and_Python_for_QF/modtest.py'>, 'power': <function power at 0x114acd268>, 'square': <function power.<locals>.f at 0x114acd510>}
import math
math.pi
## 3.141592653589793

EX:

查詢已匯入模組的命名空間

  • 當匯入模組時,並呼叫模組內定義的函數,模組函數看到的全域命名空間為『該模組的命名空間』, 並不是主函數的命名空間
  • Python的全域變數無法跨模組或跨檔案存取

EX:建立一個名為scopetest.py的模組:

'''scopetest: our scope test module'''
v = 6
def f(x):
    """f: scope test function""" 
    print("global: ", list(globals().keys())) 
    print("entry local:", locals())
    y = x
    w = v
    print("exit local:", locals().keys())
import scopetest
z = 2
scopetest.f(z)
## global:  ['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'v', 'f']
## entry local: {'x': 2}
## exit local: dict_keys(['x', 'y', 'w'])

EX:建立counter1模組

'''counter1.py module'''
num = 0
def add():
    global num
    num += 1
import counter1
counter1.num
## 0
counter1.add()
counter1.num
## 1
from counter2 import num, add
num 
## 0
add() 
num    # num變數值沒有增加
## 0
import counter2
counter2.num     # 實際上,counter2模組內的num變數有增加了
## 1
  • 所以add()函數執行時,增加的是該模組內『全域變數』num的值,而不是主程式的全域變數num
  • 故建議不要從模組import變數

EX:

list("NCCU")
## ['N', 'C', 'C', 'U']
list = [1, 2, 3, 4]
# list('NCCU')
# TypeError: 'list' object is not callable
del list
list("NCCU")
## ['N', 'C', 'C', 'U']

EX:R code – 則不會有問題

list(1, 2, 3)  # R
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
## 
## [[3]]
## [1] 3
list <- 1:3    # R
list           # R
## [1] 1 2 3
list(1, 2, 3)  # R
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
## 
## [[3]]
## [1] 3