COMP9021 Principles of Programming WEEK5 Optional

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()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容