[Python] 07 - Statements --> Functions

故事背景


1、阶级关系

1. Programs are composed of modules.
2. Modules contain statements.
3. Statements contain expressions.
4. Expressions create and process objects.html

 

 

2、教学大纲 

 

 

 

3、考点

1、异常处理

经常使用异常类型

更多内容详见: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

捕捉到了异常,可是又想从新引起它(传递异常),可使用不带参数的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后,讲异常传递给外层函数的异常处理部分。

 

2、异常基类

异常后再也不须要走“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

 

3、变量初始化技巧

X = A or B or C or None

 

4、Closure 和 Non-local 的迷惑 

大函数返回小函数小函数仅能"只读"大函数的局部变量。

若是“可写”,则须要Non-local。

 

5、万能传参

典型的装饰器写法。

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!

 

 

 

Statements


1、大纲纵览  

Python 3.X’s statements

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

 

Python 3.X reserved words

 

 

2、疑难点

上述疑难api在此总结。

 

pass

pass 是空语句,是为了保持程序结构的完整性。 pass 不作任何事情,通常用作占位语句。

好比定义一个空函数是“报错的”,空函数的内容是pass就行了。

 

星号

其中一个*号表明list或者tuple,**表明map或者dict

 

 

 

若干新特性


1、异常处理

介绍 - 处理输入错误

“输入”的内容很差预测,也是“异常多发地”,故在此用来举例。

(1) try...except方法

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')

 

(2) isdigit 方法判断

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')

 

(3) 多个 except

一个 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

  

(4) try ... else

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 

 

用户自定义异常

(a) 主动抛出异常 raise

使用 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

 

(b) 自定义异常

"异常类" 继承自 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!'

  

(c) 自定义异常基类

当建立一个模块有可能抛出多种不一样的异常时,一种一般的作法是:

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

 

定义清理行为 - finally

finally 关键字

>>>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 语句就能够保证诸如文件之类的对象在使用完以后必定会正确的执行他的清理方法。

 

 

2、变量初始化技巧

Level 1 - 弱初始化

 

Level 2 - 配对赋值

>>> 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

 

Level 3 - rest赋值

(1) 方便个别变量赋值

不是指针,而是rest

>>> a, *b, c = 'spam'
>>> a, b, c
('s', ['p', 'a'], 'm')

 

(2) 方便提取个别变量

循环遍历时貌似有一点点好处

>>> 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 []

 

(3) 剩下的天然是一个列表

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 []

 

Level 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

 

 

3、循环到头了

while ... else 

while test: # Loop test
    statements # Loop body
else: # Optional else
    statements # Run if didn't exit loop with break

这个else是个很好的东西,表示循环走到头了;有益代码阅读。

 

for ... 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】

 

 

 

Expressions


1、Python Scope

简介 - 各有各的做用域

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 “globalin 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.
Rules 

 

本段落,主要关注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

 

 

2、Lambda & Closures

lambda 怎么用比较合理?

Ref: https://www.zhihu.com/question/20125256

(1). 防止碎片函数污染

这里实际上是对函数处理后的结果排序。

sorted(list1, key=lambda x: abs(x))

 

(2). 做用域的延续

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)        # 在设置“小函数”的参数

 

Factory Functons: Clusures

(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

 

 

3、Non-local

只读 “外层变量”

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

 

 

4、Argument

参数类型检查

数据类型检查能够用内置函数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 就行了。

 

Arbitrary Arguments:星号传参 

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.

(1) 一颗星,list

>>> def f(*args): print(args)
>>> f(1,2,3)
(1, 2, 3)

 

(2) 两颗星,dict

>>> def f(**args): print(args)
>>> f()
{}
>>> f(a=1, b=2)
{'a': 1, 'b': 2}

 

(3) mixed星

参数命名规范: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}

 

(4) 更多例子

相对典型、简单的例子。

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

 

(5) 触发编译器自动判断

但若是提起编译器去判断,*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

 

(2) 两颗星

>>> 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}

 

小实战 结合 “函数指针”

minmax  (python minmax.py)

有点相似于模板,或者是函数指针之类。

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.

相关文章
相关标签/搜索