函数与闭包
大约 5 分钟
函数
# 默认参数必须定义在非默认参数之后
def login(age, username='admin', password='123456'):
if username == 'admin' and password == '123456':
# 函数返回多个值
return True, '登录成功'
else:
# 函数返回多个值
return False, '登录失败'
# 命名参数
result, msg = login(password='123456', age = 18)
print(result, msg)
# 变长参数
def calc(*args):
print("type(args) ==>", type(args))
sum = 0
for i in args:
sum += i
print(sum)
# 传递可变参数
calc(1, 2, 3, 4, 5)
# 不能直接传递元组参数
a = (1, 2, 3, 4, 5)
# 这样传递会报错
# calc(a)
# 需要传递它的地址
calc(*a)
# 列表也是一样
a = [1, 2, 3, 4, 5]
calc(*a)
# 这里默认参数并没有在非默认参数之后,仍然可以执行
def myfunc1(param0, param1='hello', *param2, param3='python'):
print("param0 ==>", param0)
print("param1 ==>", param1)
print("param2 ==>", param2)
print("param3 ==>", param3)
myfunc1('hello', 'world', 'python', 'java', 'scala', 'go')
# 支持任意数量的命名参数
def myfunc2(**params):
print("params ==>", params)
for key in params:
print("key ==>", key)
print()
for key, value in params.items():
print("key ==>", key, ", value ==>", value)
myfunc2(name='zhangsan', age=18, sex='male')
# 也可以这样调用
a = {'name': 'zhangsan', 'age': 18, 'sex': 'male'}
myfunc2(**a)
# 变量作用域
def myfunc3():
sum = 0
# 局部变量
name = 'zhangsan'
# 块级作用域
for i in range(0, 9):
# 不需要事先声明name变量,可以直接使用
name = 'lixingyun'
sum += i
print("name ==>", name)
print("sum ==>", sum)
myfunc3()
# 全局变量
global_var = 1
def myfunc4():
# 如果没有赋值,打印出来的值是1
global_var = 2
print("global_var ==>", global_var)
def myfunc5():
# 如果没有赋值,打印出来的值不会是3
global_var = 3
print("global_var ==>", global_var)
myfunc5()
# 作用域链
myfunc4()
# 依旧是1
print("global_var ==>", global_var)
# 将局部变量转换为全局变量
def myfunc6():
global local_var
local_var = 10
myfunc6()
print("local_var ==>", local_var)
内置函数
# 以`__`开头和结尾的函数,称为内置函数或魔术方法
# 但这些内置函数并不是object自带的,而是Python解释器为了增强object的功能而提供的
class Company:
def __init__(self, employee_list):
self.employee = employee_list
# __getitem__可以让任何对象都变成鸭子类型:只要在任何class中加入这个方法,那么这个class就有了可迭代的行为
# 对于其他的魔术方法也是一样
# 因此,python虽然没有多态的概念,但通过魔术方法 + 鸭子类型的组合,却具备了比多态更灵活的特性
def __getitem__(self, item):
return self.employee[item]
def __iter__(self):
return iter(self.employee)
# def __len__(self):
# return len(self.employee)
# 格式化对象输出的内容
def __str__(self):
return f'{self.__class__.__name__} (employee={self.employee})'
# 开发模式下调用,在python shell和python notebook中,会调用它而不是__str__
# __repr__会隐式地调用__str__
def __repr__(self):
return ",".join(self.employee)
company1 = Company(['lixingyun', 'wanglin', 'xiaoyan'])
for emp in company1.employee:
print(emp)
# 因为定义了__getitem__和/或__iter__,所以可以像列表和/或迭代器一样使用company
# 魔术方法定义了之后不用显式调用,Python解释器会自动调用它们
for emp in company1:
print(emp)
# 如果没于定义__len__,则不能使用len(),否则会报错
# print(len(company1))
# 如果没有定义__getitem__,则不能使用切片
# 这些内置函数直接影响了Python的运行机制
company2 = company1[:2]
# 但不定义__len__却能得到company2的长度,因为company1[:2]调用的是list的方法,而list里面是实现了__len__的
print(len(company2))
# 定义了__str__之后,就不会再打印对象的内存地址:<__main__.Company object at 0x7fb120096eb0>
print(company1)
闭包
# 全局变量a
a = 10
def outer():
# 环境变量a
a = 7
b = 5
c = 1
def inner(x):
return a * x ** 2 + b * x + c
# 返回一个函数
return inner
# 将闭包函数赋值给变量
func = outer()
# 调用变量所对应的函数
# 在这里调用的时候,全局变量a的值是不会对函数造成影响的,inner()读取的始终是环境变量a的值,也就是7
# 这就是闭包,闭包的行为由它的函数定义和外部环境变量共同决定
print(func(5))
# 打印函数地址
print(f'func = {func}')
print(f'func.__closure__ = {func.__closure__}')
print(f'func.__closure__[0] = {func.__closure__[0]}')
print(func.__closure__[0].cell_contents, func.__closure__[1].cell_contents, func.__closure__[2].cell_contents)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
# 可以通过下面的代码清楚地看到环境变量的变化
def func1():
a = 1
def func2():
# 如果这里重新给a赋值,那么a会被Python当作局部变量,而不是环境变量,因此func2也就不再是闭包
a = 2
print("func2() ==>", a)
print("func1() 1 ==>", a)
func2()
print("func1() 2 ==>", a)
return func2
func2 = func1()
# func2不是闭包,因为在func2()中定义了局部变量a
print(f'func2.__closure__ = {func2.__closure__}')
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
# 使用闭包特性来实现两个独立的累加器
def accumulator(count):
def sum():
acc = 0
# 函数的行为
for i in range(0, count):
acc = acc + i
return acc
# 返回闭包
return sum
sum1 = accumulator(10)
# 第一个累加器
print(sum1())
# 第二个累加器,每个累加器中的环境变量都是独立的,都不受外部全局变量和其他环境变量的影响
sum2 = accumulator(5)
print(sum2())
# 查看闭包的环境变量信息
print(sum1.__closure__)
print(sum1.__closure__[0])
print(sum1.__closure__[0].cell_contents)
print(sum2.__closure__)
print(sum2.__closure__[0])
print(sum2.__closure__[0].cell_contents)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
# 实现另一种累加功能
# 使用普通函数实现累加
init1 = 0
def accumulator1(step):
global init1
new_step = init1 + step
init1 = new_step
return init1
print(accumulator1(1))
print(accumulator1(3))
print(accumulator1(5))
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
# 使用闭包实现累加
def accumulator(init2):
def walk(step):
# 强制让init2成为闭包变量
nonlocal init2
new_step = init2 + step
init2 = new_step
return new_step
return walk
walk = accumulator(0)
print(walk(1))
print(walk.__closure__[0].cell_contents)
print(walk(3))
print(walk.__closure__[0].cell_contents)
print(walk(5))
print(walk.__closure__[0].cell_contents)
感谢支持
更多内容,请移步《超级个体》。