如何实现关注微信公众号怎么设置关注后才能投票后才能投票的功能,快速吸引粉

&函数作为参数
import math
def add(x,y,f):
return f(x) + f(y)print add(25,36,math.sqrt)
&map()函数
  map()是 Python 内置的高阶函数,它接收一个函数 f&和一个&list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。注意:map()函数不改变原有的 list,而是返回一个新的 list。
  代码块功能将输入的小写的字母转换成大写的字母  
def format_name(s):
return s[0].upper() + s[1:].lower()print map(format_name,['adam','LISA','bartT']) & #format-name作用于列表
&reduce()函数
  reduce()函数也是Python内置的一个高阶函数。reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。
例如,编写一个f函数,接收x和y,返回x和y的和:
def f(x, y):
return x + y
  调用&reduce(f, [1, 3, 5, 7, 9])时,reduce函数将做如下计算:
先计算头两个元素:f(1, 3),结果为4;
再把结果和第3个元素计算:f(4, 5),结果为9;
再把结果和第4个元素计算:f(9, 7),结果为16;
再把结果和第5个元素计算:f(16, 9),结果为25;
由于没有更多的元素了,计算结束,返回结果25。
上述计算实际上是对 list 的所有元素求和。虽然Python内置了求和函数sum(),但是,利用reduce()求和也很简单。
  reduce()还可以接收第3个可选参数,作为计算的初始值。如果把初始值设为100,计算:
reduce(f, [1, 3, 5, 7, 9], 100)
结果将变为125,因为第一轮计算是:
计算初始值和第一个元素:f(100, 1),结果为101。
  利用reduce()进行连乘的代码块
def f(x,y):
return x * yprint reduce(f,[2,4,5,7,12])
&filter()函数(过滤函数)
  filter()函数是&Python&内置的另一个有用的高阶函数,filter()函数接收一个函数 f&和一个list,这个函数&f&的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
例如,要从一个list [1, 4, 6, 7, 9, 12, 17]中删除偶数,保留奇数,首先,要编写一个判断奇数的函数:
def is_odd(x):
return x % 2 == 1
然后,利用filter()过滤掉偶数:
filter(is_odd, [1, 4, 6, 7, 9, 12, 17])
结果:[1, 7, 9, 17]
#利用过滤函数filter()进行删除None和空字符串
def is_not_empty(s):
return s and len(s.strip()) & 0l = ['test','str',None,'','','END']print filter(is_not_empty,l)
# 利用函数filter()过滤出1~100中平方根是整数的数import mathl = []for x in range(1,101):
l.append(x)def is_int(x):
r = int(math.sqrt(x))
return r * r == xprint filter(is_int,l)
import math
def is_sqr(x):
r = int(math.sqrt(x))
return r*r==x
print filter(is_sqr, range(1, 101))&自定义排序函数  Python内置的&sorted()函数可对list进行排序:
&&&sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]
但&sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。
因此,如果我们要实现倒序排序,只需要编写一个reversed_cmp函数:
def reversed_cmp(x, y):
这样,调用 sorted() 并传入 reversed_cmp 就可以实现倒序排序:
&&& sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]
sorted()也可以对字符串进行排序,字符串默认按照ASCII大小来比较:
&&& sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
'Zoo'排在'about'之前是因为'Z'的ASCII码比'a'小。
& &对字符串排序时,有时候忽略大小写排序更符合习惯。请利用sorted()高阶函数,实现忽略大小写排序的算法。
l = ['bob','about','Zoo','Credit']def cmp_ignore_case(s1,s2):  u1 = s1.upper()
  u2 = s2.upper()
if u1 & u2:
  return -1
if u1 & u2:
  return 1
return 0print sorted(l,cmp_ignore_case)
  Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数!
例如,定义一个函数 f(),我们让它返回一个函数 g,可以这样写:
print 'call f()...'
# 定义函数g:
print 'call g()...'
# 返回函数g:
仔细观察上面的函数定义,我们在函数 f 内部又定义了一个函数 g。由于函数 g 也是一个对象,函数名 g 就是指向函数 g 的变量,所以,最外层函数 f 可以返回变量 g,也就是函数 g 本身。
调用函数 f,我们会得到 f 返回的一个函数:
&&& x = f()
call f()...
# 变量x是f()返回的函数:
&function g at 0x&
# x指向函数,因此可以调用
call g()...
# 调用x()就是执行g()函数定义的代码
请注意区分返回函数和返回值:
def myabs():
return abs
# 返回函数
def myabs2(x):
return abs(x)
# 返回函数调用的结果,返回值是一个数值
返回函数可以把一些计算延迟执行。例如,如果定义一个普通的求和函数:
def calc_sum(lst):
return sum(lst)
调用calc_sum()函数时,将立刻计算并得到结果:
&&& calc_sum([1, 2, 3, 4])
但是,如果返回一个函数,就可以&延迟计算&:
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum
# 调用calc_sum()并没有计算出结果,而是返回函数:
&&& f = calc_sum([1, 2, 3, 4])
&function lazy_sum at 0x1037bfaa0&
# 对返回的函数进行调用时,才计算出结果:
由于可以返回函数,我们在后续代码里就可以决定到底要不要调用该函数
    请编写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。
lst = [1,2,3]def f(x,y):
  return x * ydef calc_prod(lst):
& & & def h(f,lst):
   & & print reduce(f,lst)
  & &return hx = calc_prod(lst)y = x(f,lst)
  内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。
闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:
# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
for i in range(1, 4):
return i*i
#这里的i是一直在变,但是一直没有循环时调用f(),在循环结束时i已经变为3.再调用i时肯定用的都是i
fs.append(f)
f1, f2, f3 = count()
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果全部都是&9(请自己动手验证)。
在count函数的循环内部,如果借助f函数,就可以避免引用循环变量i。
def count():
for i in range(1, 4):
return j*j
fs.append(r)
f1, f2, f3 = count()
print f1(), f2(), f3()&匿名函数  
高阶函数可以接收函数做参数,有些时候,我们不需要显式地定义函数,直接传入匿名函数更方便。
在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算 f(x)=x2&时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:
&&& map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]
通过对比可以看出,匿名函数&lambda x: x * x&实际上就是:
return x * x
关键字lambda&表示匿名函数,冒号前面的&x 表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。
使用匿名函数,可以不必定义函数名,直接创建一个函数对象,很多时候可以简化代码:
&&& sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y))
[9, 5, 3, 1, 0]
  利用匿名函数简化以下代码:
def is_not_empty(s):
return s and len(s.strip()) & 0
filter(is_not_empty, ['test', None, '', 'str', '
', 'END'])
定义匿名函数时,没有return关键字,且表达式的值就是函数返回值。
print filter(lambda s: s and len(s.strip())&0, ['test', None, '', 'str', '
', 'END'])
&装饰器函数  装饰器的作用  极大的简化代码    打印日志:@log    检测性能:@performance    数据库事务:@transaction    URL路由:@post&编写无参数decorator(装饰函数)  
Python的&decorator&本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数。
使用 decorator 用Python提供的&@&语法,这样可以避免手动编写&f = decorate(f)&这样的代码。
考察一个@log的定义:
def log(f):
#就等于写了一个模块,利用这个模块可以解决一类问题其中的log(f)就代表着一类问题
def fn(x):
print 'call ' + f.__name__ + '()...'
return f(x)
#返回就是将特殊的情况返回输出
对于阶乘函数,@log工作得很好:
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)
call factorial()...
但是,对于参数不是一个的函数,调用将报错:
def add(x, y):
return x + y
print add(1, 2)
Traceback (most recent call last):
File "test.py", line 15, in &module&
print add(1,2)
TypeError: fn() takes exactly 1 argument (2 given)
因为&add()&函数需要传入两个参数,但是&@log&写死了只含一个参数的返回函数。
要让&@log&自适应任何参数定义的函数,可以利用Python的&*args&和&**kw,保证任意个数的参数总是能正常调用:
def log(f):
def fn(*args, **kw):
print 'call ' + f.__name__ + '()...'
return f(*args, **kw)
现在,对于任意函数,@log&都能正常工作。
  编写带参数decorator
考察上一节的&@log&装饰器:
def log(f):
def fn(x):
print 'call ' + f.__name__ + '()...'
return f(x)
发现对于被装饰的函数,log打印的语句是不能变的(除了函数名)。
如果有的函数非常重要,希望打印出'[INFO] call xxx()...',有的函数不太重要,希望打印出'[DEBUG] call xxx()...',这时,log函数本身就需要传入'INFO'或'DEBUG'这样的参数,类似这样:
@log('DEBUG')
def my_func():
把上面的定义翻译成高阶函数的调用,就是:
my_func = log('DEBUG')(my_func)
上面的语句看上去还是比较绕,再展开一下:
log_decorator = log('DEBUG')
my_func = log_decorator(my_func)
上面的语句又相当于:
log_decorator = log('DEBUG')
@log_decorator
def my_func():
所以,带参数的log函数首先返回一个decorator函数,再让这个decorator函数接收my_func并返回新函数:
def log(prefix):
def log_decorator(f):
def wrapper(*args, **kw):
print '[%s] %s()...' % (prefix, f.__name__)
return f(*args, **kw)
return wrapper
return log_decorator
@log('DEBUG')
def test():
print test()
执行结果:
[DEBUG] test()...
对于这种3层嵌套的decorator定义,你可以先把它拆开:
# 标准decorator:
def log_decorator(f):
def wrapper(*args, **kw):
print '[%s] %s()...' % (prefix, f.__name__)
return f(*args, **kw)
return wrapper
return log_decorator
# 返回decorator:
def log(prefix):
return log_decorator(f)
拆开以后会发现,调用会失败,因为在3层嵌套的decorator定义中,最内层的wrapper引用了最外层的参数prefix,所以,把一个闭包拆成普通的函数调用会比较困难。不支持闭包的编程语言要实现同样的功能就需要更多的代码。
练习;根据带参数的修饰函数的定义打印出一个函数运行时间的表示方式的不同
&完善decorator
@decorator可以动态实现函数功能的增加,但是,经过@decorator&改造&后的函数,和原函数相比,除了功能多一点外,有没有其它不同的地方?
在没有decorator的情况下,打印函数名:
def f1(x):
print f1.__name__
有decorator的情况下,再打印函数名:
def log(f):
def wrapper(*args, **kw):
print 'call...'
return f(*args, **kw)
return wrapper
def f2(x):
print f2.__name__
输出:&wrapper
可见,由于decorator返回的新函数函数名已经不是'f2',而是@log内部定义的'wrapper'。这对于那些依赖函数名的代码就会失效。decorator还改变了函数的__doc__等其它属性。如果要让调用者看不出一个函数经过了@decorator的&改造&,就需要把原函数的一些属性复制到新函数中:
def log(f):
def wrapper(*args, **kw):
print 'call...'
return f(*args, **kw)
wrapper.__name__ = f.__name__
wrapper.__doc__ = f.__doc__
return wrapper
这样写decorator很不方便,因为我们也很难把原函数的所有必要属性都一个一个复制到新函数上,所以Python内置的functools可以用来自动化完成这个&复制&的任务:
import functools
def log(f):
@functools.wraps(f)
def wrapper(*args, **kw):
print 'call...'
return f(*args, **kw)
return wrapper
最后需要指出,由于我们把原函数签名改成了(*args, **kw),因此,无法获得原函数的原始参数信息。即便我们采用固定参数来装饰只有一个参数的函数:
def log(f):
@functools.wraps(f)
def wrapper(x):
print 'call...'
return f(x)
return wrapper
也可能改变原函数的参数名,因为新函数的参数名始终是 'x',原函数定义的参数名不一定叫 'x'。
&偏函数  
当一个函数有很多参数时,调用者就需要提供多个参数。如果减少参数个数,就可以简化调用者的负担。
比如,int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:
&&& int('12345')
但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做&N&进制的转换:
&&& int('12345', base=8)
&&& int('12345', 16)
假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:
def int2(x, base=2):
return int(x, base)
这样,我们转换二进制就非常方便了:
&&& int2('1000000')
#指定里面的是几进制然后转换成10进制整数
&&& int2('1010101')
functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
&&& import functools
&&& int2 = functools.partial(int, base=2)
&&& int2('1000000')
&&& int2('1010101')
所以,functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值,这样,新函数调用的难度就降低了。
阅读(...) 评论()【合集】[python问题]没有返回值的函数怎么知道结果 - 饮水思源
饮水思源 - 主题文章阅读  [讨论区: Script]本主题共有 1 篇文章,分 1 页, 当前显示第 1 页 []
[][] 发信人: (Life is lovely?空と海の), 信区: Script
题: 【合集】[python问题]没有返回值的函数怎么知道结果
发信站: 饮水思源 (日10:34:14 星期一), 站内信件
☆──────────────────────────────────────☆
mudfish (泥巴鱼) 于 日14:00:29 星期四)
以chidir函数为例,manual解释如下:
==========================
chdir( path)
Change the current working directory to path. Availability: Macintosh, Unix, W
==========================
chdir没有返回值,怎样判断该命令执行的结果?
网上看到有这样的用法示例
if __name__== "__main__":
   path = chdir('d:\\\\')
如果chdir没有返回值,那么path得到的是什么呢?
path = chdir("d:\\test\\")
print "current path is: ", path
执行的结果如下:
==========================
current path is:
Traceback (most recent call last):
File "D:\code\python\multidelete\multidelete.py", line 48, in &module&
names = os.listdir(path)
TypeError: coercing to Unicode: need string or buffer, NoneType found
==========================
☆──────────────────────────────────────☆
Azureday (zyq-zy?微妙的淡紫?情wR) 于 日14:16:58 星期四 提到:
chdir失败就抛异常了,然后你用try....except
【 在 mudfish (泥巴鱼) 的大作中提到: 】
: 以chidir函数为例,manual解释如下:
: ==========================
: chdir( path)
: Change the current working directory to path. Availability: Macintosh, Unix, W
: ==========================
: chdir没有返回值,怎样判断该命令执行的结果?
: 网上看到有这样的用法示例
: .................(以下省略)
☆──────────────────────────────────────☆
Python (python) 于 日14:24:27 星期四 提到:
【 在 mudfish (泥巴鱼) 的大作中提到: 】
题: [python问题]没有返回值的函数怎么知道结果
: 发信站: 饮水思源 (日14:00:29 星期四)
: 以chidir函数为例,manual解释如下:
: ==========================
: chdir( path)
: Change the current working directory to path. Availability: Macintosh, Unix, W
: ==========================
: chdir没有返回值,怎样判断该命令执行的结果?
我的理解,就无法通过返回值来判定了吧。具体到os.chdir()
如果参数中的目录不存在,会出现异常。如果运行正常,则当前目录就可以通过
os.getcwd()得到,可以作为os.chdir()的验证吧。
In [1]: import os
In [2]: os.chdir(r"d:\temp")
In [3]: os.getcwd()#这儿验证确实切换目录成功
Out[3]: 'd:\\temp'
In [4]: os.chdir(r"d:\test")#这儿说明d:\test目录不存在时报错。
☆──────────────────────────────────────☆
mudfish (泥巴鱼) 于 日17:58:44 星期四)
谢谢斑竹的热心回复,让我大有收获。
花了整整2天时间终于写出了一个实际应用的python程序,开心啊:)
【 在 Python 的大作中提到: 】
: 我的理解,就无法通过返回值来判定了吧。具体到os.chdir()
: 如果参数中的目录不存在,会出现异常。如果运行正常,则当前目录就可以通过
: os.getcwd()得到,可以作为os.chdir()的验证吧。
: In [1]: import os
: In [2]: os.chdir(r"d:\temp")
: In [3]: os.getcwd()#这儿验证确实切换目录成功
: Out[3]: 'd:\\temp'
: In [4]: os.chdir(r"d:\test")#这儿说明d:\test目录不存在时报错。
: (以下引言省略...)
☆──────────────────────────────────────☆
Python (python) 于 日18:48:34 星期四 提到:
【 在 mudfish (泥巴鱼) 的大作中提到: 】
: 谢谢斑竹的热心回复,让我大有收获。
不客气,有帮助就好。
: 花了整整2天时间终于写出了一个实际应用的python程序,开心啊:)
: .................(以下省略)
本主题共有 1 篇文章,分 1 页, 当前显示第 1 页 [][][]Python(27)
return语句
return语句的写法是:
Python代码&
&span&style=&font-family:&'Microsoft&YaHei',&微软雅黑,&SimHei,&tahoma,&arial,&helvetica,&sans-&font-size:&14&color:�&background-color:&#&&return&表达式&/span&&&
return语句用于退出函数,选择性地向调用方返回一个表达式。return在不带参数的情况下,默认返回None。
None是一个特殊的值,它的数据类型是NoneType。NoneType是Python的特殊类型,它只有一个值None。它不知道任何运算也没有任何内建方法,和任何其他的数据类型比较永远返回false。你可以将None复制给任何变量,但是你不能创建其他NoneType对象。实例参考:
Python代码&
&span&style=&font-family:&'Microsoft&YaHei',&微软雅黑,&SimHei,&tahoma,&arial,&helvetica,&sans-&font-size:&14&&&&
def&add(a,b):&&
&&&&sum&=&a&+&b&&
&&&&return&sum&/span&&&
函数定义之后,我们就可以在程序中调用这个函数了。通过函数名+(参数列表)就可以来调用这个函数。
试试调用刚才的add函数吧:
Python代码&
def&add(a,&b):&&&
&&&&sum&=&a&+&b&&&
&&&&return&sum&&&
print&(add(6,7))&&
更多学习内容,就在码芽网/lesson/index&
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:10962次
排名:千里之外
原创:78篇
评论:143条
(19)(28)(25)(6)python(46)
Python 里为什么函数可以返回一个函数内部定义的函数?
Python 初学。
看@函数修饰符的例子。看到一个这样的例子
def spamrun(fn):
def sayspam(*args):
print &spam,spam,spam&
return sayspam
内部定义的函数可以作为返回值……内部这个函数的是不是被复制了一份然后返回给外部了……是不是可以这样理解:
def spamrun(fn):
f = lambda args:print &spam,spam,spam&
我想百度相关的知识的,找了半天没有找到,“函数内部定义函数”这样的关键字没有找到想要的答案。求懂的人指导。
------------------------------------修改补充----------------------------
def addspam(fn): def new(*args): print &spam,spam,spam& return fn(*args) return new @addspam def useful(a,b): print a**2+b**2…&
按投票排序
题主可能并没有理解
“在Python中,函数本身也是对象”
这一本质。那不妨慢慢来,从最基本的概念开始,讨论一下这个问题:
1. Python中一切皆对象
这恐怕是学习Python最有用的一句话。想必你已经知道Python中的list, tuple, dict等内置数据结构,当你执行:
alist = [1, 2, 3]
时,你就创建了一个列表对象,并且用alist这个变量引用它:
当然你也可以自己定义一个类:
class House(object):
def __init__(self, area, city):
self.area = area
self.city = city
def sell(self, price):
#other code
return price
然后创建一个类的对象:
house = House(200, 'Shanghai')
OK,你立马就在上海有了一套200平米的房子,它有一些属性(area, city),和一些方法(__init__, self):
2. 函数是第一类对象
和list, tuple, dict以及用House创建的对象一样,当你定义一个函数时,函数也是对象:
def func(a, b):
return a+b
在全局域,函数对象被函数名引用着,它接收两个参数a和b,计算这两个参数的和作为返回值。
所谓第一类对象,意思是可以用标识符给对象命名,并且对象可以被当作数据处理,例如赋值、作为参数传递给函数,或者作为返回值return 等
因此,你完全可以用其他变量名引用这个函数对象:
add = func
这样,你就可以像调用func(1,
2)一样,通过新的引用调用函数了:
print func(1, 2)
print add(1, 2)
#the same as func(1, 2)
或者将函数对象作为参数,传递给另一个函数:
def caller_func(f):
return f(1, 2)
if __name__ == &__main__&:
print caller_func(func)
可以看到,
因此输出结果为3。
3. 函数对象 vs 函数调用
无论是把函数赋值给新的标识符,还是作为参数传递给新的函数,针对的都是函数对象本身,而不是函数的调用。
用一个更加简单,但从外观上看,更容易产生混淆的例子来说明这个问题。例如定义了下面这个函数:
def func():
return &hello,world&
然后分别执行两次赋值:
ref1 = func
#将函数对象赋值给ref1
ref2 = func()
#调用函数,将函数的返回值(&hello,world&字符串)赋值给ref2
很多初学者会混淆这两种赋值,通过Python内建的type函数,可以查看一下这两次赋值的结果:
In [4]: type(ref1)
Out[4]: function
In [5]: type(ref2)
Out[5]: str
可以看到,ref1引用了函数对象本身,而ref2则引用了函数的返回值。通过内建的callable函数,可以进一步验证ref1是可调用的,而ref2是不可调用的:
In [9]: callable(ref1)
Out[9]: True
In [10]: callable(ref2)
Out[10]: False
传参的效果与之类似。
4. 闭包&LEGB法则
所谓闭包,就是将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象
听上去的确有些复杂,还是用一个栗子来帮助理解一下。假设我们在foo.py模块中做了如下定义:
filename = &foo.py&
def call_func(f):
return f()
#如前面介绍的,f引用一个函数对象,然后调用它
在另一个func.py模块中,写下了这样的代码:
import foo
#导入foo.py
filename = &func.py&
def show_filename():
return &filename: %s& % filename
if __name__ == &__main__&:
print foo.call_func(show_filename)
#注意:实际发生调用的位置,是在foo.call_func函数中
当我们用python func.py命令执行func.py时输出结果为:
chiyu@chiyu-PC:~$ python func.py
filename:func.py
很显然show_filename()函数使用的filename变量的值,是在与它相同环境(func.py模块)中定义的那个。尽管foo.py模块中也定义了同名的filename变量,而且实际调用show_filename的位置也是在foo.py的call_func内部。
而对于嵌套函数,这一机制则会表现的更加明显:闭包将会捕捉内层函数执行所需的整个环境:
#enclosed.py
import foo
def wrapper():
filename = &enclosed.py&
def show_filename():
return &filename: %s& % filename
print foo.call_func(show_filename)
#输出:filename: enclosed.py
实际上,每一个函数对象,都有一个指向了该函数定义时所在全局名称空间的__globals__属性:
#show_filename inside wrapper
#show_filename.__globals__
'__builtins__': &module '__builtin__' (built-in)&,
#内建作用域环境
'__file__': 'enclosed.py',
'wrapper': &function wrapper at 0x7f&,
#直接外围环境
'__package__': None,
'__name__': '__main__',
'foo': &module 'foo' from '/home/chiyu/foo.pyc'&,
'__doc__': None
当代码执行到show_filename中的return &filename: %s& % filename语句时,解析器按照下面的顺序查找filename变量:
在任何一层先找到了符合要求的filename变量,则不再向更外层查找。如果直到Builtin层仍然没有找到符合要求的变量,则抛出NameError异常。这就是变量名解析的:LEGB法则。
5. 装饰器&语法糖(syntax sugar)
那么闭包和装饰器又有什么关系呢?
上文提到闭包的重要特性:封存上下文,这一特性可以巧妙的被用于现有函数的包装,从而为现有函数更加功能。而这就是装饰器。
还是举个例子,代码如下:
#alist = [1, 2, 3, ..., 100]
--& 1+2+3+...+100 = 5050
def lazy_sum():
return reduce(lambda x, y: x+y, alist)
我们定义了一个函数lazy_sum,作用是对alist中的所有元素求和后返回。alist假设为1到100的整数列表:
alist = range(1, 101)
但是出于某种原因,我并不想马上返回计算结果,而是在之后的某个地方,通过显示的调用输出结果。于是我用一个wrapper函数对其进行包装:
def wrapper():
alist = range(1, 101)
def lazy_sum():
return reduce(lambda x, y: x+y, alist)
return lazy_sum
lazy_sum = wrapper()
#wrapper() 返回的是lazy_sum函数对象
if __name__
== &__main__&:
lazy_sum()
这是一个典型的Lazy Evaluation的例子。我们知道,一般情况下,局部变量在函数返回时,就会被垃圾回收器回收,而不能再被使用。但是这里的alist却没有,它随着lazy_sum函数对象的返回被一并返回了(这个说法不准确,实际是包含在了lazy_sum的执行环境中,通过__globals__),从而延长了生命周期。
当在if语句块中调用lazy_sum()的时候,解析器会从上下文中(这里是Enclosing层的wrapper函数的局部作用域中)找到alist列表,计算结果,返回5050。
当你需要动态的给已定义的函数增加功能时,比如:参数检查,类似的原理就变得很有用:
def add(a, b):
return a+b
这是很简单的一个函数:计算a+b的和返回,但我们知道Python是&动态类型+强类型&的语言,你并不能保证用户传入的参数a和b一定是两个整型,他有可能传入了一个整型和一个字符串类型的值:
In [2]: add(1, 2)
In [3]: add(1.2, 3.45)
Out[3]: 4.65
In [4]: add(5, 'hello')
---------------------------------------------------------------------------
Traceback (most recent call last)
/home/chiyu/&ipython-input-4-f2f9e8aa5eae& in &module&()
----& 1 add(5, 'hello')
/home/chiyu/&ipython-input-1-02b3d3d6caec& in add(a, b)
1 def add(a, b):
return a+b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
于是,解析器无情的抛出了一个TypeError异常。
动态类型:在运行期间确定变量的类型,python确定一个变量的类型是在你第一次给他赋值的时候;
强类型:有强制的类型定义,你有一个整数,除非显示的类型转换,否则绝不能将它当作一个字符串(例如直接尝试将一个整型和一个字符串做+运算);
因此,为了更加优雅的使用add函数,我们需要在执行+运算前,对a和b进行参数检查。这时候装饰器就显得非常有用:
import logging
logging.basicConfig(level = logging.INFO)
def add(a, b):
return a + b
def checkParams(fn):
def wrapper(a, b):
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
#检查参数a和b是否都为整型或浮点型
return fn(a, b)
#是则调用fn(a, b)返回计算结果
#否则通过logging记录错误信息,并友好退出
logging.warning(&variable 'a' and 'b' cannot be added&)
return wrapper
#fn引用add,被封存在闭包的执行环境中返回
if __name__ == &__main__&:
#将add函数对象传入,fn指向add
#等号左侧的add,指向checkParams的返回值wrapper
add = checkParams(add)
add(3, 'hello')
#经过类型检查,不会计算结果,而是记录日志并退出
注意checkParams函数:
当调用add = checkParams(add)时,add指向了新的wrapper对象,它添加了参数检查和记录日志的功能,同时又能够通过封存的fn,继续调用原始的add进行+运算。
因此调用add(3, 'hello')将不会返回计算结果,而是打印出日志:
chiyu@chiyu-PC:~$ python func.py
WARNING:root:variable 'a' and 'b' cannot be added
有人觉得add = checkParams(add)这样的写法未免太过麻烦,于是python提供了一种更优雅的写法,被称为语法糖:
@checkParams
def add(a, b):
return a + b
这只是一种写法上的优化,解释器仍然会将它转化为add = checkParams(add)来执行。
6. 回归问题
def addspam(fn):
def new(*args):
print &spam,spam,spam&
return fn(*args)
return new
def useful(a,b):
print a**2+b**2
首先看第二段代码:
再回到addspam函数体:
最后附上一张代码执行过程中的引用关系图,希望能帮助你理解:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:696022次
积分:10472
积分:10472
排名:第1264名
原创:98篇
转载:1516篇
译文:25篇
评论:18条
(8)(58)(86)(53)(33)(108)(37)(62)(14)(1)(45)(16)(2)(2)(2)(4)(37)(5)(13)(4)(40)(10)(1)(9)(21)(2)(7)(10)(20)(57)(39)(30)(11)(18)(20)(33)(41)(7)(10)(36)(24)(60)(39)(49)(66)(53)(57)(41)(28)(26)(55)(17)(5)(20)(62)(44)}

我要回帖

更多关于 微信公众号怎么设置关注后才能投票 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信