函数基础:
- 面向对象:基本单元-->类>>>class
- 面向过程:基本单元-->过程>>>def :
- 函数式编程:基本单元-->函数>>>def ;调用-->fun()
注意:
(1)、函数里面最好写函数说明,方便别人阅读;
(2)、关键参数不能写在位置参数前面;eg:test(3,x=2,4)
(3)、全局变量和局部变量
函数与函数式编程:
特点:允许把函数本身作为参数传入了一个函数,还返回一个函数!
eg:var result = substract(multiply(add(1,2),3),4)) #(1+2)*3/4
#语法
def 函数名(参数1,参数2,参数3,...):
'''注释'''
函数体
return 返回的值
#函数名要能反映其意义
eg:
import time
def logger():
time_format = '%Y-%m-%d %X'
time_current = time.strftime(time_format)
with open('text.txt', 'a+') as f:
f.write('welcome you...%s' % time_current)
def fun1():
'''面向函数说明'''
print("fun1...")
logger() # logger()必须先于fun1定义:
def fun2():
'''面向过程说明'''
print("fun2...")
logger() # logger()必须先于fun2定义:
fun1()
fun2()
函数使用的原则:先定义,再调用;
函数在定义阶段,只检测语法,不执行代码,也就说,语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在执行时才会知道
定义函数的三种形式
#1、无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印
#2、有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值
#3、空函数:设计代码结构
#1、位置参数:按照从左到右的顺序定义的参数
位置形参:必选参数
位置实参:按照位置给形参传值
#2、关键字参数:按照key=value的形式定义的实参
无需按照位置为形参传值
注意的问题:
1. 关键字实参必须在位置实参右面
2. 对同一个形参不能重复传值
#3、默认参数:形参在定义时就已经为其赋值
可以传值也可以不传值,经常需要变的参数定义成位置形参,变化较小的参数定义成默认参数(形参)
注意的问题:
1. 只在定义时赋值一次
2. 默认参数的定义应该在位置形参右面
3. 默认参数通常应该定义成不可变类型
#4、可变长参数:
可变长指的是实参值的个数不固定
而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs
===========*args===接受N个位置参数值转换为元组的形式========
def foo(x,y,*args):
print(x,y)
print(args)
foo(1,2,3,4,5) #1 2 (3,4,5)
def foo(x,y,*args):
print(x,y)
print(args)
foo(1,2,*[3,4,5]) #1 2 (3,4,5)
def foo(x,y,z):
print(x,y,z)
foo(*[1,2,3]) #1 2 3
=======**kwargs===接受N个关键字参数转换为字典形式========
def foo(x,y,**kwargs):
print(x,y)
print(kwargs)
foo(1,y=2,a=1,b=2,c=3) #1 2 {'b': 2, 'a': 1, 'c': 3}
def foo(x,y,**kwargs):
print(x,y)
print(kwargs)
foo(1,y=2,**{'a':1,'b':2,'c':3}) # 1 2 {'b': 2, 'a': 1, 'c': 3}
def foo(x,y,z):
print(x,y,z)
foo(**{'z':1,'x':2,'y':3}) #2 3 1
===========*args+**kwargs===========
def foo(x,y):
print(x,y)
def wrapper(*args,**kwargs):
print('====>')
foo(*args,**kwargs)
#5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
可以保证,传入的参数中一定包含某些关键字
def foo(x,y,*args,a=1,b,**kwargs):
print(x,y)
print(args)
print(a)
print(b)
print(kwargs)
foo(1,2,3,4,5,b=3,c=4,d=5)
结果:
1 2
(3, 4, 5)
1
3
{'c': 4, 'd': 5}
函数对象:
函数是第一类对象,即函数可以当作数据传递
#1 可以被引用
#2 可以当作参数传递
#3 返回值可以是函数
#3 可以当作容器类型的元素
利用该特性,优雅的取代多分支的if
def foo():
print('foo')
def bar():
print('bar')
dic={
'foo':foo,
'bar':bar,
}
while True:
choice=input('>>: ').strip()
if choice in dic:
dic[choice]()
函数的嵌套
函数的嵌套调用
def max(x,y):
return x if x > y else y
def max4(a,b,c,d):
res1=max(a,b)
res2=max(res1,c)
res3=max(res2,d)
return res3
print(max4(1,2,3,4))
函数的嵌套定义
def f1():
def f2():
def f3():
print('from f3')
f3()
f2()
f1()
名称空间和作用域
#名称空间:存放名字的地方,三种名称空间,(x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方)
名称空间的加载顺序
python test.py
#1、python解释器先启动,因而首先加载的是:内置名称空间
#2、执行test.py文件,然后以文件为基础,加载全局名称空间
#3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
名字的查找顺序
局部名称空间--->全局名称空间--->内置名称空间
#需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例
# max=1
def f1():
# max=2
def f2():
# max=3
print(max)
f2()
f1()
print(max)
作用域
#1、作用域即范围
- 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
- 局部范围(局部名称空间属于该范围):临时存活,局部有效
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
x=1
def f1():
def f2():
print(x)
return f2
x=100
def f3(func):
x=2
func()
x=10000
f3(f1())
#3、查看作用域:globals(),locals()
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间
闭包:
#内部函数包含对外部作用域而非全局作用域的引用
#提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路
def counter():
n=0
def incr():
nonlocal n
x=n
n+=1
return x
return incr
c=counter()
print(c())
print(c())
print(c())
print(c.__closure__[0].cell_contents) #查看闭包的元素
意义与应用
#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
#应用领域:延迟计算(原来是传参,现在是包起来)
from urllib.request import urlopen
def index(url):
def get():
return urlopen(url).read()
return get
baidu=index('http://www.baidu.com')
print(baidu().decode('utf-8'))
递归:
1、必须有明确的结束条件;
2、每递归一次,问题规模比上一次减少;
3、递归效率不高。递归层次过多会导致栈溢出;
递归分为两个阶段:递推,回溯
# salary(5)=salary(4)+300
# salary(4)=salary(3)+300
# salary(3)=salary(2)+300
# salary(2)=salary(1)+300
# salary(1)=100
#
# salary(n)=salary(n-1)+300 n>1
# salary(1) =100 n=1
def salary(n):
if n == 1:
return 100
return salary(n-1)+300
print(salary(5))
python中的递归
python中的递归效率低,需要在进入下一次递归时保留当前的状态;其他语言中的解决方法:尾递归优化,即在函数的最后一步(而非最后一行)调用自己,尾递归优化:http://egon09.blog.51cto.com/9161406/1842475
python又没有尾递归,且对递归层级做了限制。
三元表达式:
name=input('姓名>>: ')
res='girl' if name == 'julia' else 'boy'
print(res)
列表推导式:
#1、示例
egg_list=[]
for i in range(10):
egg_list.append('鸡蛋%s' %i)
egg_list=['鸡蛋%s' %i for i in range(10)]
#2、语法
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
类似于
res=[]
for item1 in iterable1:
if condition1:
for item2 in iterable2:
if condition2
...
for itemN in iterableN:
if conditionN:
res.append(expression)
#3、优点:方便,改变了编程习惯,可称之为声明式编程
生成器表达式:
#1、把列表推导式的[]换成()就是生成器表达式
#2、示例:生一筐鸡蛋变成给你一只老母鸡,用的时候就下蛋,这也是生成器的特性
>>> chicken=('鸡蛋%s' %i for i in range(5))
>>> chicken
<generator object <genexpr> at 0x10143f200>
>>> next(chicken)
'鸡蛋0'
>>> list(chicken) #因chicken可迭代,因而可以转成列表
['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4',]
#3、优点:省内存,一次只产生一个值在内存中
高阶函数:
可以将函数作为参数传递给函数:
def sum_abc(x,y,f):
return f(x)+f(y)
s = sum_abc(-2,3,abs)
print(s) #5
内置函数:
中文查阅:http://www.runoob.com/python/python-built-in-functions.html
| | 内置函数 | | |
| ||||
|
注:
all(iterable) : iterable里面的元素均为True,则结果为True;
any(iterable) :iterable里面的任意元素为True,则结果为True,若iterable为empty,返回False;
装饰器:
装饰器就是闭包函数的一种应用场景,本质是函数
WHY
#开放封闭原则:对修改封闭,对扩展开放
WHAT
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:
1 不修改被装饰对象的源代码
2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
HOW
知识储备:
1、函数即“变量”;
回收机制:没有应用,就立马回收。
主函数执行之前,顺序无影响(类比变量顺序)。
2、高阶函数;
a、把一个函数作为实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能);
b、返回值中包含函数名(不修改函数的调用方式);
bar(): bar内存地址(门牌号);bar()基于内存地址在调用
3、嵌套函数
在一个函数的函数体内,用def声明一个函数,并不是调用。
语法:高阶函数+嵌套函数==>装饰器
被装饰函数的正上方,单独一行
@deco1
@deco2
@deco3
def foo():
pass
foo=deco1(deco2(deco3(foo)))
无参装饰器 | 有参装饰器 |
|
|
补充:wraps
from functools import wraps
def deco(func):
@wraps(func) #加在最内层函数正上方
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
@deco
def index():
'''哈哈哈哈'''
print('from index')
print(index.__doc__)
迭代器:
生成器:
面向过程编程:
Json和pickle序列化:
把变量从内存中变成可存储或可传输的过程就称之为序列化。在Python中称为picking,在其他语言中称之为serialization,marshalling,flattening等等,都是一个意思。序列化之后就可以存储到磁盘上,或通过网络传输到别的机器上。有序列化就有反序列化,把内容从序列化对象重新读取到内存的过程称之为反序列化。
json与pickle的区别
- json只能处理基本数据类型;pickle能处理所有的Python数据类型
- json多用于各个语言之间的网络传输;pickle多用于Python程序对象的持久化或Python程序之间的网络传输
json
- 序列化
import json
d = {"a":1,"b":2}
f = open("test.txt","wb")
s_dict = json.dumps(d) # 步骤1:序列化
f.write(s_dict) # 步骤2:写入磁盘
f.close()
步骤1,步骤2等价于:
json.dump(d,f)
- 反序列化
import json
f = open("test.txt","r")
d = json.loads(f.read()) # 步骤1:反序列化。此时d为dict
f.close()
步骤1等价于:
d = json.load(f) # 此时d为dict
f.close()
pickle
- 序列化
import pickle
d = {"a":1,"b":2}
f = open("test.txt","wb")
s_dict = pickle.dumps(d) # 步骤1: 序列化
f.write(s_dict) # 步骤2:将序列化后的内容存储到磁盘,因为使用pickle序列化之后变成了一个byte,所以写入方式为byte,所以写入的数据看起来像是乱码的
f.close()
步骤1,步骤2等价于:
pickle.dump(d,f)
- 反序列化
import pickle
f = open("test.txt","rb")
d = pickle.loads(f.read()) # 步骤1:反序列化。此时d为dict
f.close()
步骤1等价于
d = pickle.load(f) # d 为dict
f.close()
总结:
json 用于字符串和python数据类型间进行转换
pickle 用于python特有的类型 和 python的数据类型间进行转换
序列化:把字典或者字符串的内存对象 存到硬盘上;
反序列化:就是从硬盘上加载出来
当json dumps多次的时候,loads的时候报错;同时也不可以loads多次;
要想loads多次,就在dumps的时候dumps成不同的文件,laods不同文件。
当pickle dumps多次的时候,loads的时候不报错,但得出的结果是第一次dumps的内容;同时也不可以loads多次;
要想loads多次,就在dumps的时候dumps成不同的文件,laods不同文件。