1. Programs are composed of modules.
2. Modules contain statements.
3. Statements contain expressions.
4. Expressions create and process objects.html
更多內容詳見:Python標準異常列表python
except OSErrorgit
except IOErrorexpress
except ValueErrorapi
except ZeroDivisionError數組
except NameError閉包
如下代碼將ZeroDivisionError傳遞爲ValueError,也就不return了。app
def divide(a, b): try: return a / b except ZeroDivisionError as e: raise ValueError(‘Invalid inputs’) from e
(a) catch的處理部分不能generic,應該單獨處理特定的exception;less
(b) catch的「特殊性」,不能接收到generic的raise出的異常;ide
捕捉到了異常,可是又想從新引起它(傳遞異常),可使用不帶參數的raise語句便可:
logger = logging.getLogger(__name__) try: do_something_in_app_that_breaks_easily() except AppError as error: logger.error(error) raise # just this! # raise AppError # Don't do this, you'll lose the stack trace!
這裏,這是簡單作個warning。re-raise後,講異常傳遞給外層函數的異常處理部分。
異常後再也不須要走「return」這個流程,以下示範:
class NotFoundError(Exception): """Throw this when something can't be found on a page."""
def get_abe_status(url): # download the page page = download_page(url) # get all mentions of Abe Vigoda hits = page.find_all_mentions("Abe Vigoda") try: hits[0] except IndexError: raise NotFoundError("No mentions found.") # 對IndexError進行了封裝達到了隱藏效果 # say we expect four hits... if len(hits) != 4: raise Warning("An unexpected number of hits.") logger.warning("An unexpected number of hits.") # parse the first hit for his status status = parse_abe_status(hits[0]) if status not in ['alive', 'dead']: raise SomeTypeOfError("Status is an unexpected value.") # he's either alive or dead return status == "alive"
可見,上例中有了「封裝」的效果,不讓外層函數知道具體發生了什麼異常。也就間接解釋了「異常基類」的意義。
class Error(Exception): """Base class for exceptions in this module.""" pass
Goto: The definitive guide to Python exceptions
X = A or B or C or None
大函數返回小函數,小函數僅能"只讀"大函數的局部變量。
若是「可寫」,則須要Non-local。
典型的裝飾器寫法。
def fn_timer(function): @wraps(function) def function_timer(*args, **kwargs): start = time.time() result = function(*args, **kwargs) end = time.time() print ("Total time: %s seconds" % (str(end-start))) return result return function_timer
注意:這裏初始化一次後,再調用默認就再也不初始化了。
def log(message, when=datetime.now()): print(‘%s: %s’ % (when, message)) log(‘Hi there!’) sleep(0.1) log(‘Hi again!’)
>>> 2014-11-15 21:10:10.371432: Hi there! 2014-11-15 21:10:10.371432: Hi again!
page 372/1594
1 a, b = 'good', 'bad' 2 3 log.write("spam, ham") 4 5 print('Then Killer', joke) 6 7 if "python" in text: 8 print(text) 9 10 for x in mylist: 11 print(x) 12 13 while X > Y: 14 print('hello') 15 16 while True: 17 pass 18 19 while True: 20 if exittest(): break 21 22 while True: 23 if skiptest(): continue 24 25 def f(a, b, c=1, *d): 26 print(a+b+c+d[0]) 27 28 # 其中一個*號表明list或者tuple,**表明map或者dict 29 def f(a, b, c=1, *d): 30 return(a+b+c+d[0]) 31 32 def gen(n): 33 for i in n: 34 yield i*2 35 36 x = 'old' 37 def function(): 38 global x, y: 39 x = 'new' 40 41 # https://stackoverflow.com/questions/1261875/python-nonlocal-statement 42 def outer(): 43 x = 'old' 44 def function(): 45 nonlocal x: 46 x = 'new' 47 48 import sys 49 50 from sys import stdin 51 52 class Subclass(Superclass): 53 staticData = [] 54 def method(self): 55 pass 56 57 try: 58 action() 59 except: 60 print('action error') 61 62 # 需進一步理解 63 raise EndSearch(location) 64 65 assert X > Y, 'X too small' 66 67 with open('data') as myfile: 68 process(myfile) 69 70 del data[k] 71 del data[i:j] 72 del obj.attr 73 del variable
上述疑難api在此總結。
pass 是空語句,是爲了保持程序結構的完整性。 pass 不作任何事情,通常用作佔位語句。
好比定義一個空函數是「報錯的」,空函數的內容是pass就行了。
其中一個*號表明list或者tuple,**表明map或者dict
「輸入」的內容很差預測,也是「異常多發地」,故在此用來舉例。
while True: reply = input('Enter text:')
if reply == 'stop': break
try: num = int(reply) # 若是非法str,執行except except: print('Bad!' * 8)
else: print(num ** 2)
print('Bye')
while True: reply = input('Enter text:') if reply == 'stop': break elif not reply.isdigit(): # 若是不是數字 print('Bad!' * 8) else: # 若是是數字 num = int(reply) if num < 20: print('low') else: print(num ** 2) print('Bye')
一個 try 語句可能包含多個except子句,分別來處理不一樣的特定的異常。
最多隻有一個分支會被執行。
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip())
except OSError as err: print("OS error: {0}".format(err))
except ValueError: print("Could not convert data to an integer.")
except: print("Unexpected error:", sys.exc_info()[0]) raise
若是處理策略一致,能夠統一塊兒來寫。
except (RuntimeError, TypeError, NameError): pass
else子句將在try子句沒有發生任何異常的時候執行。例如:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close()
[意義何在?]
使用 else 子句比把全部的語句都放在 try 子句裏面要好,這樣能夠避免一些意想不到的、而except又沒有捕獲的異常。
ref: Python中try…except…else…結構中else的做用?
既然執行到了else,就說明「沒有異常發生」。如此,代碼更清晰更具備表現力。
(5) 捕獲try中的 「子函數異常」
>>>def this_fails(): x = 1/0 # 子函數中拋出異常 >>> try: this_fails() except ZeroDivisionError as err: print('Handling run-time error:', err) Handling run-time error: int division or modulo by zero
使用 raise 語句拋出一個指定的異常。
>>>try: raise NameError('HiThere')
except NameError: print('An exception flew by!') raise An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in ? NameError: HiThere
"異常類" 繼承自 Exception 類,能夠直接繼承,或者間接繼承。
>>>class MyError(Exception): def __init__(self, value): self.value = value # 類 Exception 默認的 __init__() 被覆蓋 def __str__(self): return repr(self.value) >>> try: raise MyError(2*2)
except MyError as e: print('My exception occurred, value:', e.value)
My exception occurred, value: 4 >>> raise MyError('oops!') Traceback (most recent call last): File "<stdin>", line 1, in ? __main__.MyError: 'oops!'
當建立一個模塊有可能拋出多種不一樣的異常時,一種一般的作法是:
1. 爲這個包創建一個基礎異常類;
2. 而後基於這個基礎類爲不一樣的錯誤狀況建立不一樣的子類。
# 有點像接口的感受
class Error(Exception): """Base class for exceptions in this module.""" pass
# 再定義具體的異常內容 class InputError(Error): """Exception raised for errors in the input. Attributes: expression -- input expression in which the error occurred message -- explanation of the error """ def __init__(self, expression, message): self.expression = expression self.message = message class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: previous -- state at beginning of transition next -- attempted new state message -- explanation of why the specific transition is not allowed """ def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = message
>>>def divide(x, y): try: result = x / y except ZeroDivisionError: print("division by zero!") else: print("result is", result) finally: print("executing finally clause")
>>> divide(2, 1) result is 2.0 executing finally clause
>>> divide(2, 0) division by zero! executing finally clause
>>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str'
典型例子,嘗試打開文件:
with open("myfile.txt") as f: for line in f: print(line, end="")
關鍵詞 with 語句就能夠保證諸如文件之類的對象在使用完以後必定會正確的執行他的清理方法。
>>> A, B = nudge, wink # Tuple assignment >> [C, D] = [nudge, wink] # List assignment
# 因而,變量對調 會簡單一些 >>> nudge, wink = wink, nudge # Tuples: swaps values >>> [a, b, c] = (1, 2, 3) # Assign tuple of values to list of names >>> (a, b, c) = "ABC" # Assign string of characters to tuple #若是是字符串長致使了單元個數不匹配 >>> a, b, c = string[0], string[1], string[2:] # Index and slice
高級一點的,括號匹配。
for (a, b, c) in [(1, 2, 3), (4, 5, 6)]: ... # Simple tuple assignment for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]: ... # Nested tuple assignment
不是指針,而是rest
>>> a, *b, c = 'spam' >>> a, b, c ('s', ['p', 'a'], 'm')
循環遍歷時貌似有一點點好處
>>> L = [1, 2, 3, 4] >>> while L: ... front, *L = L # Get first, rest without slicing
... front, L = L[0], L[1:] # 上一行的另外一種寫法:小變化,效果同樣
... print(front, L)
... 1 [2, 3, 4] 2 [3, 4] 3 [4] 4 []
Boundary cases
#末尾是否還有東西 >>> a, b, c, *d = seq >>> print(a, b, c, d) 1 2 3 [4] >>> a, b, c, d, *e = seq >>> print(a, b, c, d, e) 1 2 3 4 [] # 中間是否還有東西 >>> a, b, *e, c, d = seq >>> print(a, b, c, d, e) 1 2 3 4 []
節省條件判斷的變量賦值。
X = A or B or C or None # assigns X to the first nonempty (that is, true) object among A, B, and C, or to None if all of them are empty.
X = A or default
while test: # Loop test statements # Loop body else: # Optional else statements # Run if didn't exit loop with break
這個else是個很好的東西,表示循環走到頭了;有益代碼閱讀。
for target in object: # Assign object items to target statements # Repeated loop body: use target else: # Optional else part statements # If we didn't hit a 'break'
【Maybe continue... Chapter 14】
X = 99 # Global (module) scope X
def func(): X = 88 # Local (function) scope X: a different variable
細節繁瑣,但大致是繼承了C/C++的那一套東西。
• The enclosing module is a global scope. Each module is a global scope—that is, a namespace in which variables created (assigned) at the top level of the module file live. Global variables become attributes of a module object to the outside world after imports but can also be used as simple variables within the module file itself. • The global scope spans a single file only. Don’t be fooled by the word 「global」 here—names at the top level of a file are global to code within that single file only. There is really no notion of a single, all-encompassing global file-based scope in Python. Instead, names are partitioned into modules, and you must always import a module explicitly if you want to be able to use the names its file defines. When you hear 「global」 in Python, think 「module.」 • Assigned names are local unless declared global or nonlocal. By default, all the names assigned inside a function definition are put in the local scope (the namespace associated with the function call). If you need to assign a name that lives at the top level of the module enclosing the function, you can do so by declaring it in a global statement inside the function. If you need to assign a name that lives in an enclosing def, as of Python 3.X you can do so by declaring it in a nonlocal statement. • All other names are enclosing function locals, globals, or built-ins. Names not assigned a value in the function definition are assumed to be enclosing scope locals, defined in a physically surrounding def statement; globals that live in the enclosing module’s namespace; or built-ins in the predefined built-ins module Python provides. • Each call to a function creates a new local scope. Every time you call a function, you create a new local scope—that is, a namespace in which the names created inside that function will usually live. You can think of each def statement (and lambda expression) as defining a new local scope, but the local scope actually corresponds to a function call. Because Python allows functions to call themselves to loop—an advanced technique known as recursion and noted briefly in Chapter 9 when we explored comparisons—each active call receives its own copy of the function’s local variables. Recursion is useful in functions we write as well, to process structures whose shapes can’t be predicted ahead of time; we’ll explore it more fully in Chapter 19.
本段落,主要關注Global,以後講解enclosing 還有 local 相關。
X = 88 # Global X def func(): global X X = 99 # Global X: outside def
func() print(X) # Prints 99
目的:Program Design: Minimize Cross-File Changes
# first.py X = 99 # This code doesn't know about second.py
# second.py import first print(first.X) # OK: references a name in another file first.X = 88 # But changing it can be too subtle and implicit
雖然是不一樣文件,加載到內存中就都同樣了;經過函數改變總比直接改變要好一些。
# first.py X = 99 def setX(new): # Accessor make external changes explit global X # And can manage access in a single place X = new
# second.py import first first.setX(88) # Call the function instead of changing directly
Ref: https://www.zhihu.com/question/20125256
這裏實際上是對函數處理後的結果排序。
sorted(list1, key=lambda x: abs(x))
def my_add(n): return lambda x:x+n add_3 = my_add(3) # 返回的實際上是個函數指針 add_3(7)
閉包的做用:在 my_add 局部做用域中,變量 n 的值在閉包(Factory Functions: Closures)的做用下,使得它在全局做用域也能夠被訪問到。
def my_add(n): def wrapper(x): return x+n return wrapper # ----> 大函數返回小函數
add_5 = my_add(5) # 先設置「大函數」的參數 add_5(2) # 在設置「小函數」的參數
(1) 返回的是一個函數,也就是返回一個具備本身id的一類產品。【工程方法】
def maker(N): def action(X): # Make and return action return X ** N # action retains N from enclosing scope return action
f = maker(2)
(2) 或者,採用以下更爲簡潔的等價方式。【lambda更好】
def func(): x = 4 action = (lambda n: x ** n) # x remembered from enclosing def return action
f = func() print(f(2)) # Prints 16, 4 ** 2
(3) 進一步,返回內部 id 不同的一系列產品。【lambda指針數組】
def makeActions(): acts = [] for i in range(5): # Use defaults instead acts.append(lambda x, i=i: i ** x) # Remember current i return acts
>>> acts = makeActions() >>> acts[0](2) # 0 ** 2 0 >>> acts[1](2) # 1 ** 2 1 >>> acts[2](2) # 2 ** 2 4 >>> acts[4](2) # 4 ** 2 16
Changing a name in an enclosing def’s scope is not allowed by default, though; this is the normal case in 2.X as well.
在nested函數中的state是「只讀」狀態,不能改寫。
做爲對比,lambda中也引用外層變量,但也僅限於 「只讀」。
def tester(start): state = start # 且必須這裏聲明 def nested(label): print(label, state) # 這裏必須使用nonlocal代表你想訪問外層的state變量
# nonlocal state state += 1 # Cannot change by default (never in 2.X) 非法! return nested
>>> F = tester(0) >>> F('spam') UnboundLocalError: local variable 'state' referenced before assignment
Ref: python中global 和 nonlocal 的做用域
在局部若是不聲明全局變量,而且不修改全局變量。則能夠正常使用全局變量
gcount = 0 def global_test(): print (gcount) global_test()
若是在局部不修改全局變量,程序正確輸出 0 。也就是隻讀了外部的gcount。
Ref: python中global 和 nonlocal 的做用域
nonlocal 關鍵字 用來在函數或其餘做用域中使用外層 (非全局) 變量。
def make_counter(): count = 0 def counter(): nonlocal count # 代表想使用了外層變量! count += 1 return count return counter
def make_counter_test(): mc = make_counter() print(mc()) print(mc()) print(mc())
make_counter_test()
輸出:
1 2 3
數據類型檢查能夠用內置函數isinstance()
實現。
def my_abs(x): if not isinstance(x, (int, float)): raise TypeError('bad operand type') if x >= 0: return x else: return -x
def changer(a, b): b = b[:] # 拷貝技巧
a = 2 b[0] = 'spam' # Changes our list copy only
默認是引用,那麼就是本身內部 copy 就行了。
Table 18-1. Function argument-matching forms func(value) Caller Normal argument: matched by position func(name=value) Caller Keyword argument: matched by name func(*iterable) Caller Pass all objects in iterable as individual positional arguments func(**dict) Caller Pass all key/value pairs in dict as individual keyword arguments
def func(name) Function Normal argument: matches any passed value by position or name def func(name=value) Function Default argument value, if not passed in the call def func(*name) Function Matches and collects remaining positional arguments in a tuple def func(**name) Function Matches and collects remaining keyword arguments in a dictionary def func(*other, name) Function Arguments that must be passed by keyword only in calls (3.X) def func(*, name=value) Function Arguments that must be passed by keyword only in calls (3.X)
The *name form collects any extra unmatched positional arguments in a tuple, and the **name form collects extra keyword arguments in a dictionary.
In Python 3.X, any normal or defaulted argument names following a *name or a bare * are keyword-only arguments and must be passed by keyword in calls.
>>> def f(*args): print(args)
>>> f(1,2,3)
(1, 2, 3)
>>> def f(**args): print(args) >>> f() {} >>> f(a=1, b=2) {'a': 1, 'b': 2}
參數命名規範:args, kwargs
>>> def f(a, *pargs, **kargs): print(a, pargs, kargs) >>> f(1, 2, 3, x=1, y=2) 1 (2, 3) {'y': 2, 'x': 1}
相對典型、簡單的例子。
def func(a, b, c, d):
print(a, b, c, d) >>> func(*(1, 2), **{'d': 4, 'c': 3}) # Same as func(1, 2, d=4, c=3) 1 2 3 4
>>> func(1, *(2, 3), **{'d': 4}) # Same as func(1, 2, 3, d=4) 1 2 3 4
>>> func(1, c=3, *(2,), **{'d': 4}) # Same as func(1, 2, c=3, d=4) <--值得注意下! 1 2 3 4
>>> func(1, *(2, 3), d=4) # Same as func(1, 2, 3, d=4) 1 2 3 4
>>> func(1, *(2,), c=3, **{'d':4}) # Same as func(1, 2, c=3, d=4) 1 2 3 4
但若是提起編譯器去判斷,*pargs,就能夠在亂序的狀況下自動調整。
In [25]: echo(1, dd = 10, (2,), d = 1, ) File "<ipython-input-25-1fa964119dd7>", line 1 echo(1,dd = 10,(2,), d = 1, ) ^ SyntaxError: positional argument follows keyword argument
In [29]: pargs = (10, 20) In [30]: echo(1, dd = 10, *pargs, d = 1, ) (1, 10, 20) {'d': 1, 'dd': 10}
Python 3.X Keyword-Only Arguments (難點),具體參見:591/1594
(1) 一顆星
必須讓編譯器知道*b表明的參數組的長度。也就是*b後的參數必須有明確賦值。
def kwonly(a, *b, c): print(a, b, c)
>>> kwonly(1, 2, c=3) 1 (2,) 3
>>> kwonly(a=1, c=3) 1 () 3
>>> kwonly(1, 2, 3) TypeError: kwonly() missing 1 required keyword-only argument: 'c'
也可使用默認參數,默認參數是個好東西。
def kwonly(a, *b='spam', c='ham'): print(a, b, c)
>>> kwonly(1) 1 spam ham
>>> kwonly(1, c=3) 1 spam 3
>>> kwonly(a=1) 1 spam ham
>>> kwonly(c=3, b=2, a=1) 1 2 3
>>> kwonly(1, 2) # 搞不清楚,2是b的,仍是c的 TypeError: kwonly() takes 1 positional argument but 2 were given
>>> def kwonly(a, **pargs, b, c): SyntaxError: invalid syntax >>> def kwonly(a, **, b, c): SyntaxError: invalid syntax
>>> def f(a, *b, **d, c=6): print(a, b, c, d) # Keyword-only before **! SyntaxError: invalid syntax
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # Collect args in header
>>> f(1, 2, 3, x=4, y=5) # Default used 1 (2, 3) 6 {'y': 5, 'x': 4}
>>> f(1, 2, 3, x=4, y=5, c=7) # Override default 1 (2, 3) 7 {'y': 5, 'x': 4}
混合使用時,
>>> def f(a, c=6, *b, **d): print(a, b, c, d) # c is not keyword-only here!
>>> f(1, 2, 3, x=4) 1 (3,) 2 {'x': 4}
注意事項,
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # KW-only between * and **
# 直接告訴了編譯器星號對應的參數(推薦) >>> f(1, *(2, 3), **dict(x=4, y=5)) # Unpack args at call 1 (2, 3) 6 {'y': 5, 'x': 4}
# 不能放在最後 >>> f(1, *(2, 3), **dict(x=4, y=5), c=7) # Keywords before **args! SyntaxError: invalid syntax
>>> f(1, *(2, 3), c=7, **dict(x=4, y=5)) # Override default 1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1, c=7, *(2, 3), **dict(x=4, y=5)) # After or before * 1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1, *(2, 3), **dict(x=4, y=5, c=7)) # Keyword-only in ** 1 (2, 3) 7 {'y': 5, 'x': 4}
有點相似於模板,或者是函數指針之類。
def minmax(test, *args): res = args[0] for arg in args[1:]: if test(arg, res): res = arg return res
def lessthan(x, y): return x < y # See also: lambda, eval def grtrthan(x, y): return x > y
print(minmax(lessthan, 4, 2, 1, 5, 6, 3)) # Self-test code print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))
如此,設計minmax這類的函數就省事些。將定義規則和遍歷操做 」相分離「。
Track and debug
其實就是,在執行func函數前對它能先作點什麼。
def tracer(func, *pargs, **kargs): # Accept arbitrary arguments print('calling:', func.__name__) # 先跟蹤 return func(*pargs, **kargs) # 再執行
deffunc(a, b, c, d): return a + b + c + d
print(tracer(func, 1, 2, c=3, d=4))
其餘實戰,such as Generalized Set Functions,請詳見 597/1594。
補充函數的遞歸。
End.