本节内容

  1. 匿名函数
  2. 装饰器
  3. 列表生成式、迭代器&生成器
  4. 内置函数
  5. Json & pickle 数据序列化

1. 匿名函数

匿名函数就是不需要显式的指定函数

1
2
3
4
5
6
7
8
#这段代码
def calc(n):
    return n**n
print(calc(10))
 
#换成匿名函数
calc = lambda n:n**n #n为匿名函数的形参
print(calc(10)) # calc(10)为匿名函数的调用方式

即匿名函数,不需要def的定义模式,直接用赋值符号+lambda就可以定义匿名函数。

匿名函数只能处理一些简单的运算,类似列表生成式和生成器。如果内部逻辑稍微复杂一点,就不能运行了,只能用正常函数定义了。

匿名函数主要是和其它函数搭配使用:

1
2
3
res = map(lambda x:x**2,[1,5,7,4,8])
for in res:
    print(i)

输出

1
25
49
16
64

示例:

  calc = lambda x :x*3#虽然这是匿名函数,没有函数名,但感觉calc类似于函数名,x为形参,lambda≈def

  calc(3)  #如果calc约等于函数名,则调用时,也是用calc()

   lambda x :x*3  #这一部分叫做匿名函数。

2、装饰器

定义:装饰器本质就是函数。功能是装饰其他函数,就是为其他函数添加附加功能。又名:语法糖。

原则:

  1、不能修改被装饰函数的源代码(使用场景:正在上线运行的程序,在不下线修改的前提下,增添新功能)

  2、不能修改被装饰函数的调用方式(同上)

  以上两点总结为一点:装饰器对被它装饰的函数是完全透明的,即该函数的使用者并不知道这个函数发生了改变。

示例:

  import time

  def timmer(func):  #这个是装饰器

    def warpper(*args,**kwargs):  #*args和**kwargs是用来防止原函数被传递参数用的。

      start_time=time.time()

      func(args,kwargs)

      stop_time=time.time()

      print('the func run time is %s' %(stop_time-start_time))

    return warpper

  @timmer  #装饰器的调用,@+装饰器名     @timmer和test1=timmer(test1)意义一样。

  def test1():  #这是原函数

    time.sleep(3)

    print('in the test1')

  @timmer  # test2=timmer(test2)

  def test2(name):

    print('test2:',name)

  test1()

  test2('gavin')  #其实此时的test2()=warpper(),所以如果原调用方式给原函数传递参数的话,需要在内嵌函数的里层函数中设置参数组*args,**kwargs。

最高级语法糖示例:

import time
user,passwd = 'gavin','abc123'
def auth(auth_type):
print("auth func:",auth_type)
def outer_wrapper(func):  #装饰器的外层
def wrapper(*args, **kwargs):  #装饰器的内层
print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if user == username and passwd == password:
print("\033[32;1mUser has passed authentication\033[0m")
res = func(*args, **kwargs) #home【注意】:加了装饰器后,原函数调用相当于调用了装饰器内层,如果原函数需要返回值,需要在这把原函数返回值保存
print("---after authenticaion ")
return res           #home【注意】:被保存的原函数执行结果,在内层函数的最后用return返回,这样就相当于返还给了原函数的调用地方。
else:
exit("\033[31;1mInvalid username or password\033[0m")
elif auth_type == "ldap":
print("搞毛线ldap,不会。。。。") return wrapper
return outer_wrapper def index():
print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
print("welcome to home page")
return "from home"  #以home为例,这里home函数有返回值。 @auth(auth_type="ldap")
def bbs():
print("welcome to bbs page") index()
print(home()) #wrapper()  #【注意】:home函数的返回值,在这里被使用。所以需要在装饰器的内层的结尾处加入return函数。
bbs()

实现装饰器知识储备:

  1、函数即“变量”

  2、高阶函数

  3、嵌套函数

高阶函数 + 嵌套函数 => 装饰器

1、函数即"变量"

  x=1  #变量名为x,在内存中对应的数据为1

  def test():  #这是函数的定义式

    pass

  所以函数即“变量”类似于

  test = ‘函数体’  #就是‘函数体’是在内存空间的数据,函数名test是函数体的门牌号。

  python的内存回收机制是解释器做的,有一个叫做‘引用计数’的概念。即统计某个内存中的值被引用的次数。

当引用计数值为0时,即某个内存中对应的数据没有对应的“门牌号”时,内存回收机制就会把对应内存数据清空。

  x = 1

  del x  #这个del删除的不是内存地址中的1,而是删除了x这个门牌号。内存中的1是python自带的内存回收机制中的‘引用计数’刷新时,发现1这个内存数据没有引用,被内存回收机制清除的。

2、高阶函数

  满足下面两个条件之一就可以称之为高阶函数:

    1)把一个函数名当做实参传递给另外一个函数。(在不修改被装饰函数源代码的情况下,为其增加新的功能)

    2)返回值中包含函数名(不改变被修饰函数的调用方式)

3、嵌套函数

  定义:在一个函数体内再用def 去定义另外一个函数。如果在一个函数体内,没用def 声明新的函数,只是调用了其他的函数,不叫做函数的嵌套。

  

局部作用域和全局作用域的访问顺序

1
2
3
4
5
6
7
8
9
10
11
x=0
def grandpa():
    # x=1
    def dad():
        x=2
        def son():
            x=3
            print x
        son()
    dad()
grandpa()

 

  

3.列表生成式,迭代器&生成器

列表生成式

现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求把列表里的每个值加1

>>> a = [i+1 for i in range(10)]
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[i+1 for i in range(10)],这就叫做列表生成式。还可以这么写
[func(i) for i in range(10)]  #每次for循环后把临时变量i都传递给func(i)这个函数,这个函数执行后把结果返回到这个列表里作为其中的一个元素。

生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator【定义】。

生成器的特点:

  1、只有在调用的时候才会生成相应的数据。

  2、只记录当前位置

  3、只有一个__next__()方法(在Py 3.x中是前面的写法)

生成器的生成方法1:

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

1
2
3
4
5
6
>>> L = [x * for in range(10)]
>>> L
[0149162536496481]
>>> g = (x * for in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1in <module>
StopIteration

我们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

列表和生成器的区别:

  列表支持下标引用

  生成器不支持任何的下标引用和切片,无论下标是否被生成器曾经生成过。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> g = (x * for in range(10))
>>> for in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

生成器的生成方法2:

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

1
2
3
4
5
6
7
def fib(max):
    n, a, b = 001
    while n < max:
        print(b)
        a, b = b, a + b
        = + 1
    return 'done'

注意,赋值语句:

1
a, b = b, a + b

相当于:

1
2
3
= (b, a + b) # t是一个tuple
= t[0]
= t[1]

但不必显式写出临时变量t就可以赋值。

上面的函数可以输出斐波那契数列的前N个数:

1
2
3
4
5
6
7
8
9
10
11
12
>>> fib(10)
1
1
2
3
5
8
13
21
34
55
done

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
n,a,b = 0,0,1 while n < max:
#print(b)
yield b
a,b = b,a+b n += 1 return 'done'

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

data = fib(10)
print(data) print(data.__next__())
print(data.__next__())
print("干点别的事")
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__()) #输出
<generator object fib at 0x101be02b0>
1
1
干点别的事
2
3
5
8
13

在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

>>> for n in fib(6):
... print(n)
...
1
1
2
3
5
8

但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> g = fib(6)
>>> while True:
...     try:
...         x = next(g)
...         print('g:', x)
...     except StopIteration as e:
...         print('Generator return value:', e.value)
...         break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done

上面例子中的函数作用:

  yield作用是保存了程序的中断状态,并且返回了生成器当前状态的值。

  __next__()或next()的作用是只调用yield但不传值。

  send()的作用是调用yield同时给yield传值。(这个函数在下面的例子中被使用)

还可通过yield实现在单线程的情况下实现并发运算的效果

import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("老子开始准备做包子啦!")
for i in range(10):
time.sleep(1)
print("做了2个包子!")
c.send(i)
c2.send(i) producer("gavin")

上述这种程序是异步I/O的雏形,原理是在单个线程内,多个函数或功能之间快速的互相切换,给人的感觉就是多个功能或过程并发运行。

这种方式我们称之为协程,它是存在于单线程内。

【注意】:

  c = consumer('A')
c2 = consumer('B')

如果consumer是函数,这两句话就是调用函数。函数会直接运行到底并返回结果。但如果consumer是生成器,这两句话只是定义生成器,函数不会运行任何一段代码。此时需要send()或者是next()函数进行调用。

迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如listtupledictsetstr等;

一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

可以使用isinstance()判断一个对象是否是Iterable对象:

1
2
3
4
5
6
7
8
9
10
11
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

【注意迭代器和可迭代对象的定义区别】:

可以直接作用于for循环的对象统称为可迭代对象:Iterable

*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

对一个对象a使用dir方法(即dir(a)),就可以看a的所有可以使用的方法。

可以使用isinstance()判断一个对象是否是Iterator对象:

1
2
3
4
5
6
7
8
9
>>> from collections import Iterator
>>> isinstance((x for in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator可以使用iter()函数 :

1
2
3
4
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

你可能会问,为什么listdictstr等数据类型不是Iterator

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

小结

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

在Python 3.x 中range(10)也是一个迭代器。

同时文件句柄或者说是文件本身也是一个迭代器

Python的for循环本质上就是通过不断调用next()函数实现的,例如:

1
2
for in range(6):
    pass

实际上完全等价于:

# 首先获得Iterator对象:
it = iter([0,1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break

4. 内置函数  

内置函数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii

#compile
f = open("函数递归.py")
data =compile(f.read(),'','exec')
exec(data) #print
msg = "又回到最初的起点"
f = open("tofile","w")
print(msg,"记忆中你青涩的脸",sep="|",end="",file=f) # #slice
# a = range(20)
# pattern = slice(3,8,2)
# for i in a[pattern]: #等于a[3:8:2]
# print(i)
#
# #memoryview
#usage:
#>>> memoryview(b'abcd')
#<memory at 0x104069648>
#在进行切片并赋值数据时,不需要重新copy原列表数据,可以直接映射原数据内存,
import time
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = data
while b:
b = b[1:]
print('bytes', n, time.time()-start) for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = memoryview(data)
while b:
b = b[1:]
print('memoryview', n, time.time()-start)

5.Json & pickle 数据序列化

参考 http://www.cnblogs.com/alex3714/articles/5161349.html

用于序列化的两个模块

  • json,用于字符串 和 python数据类型间进行转换
  • pickle,用于python特有的类型 和 python的数据类型间进行转换

Json模块提供了四个功能:dumps、dump、loads、load

pickle模块提供了四个功能:dumps、dump、loads、load

Python全栈开发-Day4-Python基础4的更多相关文章

  1. python 全栈开发:python基础

    python具有优美.清晰.简单,是一个优秀并广泛使用的语言.诞生于1991年2.python历史 1989年,为了打发圣诞节假期,Guido开始写Python语言的编译器.Python这个名字,来自 ...

  2. Python 全栈开发四 python基础 函数

    一.函数的基本语法和特性 函数的定义 函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的.函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数 ...

  3. python全栈开发-Day8 函数基础

    python全栈开发-Day8 函数 一 .引子 1. 为何要用函数之不用函数的问题 #1.代码的组织结构不清晰,可读性差 #2.遇到重复的功能只能重复编写实现代码,代码冗余 #3.功能需要扩展时,需 ...

  4. Python 全栈开发二 python基础 字符串 字典 集合

    一.字符串 1,在python中,字符串是最为常见的数据类型,一般情况下用引号来创建字符串. >>ch = "wallace" >>ch1 = 'walla ...

  5. Python全栈开发day4

    1.python三元运算 实现方法: result = 值1 if 条件 else 值2 例如: 1 2 3 4 #!/bin/env python #-*- coding:utf-8 -*- nam ...

  6. Python 全栈开发三 python基础 条件与循环

    一. 条件语句 python条件语句是根据一条或多条语句的执行结果的真假(True Or False)来决定代码块的执行. 而执行内容可以多行,以缩进来区分表示同一范围. 1.Python判断条件真假 ...

  7. python全栈开发-Day4 列表

    python全栈开发-Day4 列表 一.首先按照以下几个点展开列表的学习 #一:基本使用 1 用途 2 定义方式 3 常用操作+内置的方法 #二:该类型总结 1 存一个值or存多个值 只能存一个值 ...

  8. Python 全栈开发一 python初识

    1.Python简介 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC ...

  9. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

  10. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

随机推荐

  1. gdb调试程序函数名为问号,什么原因?step by step解决方案

    gdb调试程序函数名为问号,什么原因? http://bbs.chinaunix.net/thread-1823649-1-1.html http://www.bubuko.com/infodetai ...

  2. 20145208 蔡野 《网络对抗》Exp8 Web基础

    20145208 蔡野 <网络对抗>Exp8 Web基础 本实践的具体要求有: (1).Web前端HTML(1分) 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POS ...

  3. Matlab绘制三维曲面(以二维高斯函数为例)

    原文地址为:Matlab绘制三维曲面(以二维高斯函数为例) 寒假学习了一下Python下的NumPy和pymatlab,感觉不是很容易上手.来学校之后,决定继续看完数字图像处理一书.还是想按照上学期的 ...

  4. matlab的三维绘图和四维绘图

    一.三维绘图1.曲线图plot3(X1,Y1,Z1,...):以默认线性属性绘制三维点集(X1,Y1,Z1)确定的曲线plot3(X1,Y1,Z1,LineSpec):以参数LineSpec确定的线性 ...

  5. VC++ 使用CreateProcess创建新进程

    https://www.cnblogs.com/fancing/p/6477918.html #include <windows.h> #include <tchar.h> # ...

  6. python操作串口

    import serial test = serial.Serial("COM1",115200)#这里就已经打开了串口 print(test.portstr) test.writ ...

  7. DataSnap服务器从xe2升级到xe5报错的处理

    DataSnap服务器从xe2升级到xe5环境下,能够正常编译,但运行后会报错,如下图: 处理参考: http://blogs.embarcadero.com/pawelglowacki/2013/0 ...

  8. mysql的 深度使用 - 游标 , 定时器, 触发器 的使用 ?

    游标 叶叫做 光标; 只能使用在 mysql的 存储过程 或函数中! 游标的概念? 为什么要使用 游标? 什么叫 定时器, 就是事件 event! 是在 mysql 5.0以上的版本中, 才能使用支持 ...

  9. CentOS7使用httpd apache 和firewalld打开关闭防火墙与端口

    Centos7 使用systemctl 工具操作命令 systemctl 是Centos7的服务管理工具中的主要工具 ,它融合之前service和chkconfig的功能于一体 一.httpd的设置 ...

  10. java 偏向锁,轻量锁,重量级锁

    synchronized的执行过程: 1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁 2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功 ...