• 函数的本质
    • 就是一串内存地址
    • 可以赋值、可以作为容器类型的元素、函数的参数和返回值 —— 第一类对象
  • 一般情况下形参定义了多少个,在函数调用的时候就要穿多少个,除非使用了 形参默认值 或 动态参数,不然会报错
  • 实参定义顺序必须先按照位置传参, 再按照关键字传参数
  • 形参定义顺序位置参数, *args, 默认参数, 关键字参数, **kwargs

1. 函数的定义

def fn(a):
    print(a)
    return a

return_val = fn('传参')

2. 函数的返回值

  • 没有返回值 -> 返回 None

def fn(a):
    print(a)

return_val = fn('传参') # None

  • 返回多个值用多个变量接收

def fn(a):
    print(a)
    return 1, 2, 3

r1, r2, r3 = fn('传参')

  • 返回多个值用一个变量接收 -> 接收到的是 元组

def fn(a):
    print(a)
    return 1, 2, 3

return_tuple = fn('传参') # (1, 2, 3)

3. 实参

  • 按照位置传参

def fn(a, b):
    print(a, b)

fn('data1', 'data2')

  • 按照关键字传参

def fn(a, b):
    print(a, b)

fn(b='data2', a='data1')

  • 混合使用 按位置传参 和 按关键字传参 -> 必须先按照位置传参, 再按照关键字传参数

def fn(a, b, c, d):
    print(a, b, c, d)

fn(1, 2, d=4, c=3)

4. 形参

  • 形参默认值

def fn(a, b=2):
    print(a, b)

fn(1)

ef fn(a, b=2):
    print(a, b)

fn(1, 3)

  • 形参默认值陷阱 -> 如果默认参数的值是一个可变数据类型,那么每一次调用函数的时候,如果不传值就共用这个数据类型的资源

def trap(lis=[]):
    lis.append(1)
    print(lis)

trap()
trap()
trap()

def trap(key, dic={}):
    dic[key] = 'val'
    print(dic)

trap(1)
trap(2)
trap(3)

5. 动态形参

  • *形参名 -> 约定俗成形参名: args -> 接收的是按照位置传参的值,组织成一个元组

def fn(*args):
    print(args) # 元组: (1, 2, 3, 4, 5, 6)

fn(1, 2, 3, 4, 5, 6)

  • **形参名 -> 约定俗成形参名: kwargs -> 接受的是按照关键字传参的值,组织成一个字典

def fn(**kwargs):
    print(kwargs) # 字典: {'k3': 'v3', 'k2': 'v2', 'k1': 'v1'}

fn(k1='v1', k2='v2', k3='v3')

  • *args **kwargs 混合使用

    • 使用场景: 当函数不知道有没有参数或者不确定参数个数的时候可以使用 *args **kwargs

def fn(*args, **kwargs):
    print(args) # (1, 2, 3)
    print(kwargs) # {'k1': 'v1', 'k3': 'v3', 'k2': 'v2'}

fn(1, 2, 3, k1='v1', k2='v2', k3='v3')

def fn(*args, default='形参默认值', **kwargs):
    print(args) # (1, 2, 3)
    print(default) # '形参默认值'
    print(kwargs) # {'k1': 'v1', 'k3': 'v3', 'k2': 'v2'}


fn(1, 2, 3, k1='v1', k2='v2', k3='v3')

6. 动态实参 

  • *序列 -> 在序列(列表,元组)前面加上*,就是将该序列按照顺序打散传递给函数中

list1 = [1, 2, 3]

def fn(d1, d2, d3):
    print(d1, d2, d3)  # 1 2 3

fn(*list1)  # 加了* 就类似于 fn(1, 2, 3)

list1 = [1, 2, 3]

def fn(*args):
    print(args) # (1, 2, 3)

fn(*list1) # 加了* 就类似于 fn(1, 2, 3)

  • **字典 -> 在字典前面加上**,就是将该字典打散传递给函数中

dict1 = {'k1': 1, 'k2': 2, 'k3': 3}

def fn(k1, k2, k3):
    print(k1, k2, k3)  # 1 2 3

fn(**dict1)  # 加了 ** 类似于 fn(k1=1, k2=2, k3=3)

dict1 = {'k1': 1, 'k2': 2, 'k3': 3}

def fn(**kwargs):
    print(kwargs) # {'k1': 1, 'k2': 2, 'k3': 3}

fn(**dict1) # 加了 ** 类似于 fn(k1=1, k2=2, k3=3)

7. 混合使用所有类型

list1 = [1, 2, 3]
dict1 = {'k1': 1, 'k2': 2, 'k3': 3}


def fn(name, age, *args, default='默认值', top, **kwargs):
    print(name) # Kevin
    print(age) # 18
    print(args) # ('175cm', '120kg', 1, 2, 3)
    print(top) # 12
    print(default) # 默认值
    print(kwargs) # {'k1': 1, 'k2': 2, 'k3': 3, 'left': 13}


fn('Kevin', 18, '175cm', '120kg', *list1, top=12, left=13, **dict1)

8. 给函数添加自定义属性

def fn():
    return 1


fn.attr = '自定义属性'

print(fn.attr)  # '自定义属性'

9. .__name__ -> 获取函数的函数名

def fn():
    print('函数')


fn_name = fn.__name__ #fn

10. .__doc__ -> 获取函数的注释 没啥用

def fn():
'''
    函数的注释
    :return:
    '''
    print('函数')


fn_doc = fn.__doc__

11. 函数注释

def func():
'''
        这个函数实现了什么功能
        参数1:
        参数2:
        :return: 是字符串或者列表的长度
    '''
    pass

12. 在函数里面可以直接对函数外的列表,字典,集合进行修改

lis = [1, 2, 3]
dic = {'a': 1, 'b': 2}

def fn():
    lis.append(4)
    dic['c'] = 3

fn()
print(lis)  # [1, 2, 3, 4]
print(dic)  # {'a': 1, 'b': 2, 'c': 3}

13. 什么是函数?什么是方法?

  • 一般情况

    • 写在模块中的叫函数,写在类中的叫方法

  • 特殊情况

    • 写法: 对象.fn() -> 叫方法

from types import FunctionType, MethodType


class Foo(object):
    def fn(self):
        return '值'


obj = Foo()

# 验证是否是函数
print(isinstance(obj.fn, FunctionType))  # False
# 验证是否是方法
print(isinstance(obj.fn, MethodType))  # True

    • 写法: 类.fn() -> 叫函数

from types import FunctionType, MethodType


class Foo(object):
    def fn(self):
        return '值'


# 验证是否是函数
print(isinstance(Foo.fn, FunctionType))  # True
# 验证是否是方法
print(isinstance(Foo.fn, MethodType))  # False