Python 入门第三天
hello ,欢迎看到黄昏的 python 函数部分,这里主要从介绍函数从入门到超神,如果你在学习的过程中,有问题欢迎咨询我哦,我们一起讨论。
此次文章主要介绍函数的入门级和函数的高级,从定义到装饰器。
Python 函数入门
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率
函数有具体的组成部分:函数名,函数体,参数,返回值
从分类上看,函数可分为内置函数 和 自定义函数
为了实现不同的编程需求,还可为函数加上各种规则及作用域的限制,以完成整个功能
1.函数定义
- 定义函数使用关键字def,后接函数名,再后接放在()中的可选参数列表,最后是冒号。格式如下
def 函数名(参数1,参数2,...,参数N):
- 例如:
def max(a, b): #定义函数名 max if a >= b: return a # 返回a else: return b # 返回b return 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
2.函数调用
- 函数调用
调用时,直接使用函数名称即可 hello("I love Python!") # 调用函数,结果输出:I love Python!
3.函数的命名规则
- 函数名的命名有其对应的规则
必须以字母或下划线开头
可以包含任意字母、数字或下划线的组合
不能使用任何特殊字符或标点符号
区分大小写
不能是保留字
不能以数字开头
4.函数的参数
形参是从函数的角度来说的,在定义函数时写到括号中的参数就是形参。所谓形参就是形式上的参数
实参是从调用的角度来说的,在调用函数时写到括号里传给函数的参数就是实参,实参可以是常量、变量、表达式、函数。
实参的个数与类型应与形参一一对应,不能多也不能少
- 形参与实参的区别:
形参是虚拟的,不占用内存空间。形参变量只有在被调用时才分配内存单元
实参是一个变量,会占用内存空间,数据传送单向,由实参传给形参,不能由形参传给实参
- 函数的参数有几种:
必备参数:以正确的顺序传入函数。调用时的数量必须和声明时的一样
def get_info(name, age): print("My name is %s, I'm %d years old." %(name, age)) get_info('tom',25) get_info('jerry',16)
关键字参数:关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值
def get_info(name, age): print("My name is %s, I'm %d years old." %(name, age)) # get_info(16, 'jerry') # TypeError: %d format: a number is required, not str get_info(age=16, name='jerry') # My name is jerry, I'm 16 years old.
默认参数:调用函数时,默认参数的值如果没有传入,则被认为是默认值
def get_info(name, age, sex='male'): print('Name: %s' %name) print('age: %d' %age) print('Sex: %s' %sex) get_info('tom',23) get_info('jerry',30,'female
不定长参数:你可能需要一个函数能处理比当初声明时更多的参数
# def add(x, y): # return x + y def add(*tuples): sum=0 for v in tuples: sum += v return sum print(add(1,4,6,9)) # 20 print(add(1,4,6,9,5))
- 参数的位置优先级
关键字参数
默认参数
args不定长参数
kwargs不定长参数
5. 函数的作用域
- Python中的作用域分4种情况:
L:local,局部作用域,即函数中定义的变量;
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
G:globa,全局变量,就是模块级别定义的变量;
B:built-in,系统固定模块里面的变量。
- 搜索变量的优先级顺序依次是:
作用域局部 > 外层作用域 > 当前模块中的全局 > python内置作用域,也就是LEGB。
x = int(2.9) # int built-in g_count = 0 # global def outer(): o_count = 1 # enclosing def inner(): i_count = 2 # local print(o_count) # print(i_count) # 找不到 inner() outer() # print(o_count) # 找不到 当然,local和enclosing是相对的,enclosing变量相对上层来说也是local
- 作用域的产生
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码
if 2 > 1: x = 1 print(x) # 1
这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用
def test(): x = 2 print(x) # NameError: name 'x2' is not defined
def、class、lambda是可以引入新作用域的
- ***变量的修***改
x = 6 def f2(): print(x) x = 5 f2() # 错误的原因在于print(x)时,解释器会在局部作用域找, # 会找到x = 5(函数已经加载到内存),但 x 使用在声明前了,所以报错: # local variable 'x' referenced before assignment. # 如何证明找到了x=5呢?简单:注释掉 x=5,x=6 # 报错为:name 'x' is not defined # 同理 x = 6 def f2(): x+=1 # local variable 'x' referenced before assignment. f2()
- global关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下\
count = 10 def outer(): global count print(count) count = 100 print(count) outer()
- nonlocal关键字
global 关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了
def outer(): count = 10 def inner(): nonlocal count count = 20 print(count) inner() print(count) outer()
- 总结
变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域; 只有模块、类、及函数才能引入新作用域; 对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量; 内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。
Python 高级函数
1.嵌套函数
- 嵌套
def bar(): print('in the bar') def foo(func): print('in the foo') def inner(): return func() return inner foo(bar) # foo(bar)()
2.装饰器之不带参数的func
- 被装饰的函数
def decorative(func): def wrapper(): # 定义一个包装器 print("Please say something: ") func() # 调用func,这个func是我们自己定义的 print("No zuo no die...") return wrapper @decorative # 使用@符号调用装饰器 def show(): # 定义func,名字取什么都无所谓,它只是用来传给装饰器中的func参数 print("I'm from Mars.") show()
3. 装饰器之带参数的func
- 被装饰的函数
def decorative(func): def wrapper(x): print("Please say something...>") func(x) print("no zuo no die...") return wrapper @decorative def show(x): print(x) show("hello,mars.")
4.有参数的装饰器
- 一个参数的装饰器
def foo(func): def inner(arg): # 验证 return func(arg) return inner @foo def bar(arg): print('bar')
- 两个参数的装饰器
def foo(func): def inner(arg1,arg2): # 验证 return func(arg1,arg2) return inner @foo def bar(arg1,arg2): print('bar')
- 三个参数的装饰器
def foo(func): def inner(arg1,arg2,arg3): # 验证 return func(arg1,arg2,arg3) return inner @foo def bar(arg1,arg2,arg3): print('bar')
- 不固定参数的装饰器
def foo(func): def inner(*args,**kwargs): # 验证 return func(*args,**kwargs) return inner @foo def bar(arg1,arg2,arg3): print('bar')
- 一个函数可以被多个装饰器装饰
def foo(func): def inner(*args,**kwargs): # 验证 return func(*args,**kwargs) return inner def foo1(func): def inner(*args,**kwargs): # 验证 return func(*args,**kwargs) return inner @foo @foo1 def bar(arg1,arg2,arg3): print('bar')
5.装饰器案例
- 为函数添加致谢时间的装饰器函数
import time def timmer(func): def wrapper(*args,**kwargs): start_time = time.time() a = func() stop_time = time.time() print('The func run time is %s'% (stop_time-start_time)) return a return wrapper @timmer def foo(): time.sleep(3) print('in the foo') print(foo())
- 页面验证装饰器
假定有三个页面,现在要实现其中2个页面验证登录之后才能访问,另一个页面不用验证即可访问 首先定义三个页面函数: def index(): print('Welcome to index page') return 'from index page' def home(): print('Welcome to home page') return 'from home page' def bbs(): print('Welcome to bbs page') return 'from bbs page' 然后定义装饰器函数: import getpass user = 'sean' passwd = 'abc123' def auth(auth_type='local'): def out_wrapper(func): def wrapper(*args,**kwargs): if auth_type == 'local': username = input('Username: ').strip() password = getpass.getpass("Password: ").strip() if username == user and password == passwd: print('authentication passed') func(*args,**kwargs) elif auth_type == 'ldap': print('This is ldap authentication') func(*args,**kwargs) return wrapper return out_wrapper 接下来将装饰器分别应用于home函数与bbs函数: def index(): print('Welcome to index page') return 'from index page' @auth('local') def home(): print('Welcome to home page') return 'from home page' @auth('ldap') def bbs(): print('Welcome to bbs page') return 'from bbs page' # 调用函数 index() home() bbs()