02

函数

概念

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数的返回值 

return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return语句返回None。

# return a,b,c等价于 return (a,b,c)
def test():
    a,b = 10,20
    return a,b
print(test())

不定长参数传递

加了星号(*)的变量args会存放所有未命名的变量参数,args为元组;

而加**的变量kwargs会存放命名参数,即形如key=value的参数, kwargs为字典。

def function(a,b,*args,**kwargs):#装箱
    print(a,b)
    print(args)#元组存放
    print(kwargs)#字典存放
A = (33,44,55)
B = {"name":"jerry","age":6}
function(11,22,*A,**B)        #拆包元组和字典

全局变量

在python中,创建变量时默认是定义新的变量(局部变量),除非显式声明global。

对于不可变对象(数值,字符串,元祖)的全局变量来说,因其指向的数据不能修改,所以不使用global时无法修改全局变量。

a = 1
def f():
    a += 1    # a = a+1,默认局部变量,变量a没有指向,就被引用计算,故报错
    print(a)
f()

UnboundLocalError: local variable 'a' referenced before assignment

对于可变对象(比如list和dictionary)的全局变量来说,因其指向的数据可以修改,所以不使用global时也可修改全局变量。

a = [1,2,3]
def f():
    a.append(4)  # 对可变对象的全局变量a的操作,id相同
f()
print(a)

#结果:[1, 2, 3, 4]

函数传递参数类型

函数传递参数类型,引用传递or值传递。

"""对变量赋值时的地址变化"""
a = [1,2,3]
b = [1,2,3]
c = 1
d = 1

print(id(a))    #2190918037640
print(id(b))    #2190920543560    
print(id(c))    #1929604576 多个变量会指向同一个地址
print(id(d))    #1929604576 

实例1

a = 1
print(id(a))        #1929604576
def function(b):
    print(id(b))    #1929604576 引用传递,指向关系相同
    b += 1          #判断变量b是否可修改,不是则默认执行b = b+1
    print(b)        #2
    print(id(b))    #1929604608 变量b改变指向(不可变对象)
function(a)
print(a)            #1

实例2

a = [1,2,3]                 a = [1,2,3]
def function(b):            def function(b):
    b = b+[4]               b += [4]   #和b.append(4)结果相同
    print(b)                print(b)    
function(a)                 function(a) 
print(a)                    print(a) 

#结果:[1, 2, 3, 4]          结果:[1, 2, 3, 4]
#     [1, 2, 3]                  [1, 2, 3, 4]

总结:赋值相当于变量指向了新的地址(+=除外), 在python中,不可变对象是共享的,创建可变对象永远是分配新地址。

缺省参数

注意:带有默认值的参数一定要位于参数列表的最后面。

def test(a,b=12,c=20):
    pass
test(10,c=10)  #默认b=12,test(10,12,10) 

默认参数的坑

def func(x,l=list()):
    for i in range(x):
        l.append(i*2)
    print(l)

func(2)             #[0, 2]
func(3,[1,2,3])     #[1, 2, 3, 0, 2, 4]
func(3)             #[0, 2, 0, 2, 4]
func(4)             #[0, 2, 0, 2, 4, 0, 2, 4, 6]
func(3,[1,2,3])     #[1, 2, 3, 0, 2, 4]

 匿名函数

语法:变量 = lambda 参数:返回值

ls = lambda  x:x+2
print(ls(5))
#结果:7

内置函数:eval()将字符串str当成有效的表达式来求值并返回计算结果。

def test(a,b,fun):
    return fun(a,b)    #匿名函数的调用
lamb = input("请输入匿名函数:")
test(10,20,eval(lamb))            #eval()原样输出
 #请输入匿名函数: lambda x,y:x+y
 #结果:30

匿名函数的应用

# 取出最大工资对应的人名

salaries = {
    'qiu': 2000,
    'xi': 30000,
    'qiuxi': 2500,
    'rufeng': 3000
}

def func(k):
    return salaries[k]

print(max(salaries,key=func))   # xi

# 1. 将可迭代对象salaries变成迭代器对象iter_obj
# 2. next(iter_obj)得到一个人名,然后将该人名当作参数传给key指定的函数,然后调用函数将函数的返回值当作比较依据
# 3. 比较大小,取出最大值对应的人名

# 上面的函数还可以用匿名函数来实现
print(max(salaries, key=lambda k: salaries[k]))
# 同样,取出最小的使用函数min
print(min(salaries, key=lambda k: salaries[k]))
max与lambda

内置函数:sorted(),排序并返回新的值

a = {k1:v1,k2:v2}
# 从大到小
print(sorted(salaries, key=lambda k: a[k], reverse=True))
# 从小到大
print(sorted(salaries, key=lambda k: a[k]))

内置函数:map()提供的函数对指定序列做映射。

map(function, iterable, ...)

name = [a,b,c,d]
# 列表生成式
new_names = [name+'_1' for name in names]
print(new_names)

# map+匿名函数
res = map(lambda x: x+'_1', names)
print(res)  #打印函数地址
print(list(res))

functools模块函数 :reduce()对参数序列中元素进行累积。

reduce(function, iterable[, initializer])

from functools import reduce
reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
#结果:15

内置函数:filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。

filter(function, iterable)

a = [1,2,3,4,5,6,7,8]
b = filter(lambda x:x%2==0,a) #满足函数条件的留下,即返回值是True,b为迭代器对象
print(list(b))

闭包

函数的嵌套

#函数的嵌套定义
def test1():
    print('-------text1-------')
    def test2():
        print('-------text2-------')
    return test2()
# 嵌套的内部函数只能在test1()里面调用
test1()
#结果
#-------text1-------
#-------text2-------

作用域

全局变量

num = 1
def fun1():
    global num  # 需要使用 global 关键字声明
    print("全局变量:",num)
    num = 12
    print("修改后的值",num)

fun1()
print("全局变量:",num)
#全局变量: 1
#修改后的值 12
#全局变量: 12

nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量.

def fun():
    a = 100
    def fun_in():
        nonlocal a    #嵌套作用域enclosing,外层非全局作用域
        # 创建的变量,默认为局部变量,在引用前赋值操作就会报错(a = a+20)
        a =20+a
        return a
    return fun_in()
print(fun())

LEGB规则查找变量

locals -> enclosing function -> globals -> builtins

即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

闭包函数

闭包:在一个内部函数里对外部作用域(不包括外部函数的外部作用域)的变量进行引用,内部函数就会被认为是闭包。

注意:1、函数名指向的地址变量是可变对象。

   2、闭包函数可以保留其用到的变量的引用。

def test():
    num = 100
    def test_in(a):
        a = a+num
        print(a)
    return test_in
re = test()    #num=100,并返回了test_in函数的引用
re(100)

带参数的闭包

def test(a,b):
    def test_in(x):
        print(a*x+b)
    return test_in

ls1 = test(2,4)    
ls1(3)          #10
ls2 = test(3,5)
print(id(ls1))        #验证ls1和ls2是否指向同一个地址ID:1967395159032
print(id(ls2))
ls2(4)                            #17
ls1(4)                            #12

装饰器

装饰器是用来为被装饰对象添加新功能的一种工具

必须遵循:

1、不能修改被装饰对象的源代码

2、不能修改被装饰对象的调用方式

通用装饰器(带返回值和参数)

import time

current_user={'login':False}#全局变量

def outter(func):  #传递被装饰函数地址
    def wrapper(*args,**kwargs): #传递被装饰函数的参数
        #条件判断
        if current_user['login']:
            return func(*args,**kwargs)

        user=input('username>>>: ').strip()
        pwd=input('password>>>: ').strip()
        if user == 'tom' and pwd == '123':
            current_user['login']=True
            return func(*args,**kwargs)
    return wrapper

@outter
def index():
    time.sleep(1)
    print('welcome to index')
    return 1234

@outter
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)

index()   # 等价于 index = outter(index)
home('tom')

装饰器带参数

带有参数的装饰器,能够起到在运行时,有不同的功能

#如果arg=ok,则执行调用两次,否则调用一次
def fun_arg(arg):
    def func(fun):
        def func_in(*args,**kwargs):
            print("-----记录日志-arg=%s----"%arg)
            if arg=="ok":
                fun(args)
                return fun(args)
            else:
                return fun(args)
        return func_in
    return func


#1. 先执行func_arg("ok")函数,这个函数return 的结果是func这个函数的引用
#2. @func
#3. 使用@func对test1和test2进行装饰

@fun_arg("ok")
def test1(a):
    print("-----test1----%d----"%a)
    return "test1"

@fun_arg("haha")
def test2(b):
    print("-----test2----%d--"%b)
    return "test2"

#调用并返回
ret1 = test1(10)
ret2 = test2(20)
print(ret1,ret2)
'''
-----记录日志-arg=ok----
-----test1----10----
-----test1----10----
-----记录日志-arg=haha----
-----test2----20--
test1 test2
'''

多个装饰器

def makeb(f1):
    print("--------mb----------")
    def makeb_in():
        print("------1------")
        return "<b>"+ f1()+"</b>"
    return makeb_in

def makei(f2):
    print("--------mi----------")
    def makei_in():
        print("------2------")
        return "<i>"+ f2()+"</i>"
    return makei_in
#f1----->f2----->test   test----->makeb_in
#语法糖
@makeb
@makei
def test():
    print("------3------")
    return "hello world!"

"""
执行流程:   1.f2指向test,test指向makei_in
            2.f1指向test,即指向makei_in,test指向了makeb_in
"""

# test = makeb(makei(test))
re = test()
print(re)
''' 
结果:
--------mi----------
--------mb----------
------1------
------2------
------3------
<b><i>hello world</i></b>

类装饰器

#__call__方法使类变成可调用的对象
class Test(object):
    def __init__(self, func):
        print("__init__初始化---")
        print("func name is %s" % func.__name__)
        #保存func变量
        self.__func = func

    def __call__(self):
        print("__call__执行装饰器---")
        self.__func()


@Test       #  test = Test(test)
def test():
    print("----test---")

test()

'''
结果:
__init__初始化---
func name is test
__call__执行装饰器---
----test---

'''
原文地址:https://www.cnblogs.com/liulyuan/p/9885858.html