函数

函数

函数是一系列代码的集合,用来完成某项特定的功能 ,有内置函数和自定义函数

为什么要用函数

1、代码的组织结构不清晰,可读性差
2、遇到重复的功能只能重复编写实现代码,代码冗余
3、功能需要扩展时,需要找出所有实现该功能的地方修改之,无法统一管理且维护难度极大

(自定义)函数的组成

函数名:使用该函数的依据 见名知意(函数名存放的是函数地址)

函数体:完成功能的代码块

返回值:功能完成的反馈结果(可有可无)

参数:完成功能需要的条件信息(形参实参)

函数在定义阶段只检测语法不执行代码

返回值:可以返回一个值,或者多个值(元组接收,解压接收)

参数

实参

位置实参 :按位置传值给形参

关键字实参:按照变量名传值可以无序

注:关键字实参一定要在位置实参之后

'''
关键字实参必须出现在位置实参后
2.多个位置实参还是按照位置传参
3.关键字实参为没有从位置实参拿到值的形参传值,可以不用按照指定顺序
'''
def fn2(a, b, c, d, e, f):
    print(a, b, c, d, e, f)

fn2(10, 20, 30, e=100, f=500, d=200)

形参

位置形参的范畴
  • 位置形参
# positional argument
def fn(a, b, c):
    print(a, b, c)
    
# 位置形参可以由 位置实参 与 关键字实参 来传值
fn(10, 20, 30)
fn(a=10, b=20, c=30)

  • 默认形参
def fn(a=10,b=20):
    print(a, b)
    
# 默认形参可以由 位置实参 与 关键字实参 来传值,还可以不用传值(采用自身默认值)
fn()
fn(20, 30)
fn(a=200, b=300)
fn(100)  # 就给a传
fn(b=100)  # 就给b传


# 混用
# 位置形参与默认形参同时存在,默认形参必须在后
def fn1(a,b,c=10,d=20):
    print(a, b, c, d)

# 位置形参必须传值,默认形参分情况传值
fn1(100, 200, d=1000)


  • 可变长形参
# 可变长形参会以 元组 形式接受 位置形参与默认形参未接受完的 所有传入的位置实参,用索引来取第几个
# 可变长形参只能由 位置实参 来传值  *****
def fn(a, b=10, *args):
    print(a, b)
    print(args)
    
    
# 小细节:可变长形参只能接受位置实参的值,位置实参还必须在关键字实参前,
#       导致默认形参只能由位置实参来传值
fn(1, 20, 100, 200) # 1给a  20给b,不能用b=20传  100,200给args
    

关键字形参范畴
# 前提:出现在 * 之后的形参
def fn(a, b=10, *, c, d=20, e):
	pass
# c,d,e都是出现在*之后,都是关键字形参,由于必须由 关键字实参 来传值,没有顺序的强行要求


# 可变长关键字形参:用来接收没有被关键字形参接收完的关键字形参,也只能由关键字实参来传值
# 用字典来存放数据
def fn(**kwargs):
    print(kwargs)
fn(a=10,b=20)  # {'a': 10, 'b': 20}

dic = {'x': 100, 'y': 200}
fn(**dic)  # {'x': 100, 'y': 200}



函数对象

函数名就是存放了函数的内存地址,存放了内存地址的变量都是对象,即 函数名 就是 函数对象

函数对象的应用

  1. 可以直接被引用 fn = cp_fn

  2. 可以当作函数参数传递 computed(cp_fn, 100, 20)

  3. 可以作为函数的返回值 get_cp_fn(cmd): return add

  4. 可以作为容器类型的元素 method_map: 对应关系中的值

def add(a, b):
    return a + b
def low(a, b):
    return a - b
def jump(a, b):
    return a * b
def full(a, b):
    return a / b
def quyu(a, b):
    return a % b
def computed(fn, n1, n2):
    res = fn(n1, n2)
    return res
method_map = {
    'add': add,
    'low': low,
    'jump': jump,
    'full': full,
    'quyu': quyu,
}
# 根据指令获取计算方法
def get_cp_fn(cmd):
    if cmd in method_map:
        return method_map[cmd]
    return add  # 输入有误用默认方法处理


while True:
    cmd = input('cmd: ')
    if cmd == 'quit':
        break
    cp_fn = get_cp_fn(cmd)
    result = computed(cp_fn, 100, 20)
    print(result)

名称空间

名称空间:存放名字与内存空间地址对应关系的容器

作用:解决由于名字有限,导致名字重复发送冲突的问题

三种名称空间

Built-in:内置名称空间;系统级,一个;随解释器执行而产生,解释器停止而销毁

Global:全局名称空间;文件级,多个;随所属文件加载而产生,文件运行完毕而销毁

Local:局部名称空间;函数级,多个;随所属函数执行而产生,函数执行完毕而销毁

注:

del 名字:可以移除查找最近的名字与内存空间地址的对应关系

加载顺序:Built-in > Global > Local

global关键词

def fn()
	global num
    num = 20
    print(num)
# global关键词可以将Local的名字提升为Global的名字
# 一个文件中的Global名字就是一个,所以函数内部外部使用的名字都是一个
fn()  # 注:一定要调用函数,才能产生名字,并提升
print(num)

nonlocal关键字

# 作用:将 L 与 E(E中的名字需要提前定义) 的名字统一

# 应用场景:如果想在被嵌套的函数中修改外部函数变量(名字)的值

# 案例:
def outer():
    num = 10
    print(num)  # 10 
    def inner():
        nonlocal num
        num = 20
        p77rint(num)  # 20
    inner()
    print(num)  # 20


作用域

作用域:名字起作用的范围
作用:解决同名字可以共存问题

四种作用域
Built-in:内置作用域,所有文件所有函数
Global:全局作用域,当前文件所有函数
Enclosing:嵌套作用域,当前函数与当前函数的内部函数
Local:局部作用域,当前函数

注:
不同作用域之间名字不冲突,以达到名字的重用
查找顺序:Local > Enclosing > Global > Built-in

len = 10
def outer():
    len = 20  # 外层函数的局部变量:Enclosing - 嵌套作用域
    def inner():
        len = 30
        print('1:', len)  # 30, inner -> outer -> global -> built-in
    inner()
    print('2:', len)  # 20, outer -> global -> built-in
outer()
print('3:', len)  # 10, global -> built-in

del len
print('4:', len)  # len地址, built-in

闭包

# closure:被包裹的函数,称之为闭包
# 完整的闭包结构:1.将函数进行闭包处理;2.提升函数名的作用域,将内部函数对象作为外部函数的返回值

def outer(url):
    def get_html():
        html = requests.get(url)
        print(html.text)
    return get_html
# 先预定义多个爬虫方法,爬页面操作并未执行
baidu = outer('https://www.baidu.com')
python = outer('https://www.python.org')
sina = outer('https://www.sina.com.cn')
# 什么时候想爬什么页面就调用指定页面的爬虫方法
baidu()
sina()
baidu()

装饰器

开放封闭原则:不改变调用方式与源代码上增加功能

1.不能修改被装饰对象(函数)的源代码(修改是封闭
2.不能更改被修饰对象(函数)的调用方式,且能达到增加功能的效果(拓展是开放的

注意:装饰器内的闭包函数有无返回值,以及闭包函数的参数有无都取决于被装饰函数

# 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
# 被装饰的函数:fn
# 外层函数:outer(func)  outer(fn) => func = fn
# 替代版函数: return inner: 原功能+新功能

def fn():
    print("原有功能")

# 装饰器
def outer(tag):
    def inner():
    	tag()
        print(新增功能")
    return inner
fn = outer(fn)              
              
fn()

@语法糖: @外层函数

def outer(f):
    def inner():
    	f()
        print("新增功能1")
    return inner
              
def wrap(f):
    def inner():
    	f()
        print("新增功能2")
    return inner              

@wrap  # 被装饰的顺序决定了新增功能的执行顺序
@outer  # <==> fn = outer(fn): inner      
def fn():
    print("原有功能")

装饰器最终写法


def wrap(fn):
    def inner(*args, **kwargs):
        print('前增功能')
        result = fn(*args, **kwargs)
        print('后增功能')
        return result
    return inner

带参装饰器:了解

def outer(input_color):
    def wrap(fn):
        if input_color == 'red':
            info = '\033[36;41mnew action\33[0m'
        else:
            info = 'yellow:new action'

        def inner(*args, **kwargs):
            pass
            result = fn(*args, **kwargs)
            print(info)
            return result
        return inner
    return wrap  # outer(color) => wrap


color = input('color: ')
@outer(color)  # @outer(color) ==> @wrap  # func => inner
def func():
    print('func run')

func()

可迭代对象

可迭代协议:只要含有 __ iter __ 方法的都是可迭代对象

可迭代对象: 有__ iter __()方法的对象,调用该方法返回迭代器对象

有哪些:str | list | tuple | dict | set | range() | file | 迭代器对象 | enumerate() | 生成器

迭代器

迭代器协议:内部含有__ iter __ ()方法和 __ next() __方法的就是迭代器

优点:1.不依赖索引,可以把容器中所有值取到

​ 2.节约内存空间

缺点:不能计算长度,不能指定位取值(只能从前往后逐一取值)

特性:惰性运算 一个迭代器只能使用一次

迭代器对象

可迭代对象.__ iter __() 构成迭代器

'''
迭代器对象: 有__next__()方法的对象,也就是用该方法一次从迭代器对象中获取一个值,取出一个少一个

有哪些:file | enumerate() | 生成器

重点:
1.从迭代器对象中取元素,取一个少一个,如果要从头开始去,需要重新获得拥有所有元素的迭代器对象
2.迭代器对象也有__iter__()方法,调用后得到的是自己本身(当前含义几个元素,得到的就只有几个元素的迭代器对象)
'''

for循环本质

1.自动获取被迭代对象的迭代器对象
2.在内部一次一次调用__next__()方法取值;
3.自动完成异常处理

生成器

本质:迭代器

自己写生成器的两种方法:生成器函数生成器表达式

# 生成器:包含yield关键字的函数就是生成器函数
def my_generator():
    yield 1
    yield 2
    yield 3
g_obj = my_generator()
# my_generator()并不会执行函数体,得到的返回值就是生成器对象
# 生成器对象就是迭代器对象
r1 = g_obj.__next__() # 1

for v in g_obj:
    print(v)  # 2 | 3

posted @ 2019-03-29 14:03  会飞的空心菜  阅读(125)  评论(0)    收藏  举报