Python的一些高级特性

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 不是迭代器

listdictstrIterable变成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]

上一篇:ES6箭头函数(箭头函数和普通函数的区别)


下一篇:es6默认传参 es5的默认传参