Python的一些高级特性
高级特性
列表生成式
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)
参考资料:
[zhenguo]