1.写列表生成式时,把要生成的元素x **2
放到前面,后面跟for
循环,这是一般的用法。
>>> b = [x**2 for x in range(0,11)] >>> b [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
2.列表生成式也可以输出某个目录下所有文件和文件夹的名称。
>>> b = [d for d in os.listdir(r'C:\Users\Administrator\Desktop\2005期基础班')] >>> b ['.idea', '2005-林永好-考试.py', 'day02', 'day02_homework', 'day03', 'day03_homework', 'day04', 'day04_homework', 'day05', 'day06', 'day07', 'day08', 'day09', 'day10', 'day11', 'day12', 'day13', 'day14', 'day15', 'day16', 'day17', 'day18']
3.列表生成式可以带有两个参数
>>> a = range(5) >>> b = ['a', 'b', 'c', 'd', 'e'] >>> c = [str(x) + str(y) for x, y in zip(a, b)] >>> c ['0a', '1b', '2c', '3d', '4e']
>>> a = {'a': 1, 'b': 2, 'c': 3} >>> b = [k + '=' + str(v) for k, v in a.items()] >>> b ['a=1', 'b=2', 'c=3']
4.除此之外,还可以跟上if
,这个if
的作用就是过滤,而不是一个表达式,后面不能跟else
>>> b = [x**2 for x in range(11) if x % 2 == 0] >>> b [0, 4, 16, 36, 64, 100]
那如果还想添加上else
,那就更改一下写法。此时的for
前面的if....else
是一个表达式,每一个x
都要根据if..else
算出一个结果,然后生成表达式。
>>> [x if x % 2 == 0 else -x for x in range(1, 11)] [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
此外还有其他几个更高级的用法
# 根据指定函数fn对lst分组 def bifurcate_by(lst, fn): return [ [x for x in lst if fn(x)], [x for x in lst if not fn(x)] ] print(bifurcate_by(['beep', 'boop', 'foo', 'bar'], lambda x: x[0] == 'b')) # [['beep', 'boop', 'bar'], ['foo']]
# 将值分组 def bifurcate(lst, filter): return [ [x for i, x in enumerate(lst) if filter[i] == True], [x for i, x in enumerate(lst) if filter[i] == False] ] print(bifurcate(['beep', 'boop', 'foo', 'bar'], [True, True, False, True])) # [['beep', 'boop', 'bar'], ['foo']]
生成器可以实现在迭代的同时生成元素,
第一种创建生成器的方法:把列表生成的[] 换成 ()
>>> gene = (x * x for x in range(10)) >>> gene <generator object <genexpr> at 0x0000015152CE1D68> # 那如何调用呢? 第一种调用方式:next() >>> next(gene) 0 >>> next(gene) 1 >>> next(gene) 4
我们也可以利用for
循环来不断调用这个生成器(本质也是不断next()),因为gene
既是一个生成器对象,也是一个可迭代对象。
>>> g = (x * x for x in range(10)) >>> for n in g: ... print(n)
第二种创建生成器的方法:定义一个以yield
关键字标识返回值的函数
def odd(): print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5)
yield
除了可以返回相应的值,还有一个重要的功能是,每当程序执行完该语句时,程序就会暂停执行,再次执行时,从上次返回的yield
语句处继续执行
>>> o = odd() # 创建一个生成器对象 >>> next(o) # 获取第一个返回值 step 1 1 >>> next(o) # 此时调用next(o)时,程序就会从print('step 2')继续执行 step 2 3 >>> next(o) step 3 5 >>> next(o) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
迭代器:支持迭代的容器类对象。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
可以使用isinstance()
判断一个对象是否是Iterable
对象:
>>> from collections.abc import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False # list,dict,str,生成器,带yield的生成器函数 都是可迭代对象
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
可以使用isinstance()
判断一个对象是否是Iterator
对象:
>>> from collections.abc import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False # 生成器其实也是迭代器 # 但是 list,dict,str 不是迭代器
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
>>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True
闭包其实也就是一个函数,只是这个函数的返回结果是内层函数的引用,内部函数可以引用外部函数的参数和局部变量。
def sum(a): def add(b): return a +b return add num2 = sum(2) num3 = sum(3) print(type(num2)) print(num3(3)) >>> <class 'function'> # 其实就是返回一个函数的引用 >>> 6
它的用处是什么呢,以上面的例子来说,我们可以根据需要再计算出总和。
虽然我们通过num2 = sum(2) 其中a=2
num3 = sum(3) 其中a=3
,但是b
是个未知数,我们可以根据自己的需要来算出 任何b+2
或者b+3
当我们调用sum()
时,每次调用都会返回一个新的函数,即使传入相同的参数,num4()
和num2()
的结果互不影响
num4 = sum(2) print(num2 == num4) >>> False
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() # f1,f2,f3 是fs的三个元素 >>> f1() 9 >>> f2() 9 >>> f3() 9 # 为什么结果都是一样的, 当你调用f1()时,此时所引用的变量i是3,因此会返回9 # 同理,f2和f3都是
# 如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值, # 无论该循环变量后续如何更改,已绑定到函数参数的值不变 def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f() return fs
关键字lambda
表示匿名函数,冒号前面的x
表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return
,返回值就是该表达式的结果。
>>> f = lambda x: x * x # def f(x): # return x*x >>> f <function <lambda> at 0x101c6ef28> >>> f(5) 25
匿名函数也是一个函数对象,要调用匿名函数,要赋于一个变量
匿名函数也可以作为函数的返回值
import time def after_add(func): def add_thing(): print('----装饰前------') func() print('----装饰后------') return add_thing def add_1(): print((time.strftime('%H:%M:%S',time.localtime(time.time())))) add_2 = after_add(add_1) # add_2 其实就是个函数对象 add_2() ----装饰前------ 16:15:25 ----装饰后------
利用我们前面所将的闭包知识,after_add其实就是形成了一个闭包,而要讲的装饰器其实就是add_2 = after_add(add_1)
那我们为什么要使用装饰器呢?如果你想要 调用add_1
前后打印----装饰前------
和----装饰后------
,而且又不希望修改add_1
函数的定义呢?
因此引出装饰器的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
借助Python的@语法
import time def after_add(func): print("22") def add_thing(): print('----装饰前------') func() print('----装饰后------') return add_thing @after_add add_1 = after_add(add_1) def add_1(): print((time.strftime('%H:%M:%S',time.localtime(time.time())))) if __name__== '__main__': add_1() 22 ----装饰前------ 16:15:25 ----装饰后------ 记住两个原则: 1、装饰器在函数调用之前被增强 2、相同的函数只被增强一次
把@after_add
放在add_1
的定义处,相当于执行语句
add_1 = after_add(add_1)
原来的add_1()
函数仍然存在,只是同名的add_1
变量指向了新的函数,于是调用add_1()
将执行新函数,也就是在add_thing()
里面的func()
函数。换句话说就是下面的例子。
add_2 = after_add(add_1) # add_2 其实就是个函数对象 add_2()
带参数的函数装饰器
def say_thing(func): def say_say(name): # 目标函数的参数供自己使用 print("Hello") func(name) return say_say @say_thing def say_name(name): print(name) print(say_name.__name__) say_name('Li Ming')
首先say_name()
经过增强了, @say_thing 其实相当于执行 say_name= say_thing(say_name)
打印say_name.__name__
时显示的是say_say
,当我们执行say_name("Li Ming")
时,相当于进入say_say(name),其中name=='Li Ming'
然后打印Helllo
,接着执行func(name) 其中name=='Li Ming'
,
而这一句就会进入say_name(name) 其中name=='Li Ming'
说白了 say_name('Li Ming')
相当于执行语句
say_thing(say_name)('Li Ming')
首先执行say_thing(say_name)
,返回的是say_say
函数,在调用返回的函数,参数是'Li Ming'
如果有多个装饰器,它是从内往外增强的。
Python的functools
模块提供了很多有用的功能,其中一个就是偏函数(Partial function)
int()
函数可以把字符串转换为整数(默认是按照十进制转换),同时也可以传入base
参数,做N进制的转换
>>> int('12',base=8) 10 # 字符串`12`经过8进制转换,转成整数10=1*(8^1)+2*(8^0) >>> int('10',base=8) 8
那如果要转换大量的二进制字符串,除了可以自己定义函数,还可以利用functools.partial
functools.partial
就是帮助我们创建一个偏函数的,可以直接使用下面的代码创建一个新的函数int2
:
>>> import functools >>> int2 = functools.partial(int, base=2) # 把base参数重新设定为默认值2 >>> int2('1000000') 64 >>> int2('1010101') 85
它的作用就是把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
最后,创建偏函数时,实际上可以接收函数对象、*args
和**kw
这3个参数
当传入:
int2 = functools.partial(int, base=2) 实际上固定了int函数的关键字参数base
也就是说
int2('10010') 相当于 kw = { 'base': 2 } int('10010', **kw)
当传入:
max2 = functools.partial(max, 10) # 实际上会把10作为*args的一部分自动加到左边 max2(5,6,7)
相当于
args = (10, 5, 6, 7) max(*args)
参考资料:
python语法糖
廖雪峰的官方网站
[zhenguo]