python基础-函数
接着上周的文档,继续分享python基础相关内容。
1.函数定义
函数:将一段有规律的、可重复使用的代码定义成函数,从而达到一次编写、多次调用的目的
a = [1,5,10,22,33,56,78]
sum = 0
for i in a:
sum = sum + i
print(sum)
def sum_list(a):
sum = 0
for i in a:
sum = sum + i
return sum
a = [1,5,10,22,33,56,78]
print(sum_list(a))
2.关键字参数和默认值
2.1关键字参数
- 位置参数:按顺序为每个参数指定参数值
- 关键字参数(命名参数):按参数名为参数指定参数值
def info(name, age, height):
print('name:', name)
print('age:', age)
print('height:', height)
info('w', 25, 175) # 位置参数
结果:
name: w
age: 25
height: 175
info(age=25, name='w', height=175) # 关键字参数(命名参数),优势:1.不需要按顺序;2.可读性高
结果:
name: w
age: 25
height: 175
info('w', height=175, age=25) # 混合使用,位置参数在前,关键字参数在后
结果:
name: w
age: 25
height: 1752
2.2参数默认值
- 关键字参数(命名参数):按参数名为参数指定参数值
指定默认参数在后
def info(age, name='w'):
print('name:', name)
print('age:', age)
info(25)
结果:
name: w
age: 25
info(18, 'k')
结果:
name: k
age: 18
def info(name='w', age=25):
print('name:', name)
print('age:', age)
info()
结果:
name: w
age: 25
info(age=18) # 省略前面的参数,则后面的参数需要关键字指定
结果:
name: w
age: 18
3.收集与分配参数(data, *args, **kwargs)
python自定义函数中有两种不定长参数:
第一种是*XXX,在传入额外的参数时可以不用指明参数名,直接传入参数值即可。
第二种是** XXX,这种类型返回的是字典,传入时需要指定参数名。
加了一个星号 * 不定长参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
加了两个星号 ** 的参数会以字典的形式导入,存放已命名的变量参数。
实例一
#coding=utf-8
#第一个是元组形式,第二个是字典形式
def sun(*a, **b):
print(a)
print(b)
sun(1, 55258, x=25412, y=5123512)
结果:
(1, 55258)
{‘x’:25412, ‘y’:5123512}
实例二
在a, *b, **c同时出现
#coding=utf-8
#使用不定长参数传固定值,注意,b、c可省略,a不可省略
def fuzhi(a, *b, **c):
print(a)
print(b)
print(c)
fuzhi(853521, 65134, 635263, 45563, 365, x=99, y=999)
结果:
这个实例中,不定长参数b、c可省略,而普通参数a不可省略,假如我们省略a
#coding=utf-8
#使用不定长参数传固定值,注意,b、c可省略,a不可省略
def fuzhi(a, *b, **c):
print(a)
print(b)
print(c)
fuzhi()
结果:
实例三
普通参数不能写在不定长参数写在后面
#coding=utf-8
def JayChou(a, *b, c):
print(a)
print(b)
print(c)
JayChou(1, 555, 5768, 55451)
运行结果
会出现如下报错,因此,位置不能随意更改
!<img src='https://s2.51cto.com/images/blog/202410/20004623_6713e25f5854729436.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=' align='left'/>
4.递归函数的实现
在python中我们使用函数经常是去调用别的函数,但是递归函数不一样,它是自己去调用自己。
递归函数的条件有两个:
1.必须给递归函数一个出口,否则会无限调用,耗费内存
2.对自己的递归条件作大胆假设
下面我们计算数学上经常计算的阶乘,阶加等算法
def scale(s):
"""
使用递归函数来计算阶乘,阶加等
:param s:
:return:
"""
if s == 1:
return s
else:
return s + scale(s - 1)
print(scale(1))
print(scale(10))
print(scale(100))
scale(3)
3+scale(2)
3+2+scale(1)
3+2+1
结果:
递归函数的好处:
1.代码比较少
2.代码界面整洁
递归函数的坏处:
1.逻辑有点难理解
2.不写出口时耗费内存
5.函数装饰器的使用(初级班不做要求,感兴趣的自己看一下)
饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。
装饰器的作用:可以增强函数的功能,如插入日志,增加权限校验,事务一致性,缓存等功能,这就是装饰器,使漂亮的姑娘(函数)变得更加漂亮。
5.1初识装饰器
5.2将函数作为参数传给另一个函数
def hi():
return "hi yasoob!"
def doSomethingBeforeHi(func):
print("I am doing some boring work before executing hi")
print(func())
print("I am doing some boring work after executing hi")
doSomethingBeforeHi(hi)
#outputs:I am doing some boring work before executing hi
# hi yasoob!
# I am doing some boring work after executing hi
5.3你的第一个装饰器
在上一个例子里,其实我们已经创建了一个装饰器!现在我们修改下上一个装饰器,并编写一个稍微更有用点的程序:
def a_function_requiring_decoration():
print("I am the function")
a_function_requiring_decoration()
#outputs: "I am the function "
def a_new_decorator(a_func):
def wrapTheFunction():
print("before a_func()")
a_func()
print("after a_func()")
return wrapTheFunction
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
a_function_requiring_decoration()
#outputs:before a_func()
# I am the function
# after a_func()
这正是 python 中装饰器做的事情!它们封装一个函数,并且用这样或者那样的方式来修改它的行为。现在你也许疑惑,我们在代码里并没有使用 @ 符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 @ 来运行之前的代码:
@a_new_decorator
def a_function_requiring_decoration():
print("I am the function")
a_function_requiring_decoration()
#outputs: before a_func()
# I am the function
# after a_func()
#the @a_new_decorator is just a short way of saying:
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
希望你现在对 Python 装饰器的工作原理有一个基本的理解。如果我们运行如下代码会存在一个问题:
print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction
这并不是我们想要的!Ouput输出应该是"a_function_requiring_decoration"。这里的函数被warpTheFunction替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps。我们修改上一个例子来使用functools.wraps:
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("before a_func()")
a_func()
print("after a_func()")
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
print("I am the function")
print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration
5.4使用场景
应用场景:
1.可以在外层函数加上时间计算函数,计算函数运行时间;
2.计算函数运行次数;
3.可以用在框架的路由传参上;
4.插入日志,作为函数的运行日志;
5.事务处理,可以让函数实现事务的一致性,让函数要么一起运行成功,要么一起运行失败;
6.缓存,实现缓存处理;
7.权限的校验,在函数外层套上权限校验的代码,实现权限校验;
使用场景实例-插入日志
现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)
# Output: addition_func was called
5.5带参数的装饰器
我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件。
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
@logit(logfile='func2.log')
def myfunc2():
pass
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串
5.6装饰器类
类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建logit。
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func): # 定义__call__方法,直接实现装饰功能。用@logit修装饰函数时就会执行__call__方法。
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile并写入
with open(self.logfile, 'a') as opened_file:
# 现在将日志打到指定的文件
opened_file.write(log_string + '\n')
# 现在,发送一个通知
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
# logit只打日志,不做别的
pass
这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:
@logit()
def myfunc1():
pass
现在,我们给 logit 创建子类,来添加 email 的功能(虽然 email 这个话题不会在这里展开)。
class email_logit(logit):
'''
一个logit的实现版本,可以在函数调用时发送email给管理员
'''
def __init__(self, email='admin@myproject.com', *args, **kwargs):
self.email = email
super(email_logit, self).__init__(*args, **kwargs)
def notify(self):
# 发送一封email到self.email
# 这里就不做实现了
pass
从现在起,@email_logit 将会和 @logit 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。
类当作装饰器的意义
如果装饰器想实现的功能比较少,那我们用普通的装饰器即可。
但如果我们想实现多种功能,会在一个装饰器里写很多代码,显然用普通的装饰器实现起来很费劲,但如果用类装饰器的话,我们可以把各种功能封装在一个个实例方法中,然后在__call__
方法中调用不同的实例方法。