Python 函数定义和使用

# 函数的概念
#     概念
#         写了一段代码实现了某个小功能; 然后把这些代码集中到一块, 起一个名字; 下一次就可以根据这个名字再次使用这个代码块, 这就是函数
#     作用
#         方便代码的重用
#         分解任务, 简化程序逻辑
#         使代码更加模块化
#     函数分类
#         内建函数
#         三方函数
#         自定义函数

# 函数的基本使用
#     简单定义
#         def 函数名():
#             函数体
#     函数的调用
#         函数名()
def myFun():
    print(1)

#函数调用
myFun()

# 函数的参数
#     单个参数
#         场景
#             需要动态的调整函数体中某一个处理信息
#             则可以, 以参数的形式接收到相关数据
#         定义
#             def 函数名(参数名称):
#                 函数体
#                 函数体中, 可以直接以变量的方式使用该参数
#         函数的调用
#             函数名(参数值)
#         形参和实参的概念
#             上述函数定义中, "参数名称"即为形参;
#             在调用函数的时候, 传递的真实数据, 即为实参
#     多个参数
#         场景
#             需要动态的调整函数体中多个处理信息时
#             则可以以 逗号 做分割, 接收多个参数
#         定义
#             def 函数名(参数名称1, 参数名称2):
#                 函数体
#                 函数体中, 可以直接以变量的方式使用所有参数
#         调用
#             方式1
#                 函数名(参数1, 参数2, 参数3...)
#                     形参和实参一一对应
#             方式2
#                 函数名(参数名称1=参数1, 参数名称n = 参数n...)
#                     可以指明形参名称
#                         称为"关键字参数"
#                     不需要严格按照顺序

#     不定长参数
#         场景
#             如果函数体中, 需要处理的数据, 不确定长度
#             则可以以不定长参数的方式接收数据
#         方式1
#             定义
#                 def 函数名(*args):
#                     元组
#                     函数体
#                     函数体中, 可以直接以元组变量的方式使用该参数
#             使用
#                 函数名(参数1, 参数2, 参数3...)
def myFunA(*args):
    print(args,type(args))
    for v in args:
        print(v)

myFunA(1, 2, 3)  #(1, 2, 3) <class 'tuple'>

#         方式2
#             定义
#                 def 函数名(**dic):
#                     字典
#                     函数体
#                     函数体中, 可以直接以字典变量的方式使用该参数
#             使用
#                 函数名(参数名称1=参数1, 参数名称2=参数2...)
def myFunB(**kwargs):
    print(kwargs, type(kwargs))

myFunB(name = "abc", age = 18) # {'name': 'abc', 'age': 18} <class 'dict'>

#         参数拆包
#             装包
#                 把传递的参数, 包装成一个集合, 称之为"装包"
#             拆包
#                 把集合参数, 再次分解成单独的个体, 称之为"拆包"
def mySum(a, b):
    print(a + b)

def myFunC(*args):
    print(args)
    #拆包
    mySum(*args)

myFunC(1, 2) #(1, 2) 3



#     缺省参数
#         场景
#             当我们使用一个函数的时候, 如果大多数情况下, 使用的某个数据是一个固定值, 或者属于主功能之外的小功能实现;
#             则可以使用默认值
#                 这种参数, 称为"缺省参数"
#         定义
#             def 函数名(变量名1=默认值1, 变量名2=默认值2):
#                 函数体
#                 函数体中, 即使外界没有传递指定变量, 也可以使用, 只不过值是给定的默认值
#         使用
#             函数名(变量1, 变量2)
#                 此处如果是缺省参数, 则可以不填写


#     参数注意
#         值传递和引用传递
#             值传递
#                 是指传递过来的, 是一个数据的副本;
#                 修改副本, 对原件没有任何影响
#             引用传递
#                 是指传递过来的, 是一个变量的地址
#                 通过地址, 可以操作同一份原件
#         但是注意
#             在Python当中, 你没得选, 只有引用传递(地址传递)
#             但是
#                 如果数据类型是可变类型, 则可以改变原件
#                 如果数据类型是不可变类型, 则不可以改变原件

def test(num):
    print(id(num))
    print(num)
    num = 11
    print(id(num)) # 和a地址不一样
a = 10
print(id(a))
test(a) # 打印的id 值一样
print(id(a))
print("------------------")

# 值发生改变,是根据数据类型来说的
def test1(num):
    print(id(num))
    num.append(666)
a = [1, 2, 3]
print(id(a))
test1(a) # 打印的id 值一样
print(a)    #[1, 2, 3, 666]

# 函数的返回值
#     场景
#         当我们通过某个函数, 处理好数据之后, 想要拿到处理的结果
#     语法
#         def 函数():
#     函数体
#     return 数据
#     注意事项
#         1. return 后续代码不会被执行
#         2. 只能返回一次
#         3. 如果想要返回多个数据, 可先把多个数据包装成一个集合, 整体返回
#             列表
#             元组
#             字典
#             ...

def myCall(a, b):
    he = a + b
    cha = a - b
    return (he, cha) # 返回元组,就可以返回多个值了

he, cha  = myCall(11, 10)
print(he, cha) #21 1

# 函数的使用描述
#     场景
#         当我们编写三方函数, 为了方便他人使用, 就需要描述清楚我们所写的函数功能以及使用方式等信息
#     定义格式
#         直接在函数体的最上面, 添加三个双引号对注释
#         def 函数():
#     ''' 这里写帮助信息 '''
#     查看函数使用文档
#         help(函数)
#     经验
#         一般函数的描述, 需要说明如下几个信息
#             函数的功能
#             参数
#                 含义
#                 类型
#                 是否可以省略
#                 默认值
#             返回值
#                 含义
#                 类型

def caculate(a, b = 1):
    """
    计算2个数的和与差
    :param a: 参数1:数值类型,不可选,没有默认值
    :param b: 参数2:数值类型,可选,默认值:1
    :return:返回的是计算的结果:元组类型,(和,差)
    """
    he = a + b
    cha = a - b
    return (he, cha) # 返回元组,就可以返回多个值了

help(caculate)

# 偏函数
#     概念&场景
#         当我们写一个参数比较多的函数时, 如果有些参数, 大部分场景下都是某一个固定值, 那么为了简化使用, 就可以创建一个新函数,
#       指定我们要使用的函数的某个参数, 为某个固定的值; 这个新函数就是"偏函数"
#     语法
#         方式1
#             自己写一个新的
#         方式2
#             借助functools模块的partial函数
#                 import functools
#                 newFunc = functools.partial(函数, 特定参数=偏爱值)
#     场景
#         int()
#方式1
def myFun1(a, b, c, d):
    print(a + b + c + d)

def myFun2(a, b, c, d = 1):
    myFun1(a, b, c, d)

myFun2(1, 2, 3)  #print 7

#方式2
import functools
newFun = functools.partial(myFun1, d = 2)
newFun(1, 2, 3) #print 8

#应用场景
numStr = "10010"
res = int(numStr, base = 2)
print(res)  #print 18
int2 = functools.partial(int, base = 2)
print(int2(numStr)) #print 18

# 高阶函数
#     概念
#         当一个函数A的参数, 接收的又是另一个函数时, 则把这个函数A成为是"高阶函数"
#     例如
#         sorted函数
#     案例
#         动态计算两个数据

l = [{"name": "a1", "age": 18}, {"name": "a2", "age": 20}, {"name": "a3", "age": 19}]
def getKey(x):
    return x["age"] #按age的值来排序
result = sorted(l, key = getKey)
print(result) # [{'name': 'a1', 'age': 18}, {'name': 'a3', 'age': 19}, {'name': 'a2', 'age': 20}]

#案例
def caculate_test(num1,num2, newFun):
    return newFun(num1, num2)

def sum(a, b):
    return a + b

res = caculate_test(1, 2, sum)
print(res) # 3

# 返回函数
#     概念
#         是指一个函数内部, 它返回的数据是另外一个函数; 把这样的操作成为"返回函数"
#     案例
#         根据不同参数, 获取不同操作, 做不同计算
def getFunc(flag):
    def sum(a, b, c):
        return a + b + c
    def jian(a, b, c):
        return a - b - c

    if flag == "+":
        return sum
    elif flag == "-":
        return jian

newFun = getFunc("+")
res = newFun(1, 2, 3)
print(res) # 6

# 匿名函数
#     概念
#         也称为 "lambda函数"
#         顾名思义, 就是指没有名字的函数
#     语法
#         lambda 参数1, 参数2.. : 表达式
#         限制
#             只能写一个表达式
#                 不能直接return
#             表达式的结果就是返回值
#             所以, 只适用于一些简单的操作处理
#     测试
#         func = lambda x, y: x + y
#         func(1, 2)

result = (lambda x, y : x + y)(1, 2)
print(result, type(result)) #3 <class 'int'>

newFunc = lambda x, y : x + y
print(newFunc(4,5)) # 9

#应用场景
l = [{"name": "a1", "age": 18}, {"name": "a2", "age": 20}, {"name": "a3", "age": 19}]
result = sorted(l, key = lambda x : x["age"])
print(result) # [{'name': 'a1', 'age': 18}, {'name': 'a3', 'age': 19}, {'name': 'a2', 'age': 20}]

# 闭包
#     概念
#         在函数嵌套的前提下
#         内层函数引用了外层函数的变量(包括参数)
#         外层函数, 又把 内层函数 当做返回值进行返回
#         这个内层函数+所引用的外层变量, 称为 "闭包"
#     标准格式
#         def test1(a):
#             b = 10
#             其他函数定义代码
#             def test2():
#                 print(a)
#                 print(b)
#             return test2
#     应用场景
#         外层函数, 根据不同的参数, 来生成不同作用功能的函数
#     案例
#         根据配置信息, 生成不同的分割线函数
#     注意事项
#         1. 闭包中, 如果要修改引用的外层变量
#             需要使用  nonlocal 变量  声明
#             否则当做是闭包内, 新定义的变量
#         2. 当闭包内, 引用了一个, 后期会发生变化的变量时, 一定要注意
#             函数, 是被调用时, 才去确定变量标识所对应的值

#应用场景
def line_config(content,length):
    print("-" * (length // 2) + content + "-" * (length // 2))

line_config("闭包",20) #----------闭包----------
line_config("闭包",20) #----------闭包----------
#上面的方式调用麻烦,现在改造一下,方便调用

def line_config(content,length):
    def line():
        print("-" * (length // 2) + content + "-" * (length // 2))
    return line

line1 = line_config("闭包",20)
line1() #----------闭包----------
line1() #----------闭包----------

line2 = line_config("闭包",40)
line2() #--------------------闭包--------------------

# 装饰器
#     作用
#         在函数名以及函数体不改变的前提下, 给一个函数附加一些额外代码
#     语法
#         @装饰器
# def 被装饰函数():
#     code
#     案例
#         发说说, 发图片
#             附加
#                 身份验证 操作
#         "开放封闭"原则
#             已经写好的代码, 尽可能不要修改
#             如果想要新增功能, 在原先代码基础上, 单独进行扩展
#         单一职责
#     注意
#         装饰器的执行时间, 是立即执行
#     进阶
#         装饰器叠加
#             从上到下装饰
#             从下到上执行
#         对有参函数进行装饰
#             无论什么场景, 保证函数调用参数个数一致
#             为了通用, 可以使用不定长参数, 结合 拆包操作进行处理
#         对有返回值的函数进行装饰
#             无论什么场景, 保证函数返回值一致
#         带有参数的装饰器
#             通过@装饰器(参数)的方式, 调用这个函数, 并传递参数; 并把返回值, 再次当做装饰器进行使用
#             先计算 @ 后面的内容, 把这个内容当做是装饰器

def checkLogin(func):
    print("*" * 10 ) # 只在使用@装饰器运行
    def inner():
        print("登陆认证....")
        func()
        print("-" * 10) # 函数调用后才运行
    return  inner

@checkLogin # fss = checkLogin(fss)
def fss():
    print("发说说")

@checkLogin # fpic = checkLogin(fpic)
def fpic():
    print("发图片")

#下面为业务的逻辑部分,如果要想在程序中保持业务逻辑和代码不变,就需要上面的装饰符
indexItem = 1
if indexItem == 1 :
    fss()
else:
    fpic()


# 生成器
#     生成器
#         是一个特殊的迭代器(迭代器的抽象层级更高)
#         所以, 拥有迭代器的特性
#             惰性计算数据, 节省内存
#             能够记录状态, 并通过next()函数, 访问下一个状态
#             具备可迭代特性
#         但是, 如果打造一个自己的迭代器, 比较复杂
#             需要实现很多方法
#                 后续在"面向对象"编程中会进行讲解
#             所以, 就有一个更加优雅的方式 "生成器"
#     创建方式
#         生成器表达式
#             把列表推导式的[] 修改成  ()
#             (i for i in range(1, 10000000) if i % 2 == 0)
#         生成器函数
#             函数中包含  yield语句
#             这个函数的执行结果就是 "生成器"
#     产生数据的方式
#         生成器具备可迭代特性
#         next()函数
#             等价于
#                 生成器.__next__()
#         for in
#     send() 方法
#         send方法有一个参数,指定的是上一次被挂起的yield语句的返回值
#         相比于.__next__()
#             可以额外的给yield 语句 传值
#         注意第一次调用
#             t.send(None)
#     关闭生成器
#         g.close()
#         后续如果继续调用, 会抛出StopIteration异常提示
#     注意
#         如果碰到return
#             会直接终止, 抛出StopIteration异常提示
#         生成器只会遍历一次

# L = [i for i in range(1,100) if i % 2 == 0] #直接生成列表,range生成比较多的时候,浪费内存
L = (i for i in range(1, 100) if i % 2 == 0)
print(L) #<generator object <genexpr> at 0x03A7AC60>
print(next(L))  #   2
print(next(L))  #   4
print(L.__next__()) # 6

for i in L:
    print(i) # 从 8 开始,因为上面已经移动了指针

def test():
    print("xxx")
    yield 1
    print("a")

    yield 2
    print("b")

runing = test() # 不会打印xxx
for i in runing:
    print(i)


# 递归函数
#     体现
#         函数A内部, 继续调用函数A
#         概念
#             传递
#             回归
#     注意事项
#         有传递, 一定要有回归
#         否则, 就是无限的循环调用
#     案例
#         求一个数值的阶乘 5!= 5 * 4 * 3 * 2 * 1

def jiecheng(n):
    if n == 1:
        return 1
    return n * jiecheng(n - 1)

print(jiecheng(5)) # 120

# 函数作用域
#     基本概念
#         变量的作用域
#             变量的作用范围
#                 可操作范围
#             Python是静态作用域,也就是说在Python中,变量的作用域源于它在代码中的位置; 
# 在不同的位置, 可能有不同的命名空间
#         命名空间
#             是作用域的体现形式
#             不同的具体的操作范围
#         Python-LEGB
#             L-Local
#                 函数内的命名空间
#                     作用范围: 当前整个函数体范围
#             E-Enclosing function locals
#                 外部嵌套函数的命名空间
#                     作用范围: 闭包函数
#             G-Global
#                 全局命名空间
#                     作用范围: 当前模块(文件)
#             B-Builtin
#                 内建模块命名空间
#                     作用范围: 所有模块(文件)
#             注意
#                 Python中没有块级作用域
#                 块级作用域
#                     代码块中, 比如 if while for 后的代码块
#             LEGB规则
#                 按照L -> E -> G -> B 的顺序进行查找
#     基于命名空间的常见变量类型
#         局部变量
#             在一个函数内部定义的变量;
#             作用域为函数内部
#             查看局部变量
#                 locals()
#         全局变量
#             在函数外部, 文件最外层定义的变量
#             作用域为整个文件内部
#             查看全局变量
#                 globals()
#         注意点
#             访问原则
#                 从内到外
#             结构规范
#                 全局变量
#                 函数定义
#                     使用
#                     修改
#                 后续代码
#             全局变量和局部变量重名
#                 获取
#                     就近原则
#                 修改
#                     global 全局变量
#                         声明
#                     l -> e
#                         unlocal 
#             命名
#                 全局变量
#                     g_xxx

 

posted @ 2018-02-25 17:14  delphiclub  阅读(292)  评论(0编辑  收藏  举报