Python中你所不知道的关于函数的秘密,原来函数这么简单

前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。

PS:如有需要Python学习资料的小伙伴可以点击下方链接自行获取

Python免费学习资料、代码以及交流解答点击即可加入


一、函数的初识

1.1、面向过程

1)获取任意一个字符串的元素的个数

s1 = 'fdskjlgfdgfdjkslgdfjkjafdsajk'
count = 0
for i in s1:
    count += 1
print(count)

2)获取列表的元素的个数

l1 = [1, 2, 3]
count = 0
for i in l1:
    count += 1
print(count)

面向过程缺点:

# 1. 代码重复。
# 2. 代码可可读性不高。

1.2、初识函数

l1 = [1, 2, 3]

def new_len():
    count = 0
    for i in l1:
        count += 1
    print(count)

new_len() #函数的调用

函数优点:

# 1、函数:函数是以功能为导向,一个函数封装一个功能。登录,注册,文件的改的操作.....
# 2、函数减少代码的重复性,增强了代码的可读性

1.3、函数的结构

l1 = [1, 2, 3]


def new_len():
    count = 0
    for i in l1:
        count += 1
    print(count)


new_len()
'''
def 关键字: 定义一个函数。紧跟一个空格。
new_len函数名:与变量命名规范一致。一定要具有可描述性。 
():结构需要,传参使用。
    : 分割符。
tab键:四个空格。缩进。函数体

'''

1.4、函数的调用

l1 = [1, 2, 3]


def new_len():
    count = 0
    for i in l1:
        count += 1
    print(count)


new_len()  # 函数名() 函数的执行者。调用者。
new_len()  # 函数名() 函数的执行者。
new_len()  # 函数名() 函数的执行者。
new_len()  # 函数名() 函数的执行者。
new_len()  # 函数名() 函数的执行者。

# -----------------------------------------------
l1 = [1, 2, 3]


def new_len():
    count = 0
    for i in l1:
        count += 1
    print(count)


for i in range(3):
    new_len()   # 函数名():函数的执行者。调用者。

1.5、函数的返回值

return关键字作用:

# 1、结束函数
# 2、给函数的执行者返回具体的值
 1.函数中没有return或者只写一个return,函数的执行者得到的是None。
    2.函数中return后面是单个值,函数的执行者得到的是这个值(不改变值的类型)。 
    3.函数中return后面是多个值,函数的执行者得到的是一个元组。
# return 第一个作用:结束函数。
l1 = [1, 2, 3]


def new_len():
    count = 0
    for i in l1:
        count += 1
    return count


print(new_len())

# ------------------------------------------------------------------------
# return第二个作用:给函数的执行者返回具体的值。
'''
    1.函数中没有return或者只写一个return,函数的执行者得到的是None。
    2.函数中return后面是单个值,函数的执行者得到的是这个值(不改变值的类型)。 
    3.函数中return后面是多个值,函数的执行者得到的是一个元组。
'''
l1 = [1, 2, 3]


def new_len():
    count = 0
    for i in l1:
        count += 1
    return


print(new_len())  # None


# ------------------------------------

def func():
    print(111)
    # return 100  # 100 <class 'int'>
    # return [1, 2, 3]    # [1, 2, 3] <class 'list'>
    return {'name': '太白'}  # {'name': '太白'} <class 'dict'>


ret = func()
print(ret, type(ret))


# ------------------------------------
def func():
    print(111)
    return 1, 'AAA', [22, 33]  # 返回一个元祖


ret = func()
print(ret, type(ret))  # (1, 'AAA', [22, 33]) <class 'tuple'>


# ------------------------------------
def func():
    print(111)
    return 1, 'AAA', [22, 33]


a, b, c = func()  # 元祖的解构
print(a, b, c)  # 1 AAA [22, 33]

# ------------------------------------
def func():
    print(111)
    # return 1+1+2
    return 2 > 1


ret = func()
print(ret)  # True

二、函数的传参

2.1、参数的种类

def new_len(a):  # 定义函数时:参数:形参。
    count = 0
    for i in a:
        count += 1
    return count


l1 = [1, 2, 3]
s1 = 'fdsjaklsfjgfds'
print(new_len(l1)) # 函数的调用者:参数 实参。
print(new_len(s1))  # 函数的调用者:参数 实参。
print(len(s1))

'''
实参角度:
    1. 位置参数。
    2. 关键字参数。
    3. 混合参数。
形参角度:
    1. 位置参数。
    2. 默认参数。
    3. 动态参数。
    4. 仅限关键字参数。(了解)
'''

2.2、实参角度

2.2.1、位置参数

从左至右,按照顺序,一一对应

def meet(sex, age, job):
    print(f'性别{sex},年龄{age}岁,{job}')

meet('女', '18~25', '讲师')


# --------------------------------------
# 写一个函数,接收两个数字的参数,将较大的数字返回。
def comp(a, b):
    if a > b:
        return a
    else:
        return b


ret = comp(1, 2000)
print(ret)

# --------------------------------------
# 三元运算符:简单的if else。
def comp(a, b):
    # ret = a if a > b else b
    # return ret
    return a if a > b else b


ret = comp(1, 2000)
print(ret)

2.2.2、关键字参数

def meet(sex, age, job, hight, weight, ):
    print(f'性别{sex},年龄{age}岁,身高{hight},体重{weight},工作{job}')


meet(sex='女', job='学生', weight=120, hight=170, age='18~25')

2.2.3、混合参数

# 关键字参数一定要在位置参数后面,一一对应
def meet(sex, age, job, hight, weight, ):
    print(f'性别{sex},年龄{age}岁,身高{hight},体重{weight},工作{job}')


meet('男', 27, 'ITC语言', weight=120, hight=175, )

2.3、形参角度

2.3.1、位置参数

# 位置参数:与实参角度位置参数一样
def meet(sex, age, job):
    print(f'性别{sex},年龄{age}岁,{job}')


meet('女', '18~25', '讲师')

2.3.2、默认参数

# 默认参数: 一定在位置参数后面,不传参数即沿用默认的参数
# open的源码
open('文件的改', encoding='utf-8')
def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True)
    
# --------------------------
def meet(age, job, sex='女'):
    print(f'性别{sex},年龄{age}岁,{job}')


meet('18~25', '幼师')     #

# 更改默认参数
meet('18~25', '幼师', sex='laddy_boy')
meet('18~25', '幼师', 'laddy_boy')

特别注意:

def func(a,b=False):
    print(a)
    print(b)
func(1,True)

# 当你的默认参数如果是可变的数据类型,你要小心了。
def func(a,l=[]):
    l.append(a)
    return l
print(func(1))  # [1]
print(func(2))  # [1, 2]
print(func(3))  # [1, 2, 3]

2.3.3、动态参数

动态参数分为两种:动态接受位置参数 *args,动态接收关键字参数 **kwargs

2.3.3.1、*args

# *会将实参所有的位置参数接收,放置在一个元组中,并将这个元组赋值给args这个形参
def eat(*args):
    print('我请你吃:', args)


eat('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿', '烧花鸭', '烧雏鸡', '烧子鹅')


# ------------------------------------------------
def my_max(*args):
    n = 0
    for i in args:
        n += i
    return n


print(my_max(1, 2, 3, 4))   # 10

2.3.3.2、**kwargs

# **kwargs,是接受所有的关键字参数然后将其转换成一个字典赋值给kwargs这个形参
def func(**kwargs):
    print(kwargs)


func(name='太白金星', sex='男')  # {'name': '太白金星', 'sex': '男'}

2.3.3.3、*args和 **kwargs结合

def func(*args, **kwargs):
    print(args)  # ('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿')
    print(kwargs)  # {'name': '太白金星', 'sex': '男'}


func('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿', name='太白金星', sex='男')

2.3.3.4、* 的魔性用法

1)聚合作用

def eat(args):
    print('我请你吃:', args)  # 我请你吃: 蒸羊羔儿


eat('蒸羊羔儿')


def eat(*args):
    print('我请你吃:', args)


eat('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿')  # 我请你吃: ('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿')

2)打散

s1 = 'alex'
l1 = [1, 2, 3, 4]
tu1 = ('武sir', '太白', '女神',)


def func(*args):
    print(args)  # ('alex', [1, 2, 3, 4], ('武sir', '太白', '女神'))


func(s1, l1, tu1)

# -----------
s1 = 'alex'
l1 = [1, 2, 3, 4]
tu1 = ('武sir', '太白', '女神',)


def func(*args):
    print(args)  # ('a', 'l', 'e', 'x', 1, 2, 3, 4, '武sir', '太白', '女神')


func(*s1, *l1, *tu1)    # 打散


# -----------
dic1 = {'name': '太白', 'age': 18}
dic2 = {'hobby': '喝茶', 'sex': '男'}


def func(**kwargs):
    print(kwargs)  # {'name': '太白', 'age': 18, 'hobby': '喝茶', 'sex': '男'}


func(**dic1, **dic2)


def func(*args, **kwargs):
    print(args, kwargs)

# -----------
l1 = [1, 2, 3]
l2 = ['太白', 'wusir', '景女神']
# func(l1, l2)  # ([1, 2, 3], ['太白', 'wusir', '景女神']) {}
#
# func(*l1, *l2)  # (1, 2, 3, '太白', 'wusir', '景女神') {}
# func(*[1, 2, 3], *(11, 22), *'fdsakl')  # (1, 2, 3, 11, 22, 'f', 'd', 's', 'a', 'k', 'l') {}
# func(*[1, 2, 3], *(11, 22), name='aaa', age='18')  # (1, 2, 3, 11, 22) {'name': 'aaa', 'age': '18'}


# ----------------------------------------------------------

def func(*args, **kwargs):
    print(args, kwargs)

# 当函数的执行时:**dict 代表打散。
# func(**{'name': "alex"}, **{'age': 73, 'hobby': '吹'})   # () {'name': 'alex', 'age': 73, 'hobby': '吹'}

3)处理剩下的元素

# 分别赋值
a, b = (1, 2)
print(a, b)  # 1 2

# 其实还可以这么用:
a, *b = (1, 2, 3, 4,)
print(a, b)  # 1 [2, 3, 4]
*rest, a, b = range(5)
print(rest, a, b)  # [0, 1, 2] 3 4
print([1, 2, *[3, 4, 5]])  # [1, 2, 3, 4, 5]

2.3.4、仅限关键字参数

# 仅限关键字参数
# 这样传参是错误的,因为仅限关键字参数c只接受关键字参数
# def func(a, b, *args, c):
#     print(a, b)  # 1 2
#     print(args)  # (4, 5)

# func(1, 2, 3, 4, 5)


# 这样就正确了:
def func(a, b, *args, c):
    print(a, b)  # 1 2
    print(args)  # (3, 4)
    print(c)  # 5


func(1, 2, 3, 4, c=5)

2.3.5、形参最终顺序

形参角度的所有形参的最终顺序为:**位置参数, *args,默认参数,仅限关键字参数,kwargs。

def foo(a, b, *args, c, sex=None, **kwargs):
    print(a, b, c, sex, args, kwargs)


foo(1, 2, 3, 4, c=6)  # 1 2 6 None (3, 4) {}
# foo(1, 2, sex='男', name='alex', hobby='old_woman')  # TypeError: foo() missing 1 required keyword-only argument: 'c'
# foo(1, 2, 3, 4, name='alex', sex='男')   # TypeError: foo() missing 1 required keyword-only argument: 'c'
foo(1, 2, c=18)  # 1 2 18 None () {}
foo(2, 3, [1, 2, 3], c=13, hobby='喝茶')  # 2 3 13 None ([1, 2, 3],) {'hobby': '喝茶'}
foo(*[1, 2, 3, 4], **{'name': '太白', 'c': 12, 'sex': '女'})   # 1 2 12 女 (3, 4) {'name': '太白'}

三、名称空间及作用域

3.1、名称空间

在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空.
我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟里一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读如内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量回储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。

我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。

代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间

在函数的运行中开辟的临时的空间叫做局部命名空间也叫做临时名称空间

现在我们知道了,py文件中,存放变量与值的关系的一个空间叫做全局名称空间,而当执行一个函数时,内存中会临时开辟一个空间,临时存放函数中的变量与值的关系,这个叫做临时名称空间,或者局部名称空间。

其实python还有一个空间叫做内置名称空间:内置名称空间存放的就是一些内置函数等拿来即用的特殊的变量:input,print,list等等,


全局命名空间--> 我们直接在py文件中, 函数外声明的变量都属于全局命名空间
局部命名空间--> 在函数中声明的变量会放在局部命名空间
内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间

3.2、加载顺序

在启动python解释器之后,即使没有创建任何的变量或者函数,还是会有一些函数直接可以用的比如abs(-1),max(1,3)等等,在启动Python解释器的时候,就已经导入到内存当中供我们使用,所以肯定是先加载内置名称空间,然后就开始从文件的最上面向下一行一行执行,此时如果遇到了初始化变量,就会创建全局名称空间,将这些对应关系存放进去,然后遇到了函数执行时,在内存中临时开辟一个空间,加载函数中的一些变量等等。所以这三个空间的加载顺序为:内置命名空间(程序运行伊始加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载

3.3、取值顺序

取值顺序就是引用一个变量,先从哪一个空间开始引用

# 如果你在全局名称空间引用一个变量,先从全局名称空间引用,全局名# 称空间如果没有,才会向内置名称空间引用。
input = 666
print(input) # 666
# 如果你在局部名称空间引用一个变量,先从局部名称空间引用,
# 局部名称空间如果没有,才会向全局名称空间引用,全局名称空间在没有,就会向内置名称空间引用。
input = 666
print(input) # 666
input = 666
def func():
    input = 111
    print(input) # 111
func()

#-------------------------
name = '太白'
def func():
    name = 'alex'
    def inner():
        print(name)
    inner()

func()

空间的取值顺序与加载顺序是相反的,取值顺序满足的就近原则,从小范围到大范围一层一层的逐步引用

3.4、作用域

作用域就是作用范围, 按照生效范围来看分为全局作用域和局部作用域

  • 全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循 从上到下逐⾏执行).
  • 局部作用域: 在函数内部可以使用.

作⽤域命名空间:

  • 全局作用域: 全局命名空间 + 内置命名空间
  • 局部作⽤域: 局部命名空间

3.5、内置函数globals()与locals()

globals(): 以字典的形式返回全局作用域所有的变量对应关系。
locals(): 以字典的形式返回当前作用域的变量的对应关系。

"""
此文件研究的是内置函数 globals locals
"""
name = 'alex'
l1 = [1, 2, 3]


def func():
    age = '18'


print(globals())  # 全局作用域所有的内容
print(locals())  # 当前位置
'''
{'__name__': '__main__', '__doc__': '\n此文件研究的是内置函数 globals locals\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018AD6E15AC8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/python学习/笔记/笔记/前面部分笔记-后面笔记在视频内/day10/day10/05 内置函数:locals globals.py', '__cached__': None, 'name': 'alex', 'l1': [1, 2, 3], 'func': <function func at 0x0000018AD6F973A8>}
{'__name__': '__main__', '__doc__': '\n此文件研究的是内置函数 globals locals\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018AD6E15AC8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/python学习/笔记/笔记/前面部分笔记-后面笔记在视频内/day10/day10/05 内置函数:locals globals.py', '__cached__': None, 'name': 'alex', 'l1': [1, 2, 3], 'func': <function func at 0x0000018AD6F973A8>}
'''
# -----------------------------------------

name = 'alex'
l1 = [1, 2, 3]


def func():
    age = '18'
    oldboy = '老男孩教育'
    print(globals())  # 全局作用域所有的内容
    print(locals())  # 当前位置的变量与值的对应关系{'age': '18', 'oldboy': '老男孩教育'}


func()


# -----------------------------------------
name = 'alex'
l1 = [1, 2, 3]


def func():
    age = '18'
    oldboy = '老男孩教育'

    def inner():
        name_class = 'python23期'
        print(globals())  # 全局作用域所有的内容
        print(locals())  # 当前位置的变量与值的对应关系{'name_class': 'python23期'}

    inner()


func()

3.6、关键字global、nonlocal

3.6.1、global

a = 1
def func():
    print(a)
func()

a = 1
def func():
    a += 1  # 报错
func()

局部作用域对全局作用域的变量(此变量只能是不可变的数据类型)只能进行引用,而不能进行改变,只要改变就会报错,但是有些时候,我们程序中会遇到局部作用域去改变全局作用域的一些变量的需求,这怎么做呢?这就得用到关键字global
1)在局部作用域中可以更改全局作用域的变量

count = 1
def search():
    global count
    count = 2
    
search()
print(count)    # 2

2)利用global在局部作用域也可以声明一个全局变量

def func():
    global a
    a = 3
func()
print(a)    # 3

所以global关键字有两个作用:

1.声明一个全局变量
2.在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)

3.6.2、nonlocal

nonlocal是python3x新加的功能,与global用法差不多,就是在局部作用域如果想对父级作用域的变量进行改变时,需要用到nonlocal

在外层的函数中,修改局部空间的变量值.完全不涉及全局变量,只修改离它最近的一层,最近的一层没有变量继续向上找,直到找到最外层函数

def add_b():
    b = 42
    def do_global():
        b = 10
        print(b)    # 10
        def dd_nonlocal():
            nonlocal b
            b = b + 20
            print(b) # 30
        dd_nonlocal()
        print(b)    # 30
    do_global()
    print(b)    #42
add_b()

nonlocal的总结:

1.不能更改全局变量。
2.在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。

四、高阶函数

4.1、函数的嵌套

def func1():
    print('in func1')
    print(3)


def func2():
    print('in func2')
    print(4)


func1()
print(1)
func2()
print(2)

'''
in func1
3
1
in func2'
4
2

'''

# ---------------------------------------------

# 例2:
def func1():
    print('in func1')
    print(3)


def func2():
    print('in func2')
    func1()
    print(4)


print(1)
func2()
print(2)

'''
1
in func2
in func1
3
4
2
'''

# ---------------------------------------------
# # 例3:
def fun2():
    print(2)

    def func3():
        print(6)

    print(4)
    func3()
    print(8)


print(3)
fun2()
print(5)

'''
3 2 4 6 8 5
'''

4.2、函数的递归

满足如下两个条件:

# 1.自己调用自己 (不断调用自己本身) -- 死递归
# 2.有明确的终止条件

# 官方声明: 最大层次1000,实际测试 998/997
def age(n):
    if n == 4:
        return 18
    else:
        return age(n+1)-2
print(age(1)) # 12

实例二:

# 循环打印
l2 = [1, 3, 5, ['太白', '元宝', 34, [33, 55, [11, 33]]], [77, 88], 66]

def func(alist):
    for i in alist:
        if type(i) == list:
            func(i)  # func(['太白','元宝',34])
        else:
            # print(i)
            print(i, end='\t')


func(l2)    # 1 3 5 太白 元宝 34 33 55 11 33 77 88 66

4.3、匿名函数:lambda表达式

def func(x, y):
    return x + y
print(func(1, 2))

# 匿名函数构建
func2 = lambda x, y: x + y
print(func2(1, 2))


# 写匿名函数:接收一个可切片的数据,返回索引为 0与2的对应的元素(元组形式)。
func = lambda x: (x[0], x[2])
print(func('太白金星'))

# 写匿名函数:接收两个int参数,将较大的数据返回。
func1 = lambda x, y: x if x > y else y
print(func1(100, 2))

func2 = lambda: 3
print(func2())  # 3

# 匿名函数最常用的就是与内置函数结合使用。

4.4、闭包

4.4.1、闭包引入

# 第一版: 没有保证数据的安全
# l1 = []  # 全局变量
# def make_average(price):
#     l1.append(price)
#     total = sum(l1)
#     return total / len(l1)
#
# print(make_average(100000))
# print(make_average(110000))
# print(make_average(120000))
# l1.append(666)    # 安全隐患
# print(make_average(90000))


# ===================================================
# 第二版:每次执行l1是空的。
# def make_average(price):
#     l1 = []
#     l1.append(price)
#     total = sum(l1)
#     return total/len(l1)
# print(make_average(100000))
# print(make_average(110000))
# print(make_average(120000))


# ===================================================
# 第三版:为了保证数据的安全,闭包
def make_average():
    l1 = [] # 自由变量

    def average(price):
        l1.append(price)
        total = sum(l1)
        return total / len(l1)

    return average


avg = make_average()
print(avg)  # <function make_average.<locals>.average at 0x00000204A997E3A8>
print(avg(100000))
print(avg(110000))
print(avg(120000))
print(globals())

4.4.2、闭包定义

# 闭包定义
 闭包是嵌套在函数中的函数
 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。

# 闭包的作用:
 保存局部信息不被销毁,保证数据的安全性。

# 闭包的应用:
 可以保存一些非全局变量但是不易被销毁、改变的数据。
 装饰器

4.4.3、闭包的判断

# 例一:是闭包
def wrapper():
    a = 1
    def inner():
        print(a)
    return inner
ret = wrapper() # 1
print(ret())    #None,inner()没有返回值

# --------------------------------
# 例二:不是闭包,闭包必须是内层函数对外层函数的变量(非全局变量)的引用
a = 2
def wrapper():
    def inner():
        print(a)
    return inner
ret = wrapper()

# --------------------------------
# 例三:是闭包
def wrapper(a, b):
    name = 'alex'
    def inner():
        print(a)
        print(b)
        name = 'alex'
    return inner

a = 2
b = 3
ret = wrapper(a, b)

# 判断一个函数是不是闭包 == 闭包函数有没有自由变量
print(ret.__code__.co_freevars)  # ('a', 'b')

# 了解
print(ret.__code__.co_varnames)  # ('name',) 函数中的局部变量

4.4.4、补充

def make_averager():
    series = []

    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total / len(series)
    return averager

avg = make_averager()


# 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars)  # ('series',)

# 函数名.__code__.co_varnames 查看函数的局部变量
print(avg.__code__.co_varnames)  # ('new_value', 'total')

# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。
print(avg.__closure__)  # (<cell at 0x000001C1B0539A38: list object at 0x000001C1B0525208>,)

# cell_contents 自由变量具体的值
print(avg.__closure__[0].cell_contents)  # []

五、函数名应用

函数名的定义和变量的定义几乎一致,在变量的角度,函数名其实就是一个变量,具有变量的功能:可以赋值;但是作为函数名他也有特殊的功能就是加上()就会执行对应的函数,所以我们可以把函数名当做一个特殊的变量

小结:函数名是一个特殊的变量,他除了具有变量的功能,还有最主要一个特点就是加上() 就执行,其实他还有一个学名叫第一类对象。

# 函数名是一个特殊的变量。
def func():
    print(666)

print(func)  # func = <function func at 0x00000000003F1EA0>

# -------------------------------------------------------------
# 1,函数名指向的是函数的内存地址,加上()就执行这个函数。
func()

# -------------------------------------------------------------
# 2函数名是一个变量。
def func():
    print(666)

f1 = func
f2 = f1
f2()
f1()

# -------------------------------------------------------------
# 3. 函数名可以作为容器类类型的元素。
def func1():
    print('in func1')

def func2():
    print('in func2')

def func3():
    print('in func3')

l = [func1, func2, func3]
# print(l)    # [<function func1 at 0x0000021D474AEE58>, <function func2 at 0x0000021D476C6828>, <function func3 at 0x0000021D476C6E58>]
for i in l:
    i()

# -------------------------------------------------------------
# 4. 函数名可以作为函数的实参。

def func1():
    print('in func1')

def func2(argv):
    argv()
    print('in func2')

func2(func1)

# -------------------------------------------------------------
# 5. 函数名可以作为函数的返回值。

# b = 666
#
# def func1():
#     print('in func1')
#
# def func2(argv):
#     print('in func2')
#     return argv
# ret = func2(b)
# print(ret)

def func1():
    print('in func1')

def func2(argv):
    # argv = func1 : function 43543
    print('in func2')
    return argv
ret = func2(func1)  # func1 : function 43543
ret()

# -----------------
def wrapper():

    def inner():
        print(666)
    return inner
# ret = wrapper()
# ret()
wrapper()()

六、f-strings格式化输出

f-strings 是python3.6开始加入标准库的格式化输出新的写法,这个格式化输出比之前的%s 或者 format 效率高并且更加简化,非常的好用
1)简单举例

name = '运维人在路上'
age = 18
sex = '男'
msg = F'姓名:{name},性别:{age},年龄:{sex}'  # 大写字母也可以
# msg = f'姓名:{name},性别:{age},年龄:{sex}'
print(msg)
'''
输出结果:
姓名:运维人在路上,性别:18,年龄:男
'''

2)任意表达式

print(f'{3 * 21}')  # 63

name = 'barry'
print(f"全部大写:{name.upper()}")  # 全部大写:BARRY

# 字典也可以
teacher = {'name': '运维人在路上', 'age': 18}
msg = f"The teacher is {teacher['name']}, aged {teacher['age']}"
print(msg)  # The comedian is 运维人在路上, aged 18

# 列表也行
l1 = ['运维人在路上', 18]
msg = f'姓名:{l1[0]},年龄:{l1[1]}.'
print(msg)  # 姓名:运维人在路上,年龄:18.

3)插入表达式

# 可以用函数完成相应的功能,然后将返回值返回到字符串相应的位置
def sum_a_b(a, b):
    return a + b


a = 1
b = 2
print('求和的结果为' + f'{sum_a_b(a, b)}')

4)多行f

name = 'barry'
age = 18
ajd = 'handsome'

speaker = f'Hi {name}.' \
          f'You are {age} years old.' \
          f'You are a {ajd} guy!'
print(speaker)  # Hi barry.You are 18 years old.You are a handsome guy!

5)其他细节

print(f"{
   
   {73}}")  # {73}
print(f"{
   
   {
   
   {73}}}")  # {73}
print(f"{
   
   {
   
   {
   
   {73}}}}")  # {
   
   {73}}

# ! , : { } ;这些标点不能出现在{} 这里面。
# print(f'{;12}')  # 报错
# 所以使用lambda 表达式会出现一些问题。
# 解决方式:可将lambda嵌套在圆括号里面解决此问题。
x = 5
print(f'{(lambda x: x * 2)(x)}')  # 10

七、可迭代对象与迭代器

7.1、可迭代对象

1)定义
在python中,但凡内部含有__ iter__方法的对象,都是可迭代对象
list,dict,str,set,tuple -- 可迭代对象 使用灵活
2)查看对象内部方法

# 通过dir() 去判断一个对象具有什么方法
s1 = '运维人在路上'
print(dir(s1))
'''
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

'''

# 判断python中的一个对象是不是可迭代对象
s1 = '运维人在路上'
i = 100
print('__iter__' in dir(i))  # False
print('__iter__' in dir(s1))  # True

3)可迭代对象小结
从字面意思来说:可迭代对象就是一个可以重复取值的实实在在的东西。
从专业角度来说:但凡内部含有iter方法的对象,都是可迭代对象。
可迭代对象可以通过判断该对象是否有iter方法来判断。

可迭代对象的优点:

  • 可以直观的查看里面的数据。

可迭代对象的缺点:

  • 占用内存。
  • 可迭代对象不能迭代取值(除去索引,key以外)。

其实for循环在底层做了一个小小的转化,就是先将可迭代对象转化成迭代器,然后在进行取值的。

7.2、迭代器

1)定义
迭代器是这样的对象:实现了无参数的__ next__方法,返回序列中的下一个元素,如果没有元素了,那么抛出StopIteration异常。python中的迭代器还实现了__ iter__方法,因此迭代器也可以迭代
在python中,内部含有'Iter'方法并且含有'next'方法的对象就是迭代器
2)判断对象是否是迭代器

o1 = 'alex'
o2 = [1, 2, 3]
o3 = (1, 2, 3)
o4 = {'name': '太白', 'age': 18}
o5 = {1, 2, 3}
f = open('file', encoding='utf-8', mode='w')
print('__iter__' in dir(o1))  # True
print('__iter__' in dir(o2))  # True
print('__iter__' in dir(o3))  # True
print('__iter__' in dir(o4))  # True
print('__iter__' in dir(o5))  # True
print('__iter__' in dir(f))  # True

print('__next__' in dir(o1))  # False
print('__next__' in dir(o2))  # False
print('__next__' in dir(o3))  # False
print('__next__' in dir(o4))  # False
print('__next__' in dir(o5))  # False
print('__next__' in dir(f))  # True
f.close()
# 只有文件句柄是迭代器,剩下的那些数据类型都是可迭代对象

3)可迭代对象转换为迭代器

l1 = [1, 2, 3, 4, 5, 6]
obj = l1.__iter__()
print(obj)  # <list_iterator object at 0x000001DB19A59148>

# 或者
print(iter(l1))  # <list_iterator object at 0x00000216C9015FC8>

4)迭代器取值
可迭代对象是不可以一直迭代取值的(除去用索引,切片以及Key),但是转化成迭代器就可以了,迭代器是利用__ next__()进行取值

l1 = [1, 2, 3, ]
obj = l1.__iter__()  # 或者 iter(l1)
# print(obj)  # <list_iterator object at 0x000002057FE1A3C8>
ret = obj.__next__()
print(ret)
ret = obj.__next__()
print(ret)
ret = obj.__next__()
print(ret)
ret = obj.__next__()  # StopIteration
print(ret)
# 迭代器利用next取值:一个next取对应的一个值,如果迭代器里面的值取完了,还要next,那么就报StopIteration的错误。

5)while模拟for的内部循环机制
for循环的循环对象一定要是可迭代对象,但是这不意味着可迭代对象就可以取值,因为for循环的内部机制是:将可迭代对象转换成迭代器,然后利用next进行取值,最后利用异常处理处理StopIteration抛出的异常

l1 = [1, 2, 3, 4, 5, 6]
# 1 将可迭代对象转化成迭代器
obj = iter(l1)
# 2,利用while循环,next进行取值
while 1:
    # 3,利用异常处理终止循环
    try:
        print(next(obj))
    except StopIteration:
        break

6)迭代器小结
从字面意思来说:迭代器就是可以迭代取值的工具。
从专业角度来说:在python中,内部含有'Iter'方法并且含有'next'方法的对象就是迭代器。

迭代器的优点:

  • 节省内存:迭代器在内存中相当于只占一个数据的空间:因为每次取值都上一条数据会在内存释放,加载当前的此条数据。
  • 惰性机制:next一次,取一个值,绝不过多取值。
  • 有一个迭代器模式可以很好的解释上面这两条:迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式。

迭代器的缺点:

  • 不能直观的查看里面的数据。
  • 取值时不走回头路,只能一直向下取值。
l1 = [1, 2, 3, 4, 5, 6]
obj = iter(l1)

for i in range(2):
    print(next(obj))

for i in range(2):
    print(next(obj))

'''
1 2 3 4
'''

7.3、可迭代对象与迭代器对比

1)可迭代对象:
是一个私有的方法比较多,操作灵活(比如列表,字典的增删改查,字符串的常用操作方法等),比较直观,但是占用内存,而且不能直接通过循环迭代取值的这么一个数据集。

应用:当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。
2)迭代器:
是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。

应用:当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。(可参考为什么python把文件句柄设置成迭代器)

八、生成器

8.1、初识生成器

1)定义
生成器的本质就是迭代器
迭代器都是Python给你提供的已经写好的工具或者通过数据转化得来的,(比如文件句柄,iter([1,2,3])。
生成器是需要我们自己用python代码构建的工具

2)生成器构建方式
在python中有三种方式来创建生成器:
1.通过生成器函数
2.通过生成器推导式
3.python内置函数或者模块提供

8.2、生成器函数

1)普通函数与生成器函数

def func():
    print(11)
    return 22


ret = func()
print(ret)
'''
11
22
'''


# -------------------------
# 将函数中的return换成yield,这样func就不是函数了,而是一个生成器函数
def func():
    print(11)
    yield 22


ret = func()
print(ret)
'''
<generator object func at 0x000002101924B448>
'''

2)生成器取值

# 生成器的本质就是迭代器.迭代器如何取值,生成器就如何取值
def func():
    print(11)
    yield 22


gen = func()  # 获取生成器
ret = gen.__next__()  # 11 这个时候函数才会执⾏
print(ret)  # 22  并且yield会将func生产出来的数据 222 给了 ret

3)多个yield

# 生成器函数中可以写多个yield
# 当程序运行完最后一个yield,那么后面继续运行next()程序会报错,一个yield对应一个next,next超过yield数量,就会报错,与迭代器一样
def func():
    yield 2, 4, 5
    yield 3
    yield 4
    yield 5

gen = func()
print(gen.__next__())   # (2, 4, 5)
print(gen.__next__())   # 3
print(gen.__next__())   # 4
print(gen.__next__())   # 5
# print(gen.__next__())   # StopIteration

4)yield与return的区别
return:一般在函数中只设置一个,他的作用是终止函数,并且给函数的执行者返回值。
yield:在生成器函数中可设置多个,他并不会终止函数,next会获取对应yield生成的元素。

def eat_baozi():
    list1 = []
    for i in range(1, 2001):
        list1.append(f'{i}号包子')
    return list1


print(eat_baozi())
# ---------------------------------

def eat_baozi_gen():
    for i in range(1, 2001):
        yield f'{i}号包子'


ret = eat_baozi_gen()

for i in range(200):
    print(next(ret))

for i in range(200):
    print(next(ret))

8.3、send方法(了解)

# next只能获取yield生成的值,但是不能传递值。
def gen(name):
    print(f'{name} ready to eat')
    while 1:
        food = yield
        print(f'{name} start to eat {food}')


dog = gen('alex')
next(dog)
next(dog)
next(dog)
'''
alex ready to eat
alex start to eat None
alex start to eat None
'''
print("========================================")

# 而使用send这个方法是可以的。
def gen(name):
    print(f'{name} ready to eat')
    while 1:
        food = yield 222
        print(f'{name} start to eat {food}')


dog = gen('alex')
next(dog)  # 第一次必须用next让指针停留在第一个yield后面
# 与next一样,可以获取到yield的值
ret = dog.send('骨头')
print(ret)
'''
alex ready to eat
alex start to eat 骨头
222
'''

print("========================================")
def gen(name):
    print(f'{name} ready to eat')
    while 1:
        food = yield
        print(f'{name} start to eat {food}')


dog = gen('alex')
next(dog)
# 还可以给上一个yield发送值
dog.send('骨头')
dog.send('狗粮')
dog.send('香肠')
'''
alex ready to eat
alex start to eat 骨头
alex start to eat 狗粮
alex start to eat 香肠
'''

send和next()区别:
相同点:

  • send 和 next()都可以让生成器对应的yield向下执行一次。
  • 都可以获取到yield生成的值。

不同点:

  • 第一次获取yield值只能用next不能用send(可以用send(None))。
  • send可以给上一个yield置传递值。

8.4、yield from

python3中提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回

# 对比yield 与 yield from
def func():
    lst = ['卫龙', '老冰棍', '北冰洋', '牛羊配']
    yield lst


g = func()
print(g)    # <generator object func at 0x000002A560D2B448>
print(next(g))  # 只是返回一个列表['卫龙', '老冰棍', '北冰洋', '牛羊配']

def func():
    lst = ['卫龙', '老冰棍', '北冰洋', '牛羊配']
    yield from lst


g = func()
print(g)    # <generator object func at 0x000002A560D81848>
# 他会将这个可迭代对象(列表)的每个元素当成迭代器的每个结果进行返回。
print(next(g))  #卫龙
print(next(g))  #老冰棍
print(next(g))  #北冰洋
print(next(g))  #牛羊配
'''
yield from ['卫龙','老冰棍','北冰洋','牛羊配'] 
等同于:
    yield '卫龙'
    yield '老冰棍'
    yield '北冰洋'
    yield '牛羊配'
'''

yield from 是将列表中的每一个元素返回,所以 如果写两个yield from 并不会产生交替的效果

def func():
    lst1 = ['卫龙', '老冰棍', '北冰洋', '牛羊配']
    lst2 = ['馒头', '花卷', '豆包', '大饼']
    yield from lst1
    yield from lst2


g = func()
for i in g:
    print(i)

'''
卫龙
老冰棍
北冰洋
牛羊配
馒头
花卷
豆包
大饼
'''

九、各种表达式

9.1、列表推导式

1)初识列表推导式

lst = []
for i in range(10):
    lst.append(i)

print(lst)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 列表推导式
print([i for i in range(10)])   # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

2)列表推导式模式
列表推导式分为两种模式:

  • 循环模式:[变量(加工的变量) for 变量 in iterable]
  • 筛选模式: [变量(加工的变量) for 变量 in iterable if 条件]

循环模式:

# 将10以内所有整数的平方写入列表。
print([i**2 for i in range(1, 11)])

# 100以内所有的偶数写入列表.
print([i for i in range(2, 101, 2)])

# 从python1期到python100期写入列表list
print([f'python{i}期' for i in range(1, 101)])

筛选模式:

print([i for i in range(1, 101) if i > 49])

# 三十以内可以被三整除的数。
print([i for i in range(1, 31) if i % 3 == 0])

# 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
l1 = ['barry', 'fdsaf', 'alex', 'sb', 'ab']
print([i.upper() for i in l1 if len(i) > 3])

# 找到嵌套列表中名字含有两个‘e’的所有名字(有难度)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
l1 = []
for i in names:
    for j in i:
        if j.count('e') > 1:
            l1.append(j)
print(l1)

print([j for i in names for j in i if j.count('e') > 1])

9.2、生成器表达式

生成器表达式和列表推导式的语法上一模一样,只是把[]换成()就行了

gen = (i ** 2 for i in range(10))
print(gen)  # <generator object <genexpr> at 0x00000182047EB448>

# 获取1-100内能被3整除的数
gen = (i for i in range(1, 100) if i % 3 == 0)
for num in gen:
    print(num)

如何触发生成器(迭代器)取值?

# 1. next(obj)
# 2. for 循环
for i in obj:
    print(i)
# 3. 数据转化
print(list(obj))

生成器表达式和列表推导式的区别:

  • 列表推导式比较耗内存,所有数据一次性加载到内存。而.生成器表达式遵循迭代器协议,逐个产生元素。
  • 得到的值不一样,列表推导式得到的是一个列表.生成器表达式获取的是一个生成器
  • 列表推导式一目了然,生成器表达式只是一个内存地址。

9.3、字典推导式

l1 = ['小潘', '怼怼哥', '西门大官人', '小泽ml亚']
dic = {}
for index in range(len(l1)):
    dic[index] = l1[index]
print(dic)  # {0: '小潘', 1: '怼怼哥', 2: '西门大官人', 3: '小泽ml亚'}
print({i: l1[i] for i in range(len(l1))})

lst1 = ['jay', 'jj', 'meet']
lst2 = ['周杰伦', '林俊杰', '郭宝元']
dic = {lst1[i]: lst2[i] for i in range(len(lst1))}
print(dic)  # {'jay': '周杰伦', 'jj': '林俊杰', 'meet': '郭宝元'}

9.4、集合推导式

集合推导式可以帮我们直接生成一个集合,集合的特点;无序,不重复 所以集合推导式自带去重功能

lst = [1, 2, 3, -1, -3, -7, 9]
s = {abs(i) for i in lst}
print(s)    # {1, 2, 3, 7, 9}

十、内置函数

python给我们提供的内置函数一共是68个,底层用C语言

# 一带而过
all()  any()  bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int()  iter() locals() next()  oct()  ord()  pow()    repr()  round()

# 重点讲解
abs() enumerate() filter()  map() max()  min() open()  range() print()  len()  list()  dict() str()  float() reversed()  set()  sorted()  sum()    tuple()  type()  zip()  dir() 

# 面向对象
classmethod()  delattr() getattr() hasattr()  issubclass()  isinstance()  object() property()  setattr()  staticmethod()  super()

10.1、一带而过

10.1.1、eval()

执行字符串类型的代码,并返回最终结果

# eval 剥去字符串的外衣,返回里面的本质
s1 = "{1: '运维人在路上'}"
s2 = '1 + 3'
ret = eval(s1)
print(ret, type(ret))  # {1: '运维人在路上'} <class 'dict'>
print(eval(s2))  # 4

print(eval('2 + 2'))  # 4
n = 81
print(eval("n + 4"))  # 85
eval('print(666)')  # 666

10.1.2、exec()

执行字符串类型的代码

s3 = '''
for i in range(3):
    print(i)
'''
exec(s3)    # 0 1 2

10.1.3、hash()

获取一个对象(可哈希对象:int,str,Bool,tuple)的哈希值。

print(hash(12322))  # 12322
print(hash('123'))  # 6204408908308768149
print(hash('arg'))  # 3826562643085661746
print(hash('alex')) # -1624301239551408769
print(hash(True))   # 1
print(hash(False))  # 0
print(hash((1, 2, 3)))  # 2528502973977326415

10.1.4、help()

用于查看函数或模块用途的详细说明

print(help(list))
print(help(str.split))

10.1.5、callable()

函数用于检查一个对象是否是可调用的。如果返回True,object仍然可能调用失败;但如果返回False,调用对象ojbect绝对不会成功

name = 'alex'
def func():
    pass
print(callable(name))  # False
print(callable(func))  # True

10.1.6、int()

函数用于将一个字符串或数字转换为整型

print(int())  # 0
print(int('12'))  # 12
print(int(3.6))  # 3
print(int('0100', base=2))  # 将2进制的 0100 转化成十进制。结果为 4

10.1.7、float()

函数用于将整数和字符串转换成浮点数。

print(float(100))   # 100.0

10.1.8、complex()

函数用于创建一个值为 a+ b* j 的复数或者转化一个字符串或数为复数。如果第一个参数为字符串,则不需要指定第二个参数

print(complex(1, 2))  # (1+2j)

10.1.9、bin(),oct(),hex()

print(bin(100))     # 0b1100100 将十进制转化成二进制。 **
print(oct(10))      # 0o12 将十进制转化成八进制字符串并返回。
print(hex(17))      # 0x11 将十进制转化成十六进制字符串并返回。 **
print(hex(15))      # 0xf

print(bin(10),type(bin(10)))  # 0b1010 <class 'str'>
print(oct(10), type(oct(10)))  # 0o12 <class 'str'>
print(hex(10), type(hex(10)))  # 0xa <class 'str'>

10.1.10、divmod()

计算除数与被除数的结果,返回一个包含商和余数的元组(a // b, a % b)

print(divmod(10, 3))  # (3, 1)

10.1.11、round()

print(round(3.1485926, 2))  # 3.15
print(round(4.245)) # 4

10.1.12、pow()

求xy次幂。(三个参数为xy的结果对z取余)

print(pow(3, 3))  # 3*3*3
print(pow(3, 3, 2)) # 1

10.1.13、bytes()

用于不同编码之间的转化

s = '你好'
bs = s.encode('utf-8')
print(bs)  # b'\xe4\xbd\xa0\xe5\xa5\xbd'
s1 = bs.decode('utf-8')
print(s1)  # 你好

bs = bytes(s, encoding='utf-8')
print(bs)   # b'\xe4\xbd\xa0\xe5\xa5\xbd'
b = '你好'.encode('gbk')
b1 = b.decode('gbk')
print(b1.encode('utf-8'))   # b'\xe4\xbd\xa0\xe5\xa5\xbd'

10.1.14、ord(),chr()

# 输入字符寻找其在unicode的位置。
print(ord('a')) # 97
print(ord('中')) # 20013

# 输入位置数字找出其对应的字符
print(chr(98))  # **
print(chr(20104))  # 予

10.1.15、repr()

# %r  原封不动的写出来
name = 'taibai'
print('我叫%r' % name)    # 我叫'taibai'

# repr 原形毕露
print(repr('{"name":"alex"}'))  # '{"name":"alex"}'
print('{"name":"alex"}')    # {"name":"alex"}

10.1.16、all(),any()

# all  可迭代对象中,全都是True才是True
# any  可迭代对象中,有一个True 就是True
# 0,'',[],{},set(),(),None  ==> False

l1 = [1, 'fgdsa', [], {1: 2}]
l2 = [0, '', [], {}]
print(all(l1))  # False
print(any(l2))  # False
print(all([1, 2, True, 0])) # False
print(any([1, '', 0]))  # True

10.1.17、globals(),locals()

name = 1
def func():
    a = 123
    # print(locals())
    # print(globals())

func()

print(globals())  # 全局空间中的变量
print(locals())  # 查看当前空间的变量

10.1.18、format()


print(format(13,">20"))  # 右对齐
print(format(13,"<20"))  # 左对齐
print(format(13,"^20"))  # 居中

print(format(13,"08b"))    # 2
print(format(13,"08d"))    # 10
print(format(13,"08o"))    # 8
print(format(12,"08x"))    # 16
'''
                  13
13                  
         13         
00001101
00000013
00000015
0000000c
'''

10.2、重点掌握

10.2.1、print()

''' 源码分析
def print(self, *args, sep=' ', end='\n', file=None): # known special case of print
    """
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    file:  默认是输出到屏幕,如果设置为文件句柄,输出到文件
    sep:   打印多个值之间的分隔符,默认为空格
    end:   每一次打印的结尾,默认为换行符
    flush: 立即把内容输出到流文件,不作缓存
    """
'''

print(111, 222, 333, sep='*')  # 111*222*333
print(111, end='')
print(222)  # 两行的结果 111222
f = open('log', 'w', encoding='utf-8')
print('写入文件', file=f, flush=True)

10.2.2、list()

将一个可迭代对象转换成列表

l1 = list('fdsafd')
print(l1)   # ['f', 'd', 's', 'a', 'f', 'd']

10.2.3、tuple()

将一个可迭代对象转换成元组

tu1 = tuple('abcd')
print(tu1)  # ('a', 'b', 'c', 'd')

10.2.4、dict()

# 创建字典的几种方式
# 方式一
dic = {1: 2}

# 方式二:字典推导式
print({i: 1 for i in range(3)})

# 方式三
dict()

# 方式四
dic = dict(one=1, two=2, three=3)
print(dic)  # {'one': 1, 'two': 2, 'three': 3}

# 方式五
# fromkeys()
seq = ('Google', 'Runoob', 'Taobao')
dict = dict.fromkeys(seq)
print("新字典为 : %s" % str(dict))  # 新字典为 : {'Google': None, 'Runoob': None, 'Taobao': None}
dict = dict.fromkeys(seq, 10)
print("新字典为 : %s" % str(dict))  # 新字典为 : {'Google': 10, 'Runoob': 10, 'Taobao': 10}

10.2.5、abs()

返回绝对值

print(abs(-10)) # 10

10.2.6、sum()

print(sum([1, 2, 3, 4]))    # 10
print(sum([1, 2, 3, 4], 100))   # 110

10.2.7、min(),max()

print(min([1, 2, 3]))  # 1 返回此序列最小值
print(min([1, 2, -5, ], key=abs))  # 1 按照绝对值的大小,返回此序列最小值

# 加key是可以加函数名,min自动会获取传入函数中的参数的每个元素,然后通过你设定的返回值比较大小,返回最小的传入的那个参数。
print(min(1, 2, -5, 6, -3, key=lambda x: abs(x)))  # 1 可以设置很多参数比较大小

# x为dic的key,lambda的返回值(即dic的值进行比较)返回最小的值对应的键
dic = {'a': 3, 'b': 2, 'c': 1}
print(min(dic, key=lambda x: dic[x]))  # c


# ------------------------------------------------------------
l1 = [('alex', 73, 170), ('太白', 18, 185), ('武大', 35, 159), ]
# 将遍历的那个元素即是 ('太白', 18) 返回
def func(x):  # x = ('alex', 73)
    return x[1]

print(min(l1, key=func))  # ('太白', 18, 185)
print(min(l1, key=lambda x: x[1]))  # ('太白', 18, 185)

# ----------------------------------------------------------
# 练习:
dic = {'a': 3, 'b': 2, 'c': 1}
# 将dic值最小的键返回。
print(min(dic, key=lambda x: dic[x]))   # c

# 将dic值最小的值返回。
print(dic[min(dic, key=lambda x: dic[x])])  # 1

dic = {'A': ['李业', 67], 'b': ['怼哥', 95], 'c': ['冯垚', 85]}

# 将成绩最低的从属于的那个列表返回。
print(dic[min(dic, key=lambda x: dic[x][1])])   # ['李业', 67]
# 将成绩最低的分数返回。
print(dic[min(dic, key=lambda x: dic[x][1])][1])    # 67

# -------------------------------------------------------
list = [
    {'name': 'alex', 'age': 73},
    {'name': 'wusir', 'age': 35},
    {'name': '太白', 'age': 25},
]
# 将年龄最小的 字典返回。
print(min(list, key=lambda x: x['age']))  # {'name': '太白', 'age': 25}
# 将年龄最小的名字返回。
print(min(list, key=lambda x: x['age'])['name'])
# 将年龄最小的年龄返回。
print(min(list, key=lambda x: x['age'])['age'])

10.2.8、reversed()

将一个序列翻转, 返回翻转序列的迭代器


l = reversed('你好')  # l 获取到的是一个生成器
print(l)    # <reversed object at 0x0000019FF1F81948>
print(list(l))  # 转换为list ['好', '你']
ret = reversed([1, 4, 3, 7, 9])
print(list(ret))  # [9, 7, 3, 4, 1]

s1 = 'alex' 
print(reversed(s1)) # <reversed object at 0x0000025D7F3B01C8>
for i in reversed(s1):
    print(i)    # x e l a

10.2.9、zip()

函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元祖组成的内容,如果各个迭代器的元素个数不一致,则按照长度最短的返回

# zip 拉链方法 返回一个迭代器
l1 = [1, 2, 3, 4]
tu = ('a', 'b', 'c')
s = 'python'
print(zip(l1, tu, s))  # <zip object at 0x000001F58882A2C8>
print(list(zip(l1, tu, s)))  # [(1, 'a', 'p'), (2, 'b', 'y'), (3, 'c', 't')]

10.2.10、sorted()

l1 = [2, 6, 4, 1, 3]
# 列表原有的方法
l1.sort()
print(l1)
# 形成新列表
print(sorted(l1))
print(l1)

# ------------------------------------
lst = [1, 3, 2, 5, 4]
lst2 = sorted(lst)
print(lst)  # [1, 3, 2, 5, 4]   原列表不会改变
print(lst2)  # [1, 2, 3, 4, 5]  返回的新列表是经过排序的

lst3 = sorted(lst, reverse=True)
print(lst3)  # [5, 4, 3, 2, 1] 倒叙

# 字典使用sorted排序
dic = {1: 'a', 3: 'c', 2: 'b'}
print(sorted(dic))  # [1, 2, 3] 字典排序返回的就是排序后的key

# 和函数组合使用
# 定义一个列表,然后根据一元素的长度排序
lst = ['天龙八部', '西游记', '红楼梦', '三国演义']

# 计算字符串的长度
def func(s):
    return len(s)

print(sorted(lst, key=func))    # ['西游记', '红楼梦', '天龙八部', '三国演义']

# 和lambda组合使用
lst = ['天龙八部', '西游记', '红楼梦', '三国演义']
print(sorted(lst, key=lambda s: len(s)))    # ['西游记', '红楼梦', '天龙八部', '三国演义']

lst = [{'id': 1, 'name': 'alex', 'age': 18},
       {'id': 2, 'name': 'wusir', 'age': 17},
       {'id': 3, 'name': 'taibai', 'age': 16}, ]

# 按照年龄对学生信息进行排序
print(sorted(lst, key=lambda e: e['age']))  # [{'id': 3, 'name': 'taibai', 'age': 16}, {'id': 2, 'name': 'wusir', 'age': 17}, {'id': 1, 'name': 'alex', 'age': 18}]

10.2.11、filter()

# filter  返回一个生成器
# 生成器表达式的筛选模式
l1 = [56, 67, 12, 34, 78, 90, ]
print([i for i in l1 if i > 60])    # [67, 78, 90]

# 返回值为True的留下来
print(filter(lambda x: x > 60, l1)) # <filter object at 0x000001B6EF11A7C8>
print(list(filter(lambda x: x > 60, l1)))   # [67, 78, 90]

lst = [{'id': 1, 'name': 'alex', 'age': 50},
       {'id': 1, 'name': 'wusir', 'age': 17},
       {'id': 1, 'name': 'taibai', 'age': 16}, ]

print(list(filter(lambda x: x['age'] < 30, lst)))   # [{'id': 1, 'name': 'wusir', 'age': 17}, {'id': 1, 'name': 'taibai', 'age': 16}]

10.2.12、map()

# 计算列表中每个元素的平方,返回新列表
lst = [1, 2, 3, 4, 5]

def func(s):
    return s * s

mp = map(func, lst)
print(mp)   # <map object at 0x000002055CD45CC8>
print(list(mp)) # [1, 4, 9, 16, 25]

# 改写成lambda
lst = [1, 2, 3, 4, 5]
print(list(map(lambda s: s * s, lst)))  # [1, 4, 9, 16, 25]

# 计算两个列表中相同位置的数据的和
lst1 = [1, 2, 3, 4, 5]
lst2 = [2, 4, 6, 8, 10]
print(list(map(lambda x, y: x+y, lst1, lst2)))  # [3, 6, 9, 12, 15]

10.2.13、reduce()

# reduce python3x 从内置函数剔除了。
from functools import reduce

'''
第一次:x,y 1,2 求和 3 记录到内存
第二次:x,y 3,3 求和 6 记录到内存
第三次:x,y 6,4 .........
'''
print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]))  # 15
print(reduce(lambda x, y: 2 * x + y, [1, 2, 3]))    # 11 
print(reduce(lambda x, y: x + y, ['alex', 's', 'b']))  # alexsb 可以用字符串拼接

10.2.14、enumerate()

枚举 ("可迭代对象","序号的起始值") # 默认的起始值是0

lst = [(0,1), (1, 2), (2, 3)]
print([i for i in enumerate(lst, 10)])  # [(10, (0, 1)), (11, (1, 2)), (12, (2, 3))]

lst = [11, 22, 33, -44, 23, 21]
new_lst = []
for i in enumerate(lst):
    new_lst.append(i)
print(new_lst)  # [(0, 11), (1, 22), (2, 33), (3, -44), (4, 23), (5, 21)]
print([i for i in enumerate(lst, 1000)])    # [(1000, 11), (1001, 22), (1002, 33), (1003, -44), (1004, 23), (1005, 21)]

10.2.15、dir()

print(dir(list))  # 查看当前函数的方法

10.3、面向对象函数

十一、装饰器

11.1、开放封闭原则

扩展开放,修改封闭

11.2、装饰器演变

测试代码的执行效率
1)版本一:
测试单个程序的执行效率

import time

def index():
    time.sleep(2)
    print('欢迎访问博客园首页')


start_time = time.time()
index()
end_time = time.time()
print(f'此函数的执行效率{end_time - start_time}')

2)版本二
测试多个程序的执行效率呢?==>重复代码太多

def func1():
    time.sleep(2)
    print('欢迎访问日记首页')


def func2():
    time.sleep(1)
    print('欢迎访问评论首页')


start_time = time.time()
func1()
end_time = time.time()
print(f'此函数的执行效率{end_time - start_time}')

start_time = time.time()
func2()
end_time = time.time()
print(f'此函数的执行效率{end_time - start_time}')

3)版本三
整合到函数中

def func1():
    time.sleep(2)
    print('欢迎访问日记首页')


def func2():
    time.sleep(1)
    print('欢迎访问评论首页')


def test_time(x):
    start_time = time.time()
    x()
    end_time = time.time()
    print(f'此函数的执行效率{end_time - start_time}')


test_time(func1)
test_time(func2)

4)版本四
违反了封闭原则:改变了函数的调用方式。原先的调用方式是index(),现在是test_time(index)

def index():
    time.sleep(2)
    print('欢迎访问博客园首页')


def test_time(x):
    start_time = time.time()
    x()
    end_time = time.time()
    print(f'此函数的执行效率{end_time - start_time}')


test_time(index)

5)版本五
不改变原来函数的调用方式,使用闭包

def index():
    time.sleep(2)
    print('欢迎访问博客园首页')

#原先的调用方式
# index()

def func1():
    time.sleep(2)
    print('欢迎访问日记首页')

def test_time(x):  # x = index
    def inner():
        start_time = time.time()
        x()
        end_time = time.time()
        print(f'此函数的执行效率{end_time - start_time}')

    return inner


index = test_time(index)
index()

# -----------------------------------------------------------------

# 语法糖 @加上装饰器函数的名
def test_time(x):  # x = index
    def inner():
        start_time = time.time()
        x()
        end_time = time.time()
        print(f'此函数的执行效率{end_time - start_time}')

    return inner


@test_time  # index = test_time(index)
def index():
    time.sleep(2)
    print('欢迎访问博客园首页')


# index = test_time(index)
index()

6)版本六:被装饰函数有返回值

def test_time(x):  # x = index
    def inner():
        start_time = time.time()
        ret = x()
        end_time = time.time()
        print(f'此函数的执行效率{end_time - start_time}')
        return ret

    return inner


@test_time  # index = test_time(index)
def index():
    time.sleep(0.5)
    print('欢迎访问博客园首页')
    return True


# print(index())  # inner()
index()

7)版本7:被装饰函数带参数

def test_time(x):  # x = index
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = x(*args, **kwargs)
        end_time = time.time()
        print(f'此函数的执行效率{end_time - start_time}')
        return ret

    return inner


@test_time  # index = test_time(index)
def index(n):
    time.sleep(0.5)
    print(f'欢迎{n}访问博客园首页')
    return True

@test_time  # index = test_time(index)
def func2(a, b):
    time.sleep(0.5)
    print(f'最终结果:{a + b}')
    return a + b


print(index('苹果'))  # inner('苹果')
print(func2(1, 3))  # inner(1,3)
'''
欢迎苹果访问博客园首页
此函数的执行效率0.5003409385681152
True
最终结果:4
此函数的执行效率0.5003702640533447
4
'''

8)版本八:标准装饰器

def warpper(f):
    def inner(*args, **kwargs):
        print("被装饰函数之前的操作....")
        ret = f(*args, **kwargs)
        print("被装饰函数之后的操作....")
        return ret

    return inner


@warpper
def func():
    print(111)


func()

11.3、装饰器应用

装饰器的应用:在不改变原函数的源码以及调用方式前提下,为其增加额外的功能,如登录认证,打印日志等

11.4、带参装饰器

# 在装饰器外再套一层
def wrapper_out(n):
    def wrapper(f):
        def inner(*args, **kwargs):
            # if n == 'qq':
            #     username = input('请输入用户名:').strip()
            #     password = input('请输入密码:').strip()
            #     with open('qq',encoding='utf-8') as f1:
            #         for line in f1:
            #             user,pwd = line.strip().split('|')
            #             if username == user and password == pwd:
            #                 print('登陆成功')
            #                 ret = f(*args,**kwargs)
            #                 return ret
            #         return False
            # elif n == 'tiktok':
            #     username = input('请输入用户名:').strip()
            #     password = input('请输入密码:').strip()
            #     with open('tiktok', encoding='utf-8') as f1:
            #         for line in f1:
            #             user, pwd = line.strip().split('|')
            #             if username == user and password == pwd:
            #                 print('登陆成功')
            #                 ret = f(*args, **kwargs)
            #                 return ret
            #         return False
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            with open(n, encoding='utf-8') as f1:
                for line in f1:
                    user, pwd = line.strip().split('|')
                    if username == user and password == pwd:
                        print('登陆成功')
                        ret = f(*args, **kwargs)
                        return ret
                return False

        return inner

    return wrapper



@wrapper_out('qq')
def qq():
    print('成功访问qq')


@wrapper_out('tiktok')
def tiktok():
    print('成功访问抖音')


qq()
tiktok()

实例:

def wrapper_out(n):
    def wrapper(f):
        def inner(*args, **kwargs):
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            with open(n, encoding='utf-8') as f1:
                for line in f1:
                    user, pwd = line.strip().split('|')
                    if username == user and password == pwd:
                        print('登陆成功')
                        ret = f(*args, **kwargs)
                        return ret
                return False

        return inner

    return wrapper


@wrapper_out('JD')
def jd():
    print('欢迎访问京东首页')


@wrapper_out('JD')
def jd_shop():
    print('欢迎访问京东超市')


@wrapper_out('taobao')
def taobao():
    print('欢迎访问淘宝首页')


@wrapper_out('taobao')
def taobao_shop():
    print('欢迎访问淘宝超市')


def _quit():    # 与内置函数quit区别
    pass


choice_dict = {
    1: jd,
    2: jd_shop,
    3: taobao,
    4: taobao_shop,
    5: _quit
}

while 1:
    print('''
        1,京东首页
     2,京东超市
     3,淘宝首页
     4,淘宝超市
     5,退出程序''')
    num = input('请输入:').strip()
    num = int(num)
    choice_dict[num]()

11.5、多个装饰器

def wrapper1(func1):  # func1 = f原函数
    def inner1():
        print('wrapper1 ,before func')  # 2
        func1()
        print('wrapper1 ,after func')  # 4

    return inner1


def wrapper2(func2):  # func2 == inner1
    def inner2():
        print('wrapper2 ,before func')  # 1
        func2()  # inner1
        print('wrapper2 ,after func')  # 5

    return inner2


@wrapper2  # f = wrapper2(f) 里面的f == inner1  外面的f == inner2
@wrapper1  # f = wrapper1(f) 里面的f == func1  外面的 f == inner1
def f():
    print('in f')  # 3


f()  # inner2()

'''
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
'''

猜你喜欢

转载自blog.csdn.net/pythonxuexi123/article/details/114761982