1. Quiz_3 hints
最大的难点是如何避免完全重复的stairs,比如一个3 step的size 2 stairs包含2个2 step的size 2 stairs和3个1 step的size 2 stairs。解决方法是做标记,以3 step的size 2 stairs为例:
#1 2 3
#2 1 2
#3 3 2
#4 1 2
第一行的stairs有3个step,size2;第二行进入的时候会重复遇见从1开始的另一个2个stp,size2的,它是前面staris的一部分,不能计算。所以要想一个解决办法,既能做标记,同时不影响其他size查找的工作。做标记要求不能出现矩阵中有的数字,所以倾向于使用negative integer;不能影响其他size就是说最好只和当下size有关,所以倾向于用size大小做标记。两条叠加,最终确定使用-size做标记。这样上例变成:
#1 2 3
#2 1 -2
#3 3 -2
#4 1 -2
这样从第二行进入的时候一旦改变方向的数字是-size则放弃,同时当size2全部检索完成之后,标记-2和其他的非0数字全部一样,不对size3造成影响,size3的标记是-3.
from random import randrange
def print_grid():
for i in range(10):
print(' '.join(str(int(e != 0)) for e in grid[i]))
density = 2
grid = [[randrange(density) for _ in range(10)] for _ in range(10)]
print_grid()
#每次搜索全部的同一个size,比如size2,如果完成一个step,在最后的部分把数字改成-size,以避免sub-stairs
2. 函数嵌套
def f(a):
def g (b):
return a * b
return g
print(f(2))
print(f(2)(3))
>>>
<function f.<locals>.g at 0x10e608730>
6
对于函数f来说,输入值是a,返回值是函数g;
对于函数g来说,输入值是b,返回值是函数f的输入值和b的乘积。
所以f(2)输出的是一个函数的内存地址;而对于f(2)(3)而言,a = 2,g = f(2),所以f(2)(3) = g(3),b = 3,所以a * b = 6,接着g函数返回6,所以a函数返回g函数的返回值6,最终输出6。
3. 变化的input variable
很多时候一个函数不能确定有多少输入值,比如定义一个未知用户数量计算平均分的函数,此时多少用户使用是未知的,当然可以直接定义一个变量表征用户数量,但也可以使用可变化的input variable。
python中常见的flexible input viriable有两个:*args和**kwargs。
(1)*args
*args is used to send a non-keyworded variable length argument list to the function.例如:
def test_var_args(f_arg, *argv):
print ("first normal arg:", f_arg)
for arg in argv:
print ("another arg through *argv:", arg)
test_var_args('yasoob','python','eggs','test')
>>>
first normal arg: yasoob
another arg through *argv: python
another arg through *argv: eggs
another arg through *argv: test
(2)**kwargs
**kwargs allows you to pass keyworded variable length of arguments to a function. You should use **kwargs if you want to handle named arguments in a function. 例如:
def greet_me(**kwargs):
if kwargs is not None:
for key, value in kwargs.items():
print ("%s == %s" % (key,value))
greet_me(name = "yasoob")
>>>
name == yasoob
(3)实例
通常我们要评估一个程序的运算时间,应用函数嵌套+变化的input variable,实现方式如下
def running_time(f):
def g(*args):
#因为这个函数是对其他函数的运行时间进行计算,而其他函数的input个数是随意的,可以是1个,也可能是多个,所以使用*args
from time import time
before = time()
f(*args)
after = time()
print(f'It took {after - before} seconds to execute the function.')
return g
def h(n):
for _ in range(n):
pass
running_time(h)(10_000_000)
# running_time(h) = function g returned by running_time with f initialised to h
#上面的嵌套函数running_time(h)(10_000_000)和下面的代码等价
h = running_time(h)
h(10_000_000)
>>>
It took 0.316251277923584 seconds to execute the function.
It took 0.3344690799713135 seconds to execute the function.
4. Decorator
Decorator是一种简化写法,方便函数的嵌套,例如上文实例可以用decorator实现如下:
def running_time(f):
def g(*args):
from time import time
before = time()
f(*args)
after = time()
print(f'It took {after - before} seconds to execute the function.')
return g
@running_time
def h(n):
for _ in range(n):
pass
h(10_000_000)
decorator的好处是不需要复杂的操作,方便理解。上面程序可以理解为写了两个函数running_time(f)和h(n),如果调用h(n)函数,则自动嵌套running_time(f)。
@running_time可以理解为
h = running_time(h)
5. Decorator实例
应用上文的decorator方法实现函数调用的计数,这里新引入了另一个函数的attribute,代码中用符号'.'来表示。
def count_function_calls(f):
def super_f(*args):
f(*args)
super_f.count += 1
#在super_f()函数的attribute中加入一个count的统计
super_f.count = 0
return super_f
@count_function_calls
def hi():
print('Hello!')
#每次调用hi()函数的时候都会嵌套函数count_function_calls(f),这里的decorator(@count_function_calls)等价于
#hi = count_function_calls(hi)
hi()
hi()
hi()
hi.count
>>>
Hello!
Hello!
Hello!
3
如果不使用函数的attribute来记录count,使用下面的代码不成立,要小心local和global variable
def count_function_calls(f):
count = 0
def super_f(*args):
f(*args)
count += 1
return super_f
#不成立,因为count是local variable
@count_function_calls
def hi():
print('Hello!')
hi()
hi()
hi()
hi.count
如果把count改成global variable则可以运行,count = 0并不会执行多次,从而影响count的数值。编译过程是:
1.先编译count_function_calls(),这是count被赋值0;
2.再编译hi();
3.再编译decorater @count_function_calls(),此后执行的所有hi()都是嵌套函数,并不会重新给count赋值0;
所以count = 0只初始化一次,在最开始编译count_function_calls()的时候。
def count_function_calls(f):
count = 0
def super_f(*args):
nonlocal count
f(*args)
count += 1
print(f'Function called for the {count}th time.')
return super_f
#成立,因为count是nonlocal variable
@count_function_calls
def hi():
print('Hello!')
hi()
hi()
hi()