python函数使用的优点
①将相似的代码抽取到一块避免了代码的重复
②方便修改,便于扩展
③保持代码的一致性
1.函数的格式
#函数的创建方法
def function_name():
print("this is function body")
调用创建的函数方法
#函数的调用
function_name()
ok,现在最简单的一个无参无返回值的函数就已经写好了,但是在实际的运用中这样的参数是基本上用不到的.接下来简单的说一下函数名的命名规则,函数名的命名规则和变量的命名规则是一样一样的,变量的命名规则①创建的名字必须有意义②变量名的组成必须是字母和数字下划线③
函数初识,模仿日志log文件
#打印log日志的方法
def print_log(val):
#首先创建一个txt文件来记录日志内容
file_log = open("log.txt",'a+',encoding="utf8")
file_log.writelines(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))+"the log is "+val)
file_log.close()
print_log("hello world\n")
print_log("测试log1\n")
print_log("测试log2\n")
函数的参数分类
1.必须参数
必须参数就是,类似于下面这种形式的参数.必须参数的实参和形参一定要保持一致,否则就算程序不报错也会路唇不对马嘴.
#必须参数
def parameterMust(name,age,job):
print("Name is %s"%name)
print("Age is %d"%age)
print("Job is %s"%job)
parameterMust("张三",23,"IT")
2.关键字参数
类似于下面的这种参数的形式就是关键字参数.此时实际参数在使用的时候可以加上形参的名字来传递参数,在全部使用关键字参数的时候位置就会显得不那么重要了.
def parameterKeyword(name,age,job):
print("Name is %s" % name)
print("Age is %d" % age)
print("Job is %s" % job)
parameterKeyword(name="张三",job="IT",age=23)
3.默认参数
默认参数就是给某个形式参数直接赋予一个常用的值,假如实际参数不写的话就会使用这个默认的参数,假如实际参数重新写的话会将这个默认参数覆盖掉.
在使用默认参数的情况下
def parameterKeyword(name,age,job,sex = "male"):
print("Name is %s" % name)
print("Age is %d" % age)
print("Job is %s" % job)
print("Sex is %s" % sex)
parameterKeyword("张三",23,"IT")
在不适用默认参数的情况下
def parameterKeyword(name,age,job,sex = "male"):
print("Name is %s" % name)
print("Age is %d" % age)
print("Job is %s" % job)
print("Sex is %s" % sex)
parameterKeyword("张三",23,"IT","female")
4.不定长参数
元组类型的不定长参数
自我感觉最麻烦也是最常用的一个参数形式.
不定长参数就是参数的个数不确定,根据实际的参数个数来决定形参有多少个参数
#不定长参数之 *args
def parameterArgs(*args):
print(type(args))
for i in args:
print(i)
parameterArgs("张三",23,"IT","male")
从运行结果可以看出来不定长参数的运行原理就是将实参组成了一个元组传递给了args
字典类型的不定长参数
假如想要在一个对象中扩充属性,可以使用**kwargs的方式来传递字典收类型的参数
#不定长参数之**kwargs
def parameterKwargs(**kwargs):
for i in kwargs.keys():
print(kwargs)
print(i,kwargs[i])
parameterKwargs(name="张三",age=23,job="IT",sex="male")
关于参数的位置排列组合
组合1,关键参数,不定长参数,这种排序的方式时错误的,在执行的时候会报错,因为不定长参数只能放到最后
def parameterCombinationOne(name,age,*args):
print("name is %s" % name)
print("age is %d" % age)
print(args)
for i in args:
print(i)
parameterCombinationOne("张三",23,"IT","male")
running result
从运行结果中可以看出在在形参列表中前面是必须参数,后面是不定长参数的时候,会首先遵循必须参数的规则,然后将没有名字的参数放在不定长参数中
参数组合2
将不定长参数放在前面,必须参数放在后的时候,所有参数会遵循不定长参数的规则,会一股脑的将参数作为元组放进去传给不定长参数args,而后面的必须参数则会取不到值所以会报错,由此可以发现不定长参数一般都是放到必须参数和默认参数的后面的.
def parameterCombinationOne(*args,name,age):
print(args)
for i in args:
print(i)
#print("name is %s" % name)
#print("age is %d" % age)
parameterCombinationOne("张三",23,"IT","male")
参数异常类型:missing 2 required keyword-only arguments:
3.args和keyword排序
def parameterCombinationOne(name,age,*args):
print(args)
for i in args:
print(i)
print("name is %s" % name)
print("age is %d" % age)
parameterCombinationOne(name="张三",age=23,"IT","male")
这个代码呢实际上是报错的,位置在实参列表中的"IT"和"male"
来看下报错类型
此时python的参数处理机制似乎将关键字参数作为了kwargs来处理
4.args和kwargs组合
def parameterCombinationOne(*args,**kwargs):
print(args)
for i in args:
print(i)
print(kwargs)
for i in kwargs.keys():
print(i,kwargs[i])
#print("name is %s" % name)
#print("age is %d" % age)
parameterCombinationOne("IT","male",name="张三",age=23)
ok,此时看到咯在不定长参数的时候args在前kwargs在后是没有问题的.
5.kwargs在前args在后
def parameterCombinationOne(**kwargs,*args):
print(args)
for i in args:
print(i)
print(kwargs)
for i in kwargs.keys():
print(i,kwargs[i])
#print("name is %s" % name)
#print("age is %d" % age)
parameterCombinationOne(name="张三",age=23,"IT","male")
ok,the running result tell us ,形参列表中参数类型是有位置优先级滴,kwargs优先级最后
5.此时参数顺序是酱紫滴 (job,*args,**kwargs)
def parameterCombinationOne(job,*args,**kwargs):
print("job is %s"%job)
print(args)
for i in args:
print(i)
print(kwargs)
for i in kwargs.keys():
print(i,kwargs[i])
#print("name is %s" % name)
#print("age is %d" % age)
parameterCombinationOne("IT","male",name="张三",age=23)
此时发现这种形式是OK的no problem.
.关键字参数和kwargs组合
def parameterCombinationOne(job,sex,**kwargs):
print("job is %s"%job)
print("sex is %s" %sex)
print(kwargs)
for i in kwargs.keys():
print(i,kwargs[i])
#print("name is %s" % name)
#print("age is %d" % age)
parameterCombinationOne(job="IT",sex="male",name="张三",age=23)
.关键字参数和kwargs
def parameterCombinationOne(job,sex,**kwargs):
print("job is %s"%job)
print("sex is %s" %sex)
print(kwargs)
for i in kwargs.keys():
print(i,kwargs[i])
#print("name is %s" % name)
#print("age is %d" % age)
parameterCombinationOne("IT","male",name="张三",age=23)
ok,现在可以知道了,必须参数在前面的时候后面既可以是args 又可以是kwargs
但是当前面是关键字参数的时候后面不能有args
当使用默认参数的时候
错误
错误
正确
总结发现 参数的优先级基本上就是 必须参数->关键字参数->默认参数->args->kwargs
但是注意使用关键字参数的时候后面不能跟args不定长参数.
三.函数的作用域
首先函数中呢都有作用域,作用域的优先级和范围,范围越大优先级越低,范围越小优先级越高,变量在python,或者其他语言中都会遵循这个法则.
下面来看看python对于变量优先级的分类.
python的作用域呢分为四个等级简写呢分别是L,E,G,B
L:local 局部变量
E:enclosing 靠叫什么名字忘记了,无所谓只要认真的看代码理解了,就可以
G:global 全局变量
B:build_in 最外层作用域
x = int(3.9)#bulit_in
a = 100 #global全局作用域
def outer():
o_outer = 1 #enclosing 作用域
def inner():
i_inner = 2 #local 局部变量.
print(i_inner)
inner()
outer()
局部变量和全局变量的关系
局部变量不能修改全局变量,在未使用关键字修饰的时候
例如在上面的代码中print count出错原因应该是酱紫滴,由于python是一种解释性的语言,所以在执行到outer这个函数的时候,python会先在outer函数的内部寻找名字是count的变量,走到下面之后发现已经出现了局部的 count = 5,但是检查后发现语法错误所以在print(count)的时候直接飘红.
当然如果非要在函数中修改局部变量也是可以的.不过需要是使用关键字 global来修饰
count = 10 #声明一个全局变量
def outer():
global count #使用global来修饰这个变量就是一个全局变量
print(count)
count = 5
outer()
同样在嵌套函数中也存在类似于这样的问题
例如
def outer():
count = 10
def inner():
nonlocal count
count = 20
print(count)
inner()
print(count)
outer()
关键字nonlocal 和上面的关键字global的作用相同,均是在范围小的作用域中修改范围大的变量.
四.函数的返回值return
return的作用:
①结束函数
②返回对象
用一个例子解释
def add(*args):
sum = 0
for i in args:
sum += i
return sum
print(add(2,6,8))
在函数没有return的时候返回值默认是None
def add(*args):
sum = 0
for i in args:
sum += i
#return sum
print(add(2,6,8))
高阶函数
#高阶函数
def square(val):
return val * val
def add_square(a,b,func):
return func(a) + func(b)
print(add_square(2,3,square))
其实函数名跟变量名是一样的,函数名也是可以作为参数传递的.函数名可以进行赋值,
如果直接打印函数名字的话会直接将函数名的内存地址打印出来.
递归函数
递归函数的特点,①无穷无尽的调用自己②结束条件
递归函数的示例一
阶乘:5! = 5*4*3*2*1
#阶乘
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
print(factorial(5))
递归函数典型示例二
斐波那契数列 0,1,1,2,3,5,8,13,..............
#斐波那契数列
def num_list(n):
if n == 1:
return 0
elif n == 2:
return 1
return num_list(n-1)+num_list(n-2)
print(num_list(7))
虽然斐波那契数列用着递归好用但是,在不用递归的情况下尽量不用递归,因为递归的效率非常慢,而且可以使用递归解决的问题都一定可以用循环来解决此类问题
装饰器
靠,猴哥说装饰器好理解,但是想不到怎么用.终于体会到了.
什么是装饰器呢,就是给原有函数添加功能的函数就是装饰器函数.例如原有一堆函数,如果想要计算这些函数的执行时间应该怎么计算呢,这就用到了装饰器函数.
首先来说说装饰器必须要回的三个条件好了.
①变量的作用域
②函数名是可以作为参数传递的
③闭包
首先来看一看简单的装饰器
计算函数foo的执行时间,
#简单装饰器
def show_time(func):
def inner():
start = time.time()
func()
end = time.time()
print(end - start)
return inner
def foo():
sum = 0
for i in range(1000000):
sum += i
print(sum)
foo = show_time(foo)
foo()
python提供的@符号取代 'foo = show_time(foo)'
例子
def show_time(func):
def inner():
start = time.time()
func()
end = time.time()
print(end - start)
return inner
@show_time #这句话等效于foo = show_time(foo)
def foo():
sum = 0
for i in range(1000000):
sum += i
print(sum)
foo()
ok接下来看带参数的装饰器
需求,对于某一些需要执行计算时间的,一些函数不需要装饰器修饰来计算的可以通过装饰器参数来解决
def logger(flag):
def show_time(func):
def inner():
if flag == "True":
start = time.time()
func()
end = time.time()
print(end - start)
return inner
return show_time
@logger(flag="True") #这句话等效于foo = show_time(foo)
def foo():
sum = 0
for i in range(1000000):
sum += i
print(sum)
foo()
某些不需要执行修饰功能的函数就需要在参数中写falsse
例如
def logger(flag):
def show_time(func):
def inner():
if flag == "True":
start = time.time()
func()
end = time.time()
print(end - start)
return inner
return show_time
@logger(flag="False") #这句话等效于foo = show_time(foo)
def foo():
sum = 0
for i in range(1000000):
sum += i
print(sum)
foo()
装饰器练习题
模仿京东的购物登录功能.
例如 下面的操作
Start :
1.首页
2.金融
3.图书
请输入你的选项:1
请输入用户名:jingdong
请输入密码:123456
welcome to home page
-----------------
Start:
1.首页
2.金融
3.图书
请输入你的选项:2
welcome to finance
----------------------
题目要求:
1.选择相应的项,回车后判断是否登录,假如已经登录,再次选择其他的选项的时候就不再进行登录验证
2.登录方式分为两种,京东账号和微信账号.
3.首页登录方式选择京东账号,金融模块选择微信登录,图书模块不用管
4.验证的用户名和密码分别从记事本中取出
例题的实现:
#模拟京东的用户登录
#开始装饰器
#1.登陆之前判断是京东登录还是微信登录
#2.一个模块登录之后再登录其他模块 就不用再次登录
Jfile = open("user.txt",'r')
Jfr = Jfile.readlines()
Juser = Jfr[0].strip('\n')
Jpassword = Jfr[1].strip('\n')
Wfile = open("weixin.txt",'r')
Wfr = Wfile.readlines()
Wuser = Wfr[0].strip('\n')
Wpassword = Wfr[1].strip('\n')
flag = False
def login(auth_type):
def model(func):
def inner():
global flag
global Juser
global Jpassword
global Wuser
global Wpassword
if not flag :#如果没有登录就进行登录操作
username = input("请输入用户名")
password = input("请输入密码:")
if auth_type == "jingdong":
if Juser== username and Jpassword == password:
func()
flag = True
if auth_type == "weixin":
if Wuser == username and Wpassword == password:
func()
flag = True
else:
func()
return inner
return model
@login(auth_type="jingdong")
def home():
print("welcome to home ")
@login(auth_type="weixin")
def finance():
print("welcome to finance")
def book():
print("welcome to book")
while True :
print("Start")
print("1.home:")
print("2.finance:")
print("3.book;")
chose = input("请输入你的选项:")
if chose == '1':
home()
elif chose == '2':
finance()
elif chose == '3':
book()
else:
break
print("-------------------------------")
运行结果:
这道练习题基本上对于初学者两个小时要搞定的.否则装饰器还是没有学会.
生成器
列表生成的方式呢有两种方法,
第一种呢就是利用列表生成式来生成列表,详情代码如下
#列表表达式
li_creat = [x for x in range(10)]
print(li_creat)
ok此时一个简单的列表表达式创建的列表就已经完成了,列表表达式语法领悟
在表达式中其实就是x每次循环后面的序列后将值存放到for前面的变量之中.同时呢变量x是可以进行运算的(其中也包括作为函数的参数).但是有一点一定要符合的就是.for前面的变量必须和for后面的变量名要一直.
例如:x放到函数的参数之中进行的运算
def f(n):
return n**3
li_creat = [f(x) for x in range(10)]
print(li_creat)
第二种方式的生成器
第二种方式其实与第一种方式差不多的.只是括号的差别但是两个输出之后会发现还是有差别的.第一种方式返回的就是直接计算好以后的一个列表,第二种方式返回的就是一个可迭代对象.
这两种方式的差别就像鱼和渔夫的关系类似,
第一种方式:[x*2 for x in range(10)]
第二种方式:(x*2 for x in range(10))
接下来用代码来实验两种方式的区别
ret1 = [x for x in range(10)]
ret2 = (x for x in range(10))
print("ret1的输出结果:",ret1)#运行结果 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print("ret2的输出结果:",ret2)#运行结果 <generator object <genexpr> at 0x00000000010CC1A8>
#ok从运行结果可以看出来第二种方式其实就是返回返回的一个可以迭代的对象,你想要取得哪一个值就用迭代对象的next方法去对象中重新取值
print("ret2使用__next__取出来的结果:",end=" ")
for i in range(10):
print(ret2.__next__(),end=" ") #在py3中使用的是__next__()方法,但是在py2中最常用的就是next内置方法
print()
ret2 = (x for x in range(10))
print("ret2使用next()内置方法取出来的结果:",end=" ")
for i in range(10):
print(next(ret2),end=" ") #从运行的结果可以看出来每次运行一次next就会自动往下走一个数,一直到结尾.并且在生成器中并没有像列表那么灵活,是不可以进行切片,或者倒着取值的.
方式二方式本质讲解:其实生成器就是一个可迭代对象.那么什么是可迭代对象呢就时对象内部有__iter__方法的都是可迭代对象.
说道生成器就不得不说一下for工作的原理了.其实for的工作原理也是利用next()方法生成器对象中取值的.
例如 for i in s :
print(i) 首先进行next(s)运算,每次运算就将运算的值传递给i,直到next()方法取值到最后一个数,报异常后进行异常处理
其实(x for x in range(10)这种就是创建生成器的第一种方式.
那么接下来看创建生成器的第二种方式:
使用yield关键字来创建生成器,同时呢也可以这样说,有yield关键字的就是生成器,没有yield的就是函数.
def foo():
print("ok")
yield 1
g = foo()
print(next(g))
生成器中的send方法
其实send方法和next方法作用差不多只不过send可以传递参数给yield前面的变量,并且第一次传递只能是空值,除非前面有next方法执行过,否则就会报错.
#send方法
def bar():
print("ok1")
count = yield 1
print(count)
yield 2
b = bar()
b.send(None)
b.send("ffff")
上面的代码值行过程是这个样子的,首先执行,b = bar()->b.send("None")->执行生成器bar()->print("ok")->yield 1结束状态为1的生成器->b.send("ffff")->count = ffff -> print(count) -> yield 2
迭代器
总结来说,生成器都是迭代器,迭代器不一定是生成器.
例如 L 是一个可迭代的序列 d = iter(L) 等价于 L.__iter__
此时呢,这个d就是一个迭代器对象.
so:迭代器要满足两个条件,①有iter方法(用来生成一个迭代器对象)②有next方法(从可迭代对象中取出值)
for循环内部的三件事
①调用iter方法,返回一个迭代器对象
②不断调用迭代器的next方法
③处理stopIteration异常
注意事项:iterator是迭代器 iterable是可迭代对象
多变量赋值
什么叫多变量赋值呢,顾名思义就是对多个变量进行的赋值
例如,在元组中进行的多变量赋值
tuple_val = (1,2,3)
a,b,c = tuple_val
print("a = ",a,"b = ",b,"c = ",c)
通过上面简单的验证可以发现,元组是可以进行这种方式进行赋值的不仅仅是元组可以这个样子赋值序列都可以进行这样赋值,还有一点要注意的就是变量的个数和序列中的个数要一个一个对应起来.否则就会出错.
不仅仅对于序列可以进行这个样子的方式赋值其实这样也是可以的
a,b = 1,2
print(a,b)
此时这种赋值方式就是a = 1 b = 2
多变量赋值的经典应用,就是斐波那契数列的应用
#斐波那契数列
def fib(n):
i,a,b = 0,0,1
while i < n:
print(b,end=" ")
a,b = b,a+b
i += 1
#此时想要看到第7项的斐波那契数列
fib(7)