Python基础-函数介绍

第1章 函数介绍

1.1 函数相关知识介绍

1.1.1 函数的作用

  1. 可读性强
  2. 复用性强

1.1.2 函数的用法

  1. 先定义再调用
  2. 只定义不调用就一定不执行

1.1.3 函数名

  1. 函数名就是一个变量,保存了函数所在的内存地址
  2. 函数名可以赋值
  3. 函数名可以作为容器类型的元素
  4. 函数名可以作为函数的返回值
  5. 函数名可以作为函数的参数

Tips:函数名()==函数的内存地址()

【示例】:

def func():
    print(123)

func()              # 函数名就是内存地址
func2 = func        # 函数名可以赋值
func2()

l = [func,func2]    # 函数名可以作为容器类型的元素
print(l)
for i in l:
    i()

def func3(f):
    f()
    return f        # 函数名可以作为函数的返回值

func4 = func3(func) # 函数名可以作为函数的参数
func4()

【运行结果】:

123
123
[<function func at 0x03507930>, <function func at 0x03507930>]
123
123
123
123

1.2 函数的注释

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

1.3 定义一个函数

【功能】:

定义了之后,可以在任何需要它的地方调用。

【示例】:

s = '函数长度测试字符串'
length1 = len(s)
print(length1)

def my_len1():
    i = 0
    for k in s:
        i += 1
    print(i)

my_len1()

【运行结果】:

9
9

1.4 函数的返回值

1.4.1 没有返回值(返回None)

【功能】:

  1. 不写return
  2. 只写return:结束一个函数的继续
  3. return None(不常用)

【示例】:

def func1():
    l = ['leon', 'shadow']
    for i in l:
        print(i)
        if i == 'leon':
            return None     # 直接结束函数调用
    print('='*20)

ret = func1()
print(ret)

【运行结果】:

leon
None

1.4.2 返回一个值

【功能】:

  1. 可以返回任何数据类型
  2. 只要返回就可以接收到
  3. 如果在一个程序中有多个return,那么只执行第一个

【示例】:

def func2():
    return {'k': 'v'}
print(func2())

【运行结果】:

{'k': 'v'}

1.4.3 返回多个值

【功能】:

  1. 用多个变量接收:有多少返回值就用多少变量接收
  2. 用一个变量接收: 得到的是一个元组

【示例】:

def func3():
    return 1,2,3
print(func3())

【运行结果】:

(1, 2, 3)

1.5 函数的参数

1.5.1 形式参数和实际参数

  • 形式参数:形参,定义函数时,()中要传递的参数
  • 实际参数:实参,使用函数时,()中实际传递的参数

【示例】:

def my_len3(s):
    i = 0
    for k in s:
        i += 1
    print(i)

s1 = '函数长度测试字符串01'
my_len3(s1)
my_len3('abcdefg')
my_len3([1,2,3,4,5])

【运行结果】:

11
7
5

1.5.2 参数个数

  • 没有参数:定义函数和调用函数时括号里都不写内容
  • 有一个参数:传什么就是什么
  • 有多个参数:位置参数

【示例】:

def my_sum(a, b):
    print(a, b)
    c = a + b
    return c

print(my_sum(1,2))
print(my_sum(2,1))

【运行结果】:

1 2
3
2 1
3

1.5.3 传参的方式

  • 站在实参的角度上:
  1. 按照位置传参
  2. 按照关键字传参
  3. 混合使用:但是必须先按照位置传参,再按照关键字传参数,并且不能给同一个变量传多个值
  • 站在形参的角度上:
  1. 位置参数:必须传,且有几个参数就传几个值
  2. 默认参数: 可以不传,如果不传就是用默认的参数,如果传了就用传的参数

【示例】:

def classmate(name,sex='男'):
    print('%s : %s' % (name, sex))

classmate('leon')
classmate('shadow')
classmate('girl', '女')
classmate('girl', sex='女')
classmate(name='leon', sex='男')

【运行结果】:

leon : 男
shadow : 男
girl : 女
girl : 女
leon : 男
  • 动态参数的另一种传参方式
  1. 站在形参的角度上,给变量加上*,就是组合所有传来的值。
  2. 站在实参的角度上,给一个序列加上*,就是将这个序列按照顺序分开

【示例】:

def func1(*args):       # 形参的角度
    print(args)

l = [1, 2, 3, 4, 5]
func1(*l)               # 实参的角度

def func2(**kwargs):
    print(kwargs)

d = {'a':1, 'b':2}
func2(**d)

【运行结果】:

(1, 2, 3, 4, 5)
{'a': 1, 'b': 2}

1.5.4 参数的类型

  • 位置参数:直接定义参数
  • 默认参数,关键字参数:参数名 = ‘默认的值’
  • 动态参数:可以接受任意多个参数
  1. 参数名之前加*,习惯参数名args,接收的是按照位置传参的值,组织成一个元组
  2. 参数名之前加**,习惯参数名kwargs,接收的是按照关键字传参的值,组织成一个字典
  3. args必须在kwargs之前

【使用顺序】:位置参数,*args,默认参数,**kwargs

【示例】:

def my_sum(*args):
    n = 0
    for i in args:
        n += i
    return n

print(my_sum(1,2,3,4,5))
print(my_sum(1,2,3))

【运行结果】:

15
6

【示例】:

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

func(a = 1,b = 2,c =3)
func(a = 1,b = 2)
func(a = 1)

【运行结果】:

{'a': 1, 'b': 2, 'c': 3}
{'a': 1, 'b': 2}
{'a': 1}

【示例】:

def func(*args,default = 1,**kwargs):
    print(args,kwargs,default)
func(1,2,3,4,5,default=2,a = 'aaaa',b = 'bbbb',)

【运行结果】:

(1, 2, 3, 4, 5) {'a': 'aaaa', 'b': 'bbbb'} 2

1.5.5 默认参数的陷阱

如果默认参数的值是一个可变数据类型,那么每一次调用函数的时候,如果不传值就公用这个数据类型的资源。

【示例】:

def xianjing(l = []):
    l.append(1)
    print(l)

xianjing()
xianjing()
xianjing()

【运行结果】:

[1]
[1, 1]
[1, 1, 1]

【示例】:

def xianjing(k,l = {}):
    #l.append(1)
    l[k] = 'v'
    print(l)

xianjing(1)
xianjing(2)
xianjing(3)

【运行结果】:

{1: 'v'}
{1: 'v', 2: 'v'}
{1: 'v', 2: 'v', 3: 'v'}

1.6 函数的命名空间

1.6.1 命名空间的分类

  • 内置命名空间:python解释器

python解释器一启动就可以使用的名字存储在内置命名空间中,内置的名字在启动解释器的时候被加载进内存里。

  • 全局命名空间:我们写的代码但不是函数中的代码

是在程序从上到下被执行的过程中依次加载进内存的,放置了我们设置的所有变量名和函数名。

  • 局部命名空间:函数

就是函数内部定义的名字,当调用函数的时候才会产生这个名称空间 随着函数执行的结束 这个命名空间就又消失了

1.6.2 命名空间内部关系

  • 在内置:不能使用局部和全局的名字的
  • 在全局:可以使用内置命名空间中的名字,但是不能在局部中使用
  • 在局部:可以使用全局、内置命名空间中的名字

1.6.3 命名空间的调用顺序:

  1. 在正常情况下,如果程序中使用了内置命名空间的函数则直接使用内置的名字;
  2. 当我们在全局定义了和内置命名空间中同名的名字时,会使用全局的名字;
  • 总结:
  1. 当我们程序中的函数有的时候,程序就不到上级空间进行寻找;
  2. 如果我们没有定义,就到上一级空间寻找,上一级没有再到上一级,如果内置的名字空间都没有,就报错;
  3. 多个函数应该拥有多个独立的局部名字空间,不互相共享。

1.6.4 命名空间的加载顺序

  1. 内置命名空间(程序运行前加载)
  2. 全局命名空间(程序运行中:从上到下加载)
  3. 局部命名空间(程序运行中:调用时才加载)

1.7 函数的作用域

1.7.1 作用域的分类

  • 全局作用域:作用在全局,内置和全局名字空间中的名字都属于全局作用域(globals())
  • 局部作用域:作用在局部,函数(局部名字空间中的名字属于局部作用域)(locals())

对于不可变数据类型,在局部作用域中可以查看全局作用域中的变量,但是不能直接修改,如果想要修改,需要在程序的一开始添加global声明;如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效。

【示例】:

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

func()
print(a)

【运行结果】:

2
2

【示例】:

a = 1
b = 2
def func():
    x = 'aaa'
    y = 'bbb'
    print(locals())         # globals:永远打印全局的名字
    print(globals())        # locals:输出什么是根据locals所在的位置

func()
print(locals())
print(globals())

【运行结果】:

{'y': 'bbb', 'x': 'aaa'}
{'__name__': '__main__', '__doc__': '\n----------------------------------------------------------\n# File Name: 作用域\n# Author: Leon\n# Date: 2018/8/28 0028 19:10\n# Description:\n----------------------------------------------------------\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x035BB570>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/WorkSpace/Python/python_study/week07/作用域.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x036579C0>}
{'__name__': '__main__', '__doc__': '\n----------------------------------------------------------\n# File Name: 作用域\n# Author: Leon\n# Date: 2018/8/28 0028 19:10\n# Description:\n----------------------------------------------------------\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x035BB570>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/WorkSpace/Python/python_study/week07/作用域.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x036579C0>}
{'__name__': '__main__', '__doc__': '\n----------------------------------------------------------\n# File Name: 作用域\n# Author: Leon\n# Date: 2018/8/28 0028 19:10\n# Description:\n----------------------------------------------------------\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x035BB570>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/WorkSpace/Python/python_study/week07/作用域.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x036579C0>}

1.7.2 正确使用作用域的方式

通过返回值将局部作用域中的值返回到全局作用域中,再在全局作用域中使用,不要使用global此类关键字,这样破坏了作用域的隔离,并且不利于阅读。

【示例】:

a = 1
def func():
    a = 2
    return a

a = func()
print(a)

【运行结果】:

2

1.7.3 作用域的用法

小范围的可以用大范围的,但是大范围的不能用小范围的。在小范围内,如果要用一个变量,是当前这个小范围有的,就用自己的,如果在小范围内没有,就用上一级的,上一级没有就用上上一级的,以此类推,如果都没有,就报错。

1.8 函数的嵌套

1.8.1 函数的嵌套定义

内部函数可以使用外部函数的变量,定义在内部的函数无法直接在全局被调用。

【示例】:

def max(a,b):
    return a if a > b else b

def my_max(x,y,z):
    c = max(x,y)
    return max(c,z)

print(my_max(1,2,3))

【运行结果】:

3

1.8.2 nonlocal的使用

nonlocal只能用于局部变量,影响上层中离当前函数最近一层的局部变量。声明了nonlocal的内部函数的变量修改会影响到离当前函数最近一层的局部变量,它对全局无效,对局部也只是对最近的一层才有影响。

【示例】:

a = 1
def outer():
    a = 1
    def inner():
        def inner2():
            nonlocal a
            a += 1
        inner2()
        print('inner2:',a)
    inner()
    print('inner:',a)

outer()
print('全局:',a)

【运行结果】:

inner2: 2
inner: 2
全局: 1

【示例】:

a = 1
def outer():
    a = 1
    def inner():
        a = 1
        def inner2():
            nonlocal a
            a += 1
        inner2()
        print('inner2:',a)
    inner()
    print('inner:',a)

outer()
print('全局:',a)

【运行结果】:

inner2: 2
inner: 1
全局: 1

1.8.3 闭包

内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数(函数内部定义的函数称为内部函数)称为闭包函数。

【示例】:

def outer():
    a = 1
    def inner():
        print(a)
    return inner

inn = outer()
inn()

【运行结果】:

1

第2章 装饰器介绍

2.1 装饰器基础

2.1.1 装饰器的本质和功能

  1. 装饰器的本质:一个闭包函数
  2. 装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展,即在不改变函数调用方式的基础上在函数的前、后添加功能。

2.1.2 开放封闭原则

  1. 对扩展是开放的:任何一个程序不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
  2. 对修改是封闭的:我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。
  3. 装饰器完美的遵循了这个开放封闭原则,即可以对函数的功能进行扩展又可以不影响已使用该函数的用户。

2.1.3 装饰器的固定格式

def wrapper(func):                        <em># </em><em>装饰器函数,func是被装饰的函数
     </em>def inner(*args,**kwargs):
         '''在被装饰函数之前要做的事'''<em>
         </em>ret = func(*args,**kwargs)        <em># </em><em>被装饰的函数
         </em>'''在被装饰函数之后要做的事'''
         return ret
     return inner
 
 @wrapper         <em># </em><em>语法糖:@装饰器函数名,相当于func=wrapper(func)
 </em>def func():      <em># </em><em>被装饰的函数
     </em>pass
     return

2.2 装饰器进阶

2.2.1 wraps装饰器

【示例】:

from functools import wraps
def wrapper(func):
    @wraps(func)
    def inner(*args,**kwargs):
        '''在被装饰函数之前要做的事'''
        ret = func(*args,**kwargs)
        '''在被装饰函数之后要做的事'''
        return ret
    return inner

@wrapper
def holiday(day):
    '''这是一个放假通知'''
    print('全体放假%s天'%day)
    return '好开心'

day = 5
print(holiday.__name__)     # 查看字符串格式的函数名
print(holiday.__doc__)      # 查看函数中的注释
print(holiday(day))

【运行结果】:

holiday
这是一个放假通知
全体放假5天
好开心

2.2.2 带参数的装饰器

【固定格式】:相当于又套了一层装饰器函数

FLAGES = True
def outer(Flags):
    def wrapper(func):
        def inner(*args,**kwargs):
            ret = func()
            return ret
        return inner
    return wrapper

@outer(FLAGES)
def func(*args,**kwargs):
    pass
return

【示例】:统计多个函数的执行时间

import time

FLAGES = True

def onoff(flages):
    def timer(func):
        def inner(*args,**kwargs):
            if flages:
                start = time.time()
                ret = func()
                end = time.time()
                mistiming = end-start
                print('执行时间为:%s'%mistiming)
                return ret
            else:
                ret = func()
                return ret
        return inner
    return timer

@onoff(FLAGES)
def test01():
    time.sleep(0.1)
    print('这是第一个测试函数!!')

@onoff(FLAGES)
def test02():
    time.sleep(0.1)
    print('这是第二个测试函数!!')

test01()
test02()

【运行结果】:

这是第一个测试函数!!
执行时间为:0.10069751739501953
这是第二个测试函数!!
执行时间为:0.10073208808898926

2.2.3 多个装饰器修饰一个函数

【函数执行顺序】:

【示例】:

def wrapper1(func):
    def inner1():
        print('wrapper1 ,before func')
        ret = func()
        print('wrapper1 ,after func')
        return ret
    return inner1

def wrapper2(func):
    def inner2():
        print('wrapper2 ,before func')
        ret = func()
        print('wrapper2 ,after func')
        return ret
    return inner2

def wrapper3(func):
    def inner3():
        print('wrapper3 ,before func')
        ret = func()
        print('wrapper3 ,after func')
        return ret
    return inner3

@wrapper3
@wrapper2
@wrapper1
def f():
    print('in f')
    return '测试结束'

print(f())

【运行结果】:

wrapper3 ,before func
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
wrapper3 ,after func
测试结束

第3章 迭代器

3.1 迭代协议

可迭代协议:只要含有__iter__方法的都是可迭代的。

迭代器协议:内部含有__next__和__iter__方法的就是迭代器。

3.2 迭代器特性

  1. 可以被for循环的都是可迭代的;
  2. 可迭代的内部都有__iter__方法;
  3. 只要是迭代器一定可迭代;
  4. 可迭代的.__iter__()方法就可以得到一个迭代器;
  5. 迭代器中的__next__()方法可以一个一个的获取值。

3.3 迭代器的优点

  1. 从容器类型中一个一个的取值,会把所有的值都取到;
  2. 节省内存空间,迭代器并不会在内存中再占用一大块内存,而是随着循环每次生成一个,每次next获取一个。

3.4 可迭代的数据类型

Tips:只要是能被for循环的数据类型 就一定拥有__iter__方法,可迭代的对象执行__iter__()就获得了一个迭代器

list
dic
str
set
tuple
f = open()
range()
enumerate

【示例】:查看数据类型是否可迭代

print(dir([]))
print('__iter__' in dir([]))    # __iter__:由2个下划线开头的协议叫做双下协议
Tips:双下方法很少直接调用的方法,一般情况下是通过其他语法触发的。

【运行结果】:

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
True

3.5 迭代器方法

【示例】:获得迭代器

print(type([].__iter__()))
print(set(dir([].__iter__()))-set(dir([])))

【运行结果】:

<class 'list_iterator'>
{'__length_hint__', '__setstate__', '__next__'}

3.5.1 __length_hint__()

【示例】:

l1 = [1,2,3,4,5,6]
print(l1.__iter__().__length_hint__())
l2 = ['a','b','c',1,2,3,4,5]
print(l2.__iter__().__length_hint__())

【运行结果】:

6
8

3.5.2 __next__()

【示例】:

l1 = [1,2,3,4,5,6]
iterator = l1.__iter__()
print(iterator.__next__())      # 每次执行next获取一个值
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())

【运行结果】:

1
2
3
4
5

3.5.3 判断是否可迭代和迭代器

【示例】:

from collections import Iterable        # 判断可迭代
from collections import Iterator        # 判断迭代器
print(isinstance([],Iterator))
print(isinstance([],Iterable))

【运行结果】:

False
True

第4章 生成器

4.1 生成器定义

我们自己写的能实现迭代器功能的东西就叫生成器。

4.2 生成器种类

  1. 生成器函数:常规函数定义,但是使用yield语句而不是return语句返回结果,yield语句一次返回一个结果,在每个结果中间挂起函数的状态,以便下次从它离开的地方继续执行。
  2. 生成器表达式:类似于列表推导,但是生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。

4.3 生成器本质和特性

  1. 本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
  2. 特点:惰性运算,开发者自定义

4.4 生成器函数定义

一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值,直到函数执行结束。

4.5 生成器函数特点

yield不能和return共用且需要写在函数内,生成器函数执行之后会得到一个生成器作为返回值,通过生成器获取数据。

  1. 调用函数的之后函数不执行,返回一个生成器;
  2. 每次调用next方法的时候会取到一个值;
  3. 直到取完最后一个,在执行next会报错。

【示例】:定义生成器

def generator():
    print(1)
    yield 'a'

g = generator()

for i in g:
    print(i)

【运行结果】:

1
a

【示例】:生成器执行过程

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
    yield 'c'

g = generator()
print(g.__next__())     # 每次执行到yield后停止
print(g.__next__())
print(g.__next__())

【运行结果】:

1
a
2
b
c

4.6 生成器取值方法

  1. next
  2. for
  3. 数据类型的强制转换(占用内存,不推荐使用)

4.7 send方法

send 获取下一个值的效果和next基本一致,只是在获取下一个值的时候,给上一yield的位置传递一个数据。

4.7.1 使用send的注意事项

  1. 第一次使用生成器的时候是用next获取下一个值
  2. 最后一个yield不能接受外部的值

【示例】:

def generator():
    print(1)
    content = yield 'a'
    print(content)
    print(2)
    arg = yield 'b'
    print(arg)
    yield

g = generator()

print(g.__next__())
print(g.send(''))
print(g.send('arg'))

【运行结果】:

1
a

2
b
arg
None

4.7.2 send的实例

【示例】:计算移动平均值

def avarage():
    sum =0
    count = 0
    avg =0
    while True:
        num = yield avg
        sum += num
        count += 1
        avg = sum/count

g = avarage()
g.__next__()
print(g.send(10))
print(g.send(20))
print(g.send(90))

【运行结果】:

10.0
15.0
40.0

【示例】:预激生成器装饰器

def init(func):
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        g.__next__()
        return g
    return inner

@init
def average():
    sum =0
    count = 0
    avg =0
    while True:
        num = yield avg
        sum += num
        count += 1
        avg = sum/count

g = average()
print(g.send(10))
print(g.send(20))
print(g.send(90))

【运行结果】:

10.0
15.0
40.0

4.8 yield from方法

Tips:yield from方法可以把数据拆分后输出,与for循环输出相同。

【示例】:

def generator01():
    a = 'abcdef'
    b = '123456'
    for i in a:
        yield i
    for i in b:
        yield i

def generator02():
    a = 'abcdef'
    b = '123456'
    yield from a
    yield from b

g1 = generator01()
g2 = generator02()

for i,j in zip(g1,g2):
print(i,j)

【运行结果】:

a a
b b
c c
d d
e e
f f
1 1
2 2
3 3
4 4
5 5
6 6

4.9 生成器表达式和推导式

4.9.1 表达式和推导式的区别

  1. 把列表解析的[]换成()得到的就是生成器表达式;
  2. 列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存。

4.9.2 推导式

【推导式格式】:

[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型]               # 遍历功能
[满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件]     # 筛选功能

【示例】:列表推导式

egg_list01 = ['鸡蛋%s'%i for i in range(10)]
print(egg_list01)
# 等价于
egg_list02 = []
for i in range(10):
    egg_list02.append('鸡蛋%s'%i)
print(egg_list02)

【运行结果】:

['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

【示例】:30以内所有能被3整除的数

ret = [ i for i in range(30) if i%3 == 0 ]
print(ret)
g = ( i for i in range(30) if i%3 == 0 )
for i in g:
    print(i)

【运行结果】:

[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
0
3
6
9
12
15
18
21
24
27

【示例】:找到嵌套列表中名字含有两个‘e’的所有名字

names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
          ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
ret = [ name for list in names for name in list if name.count('e') == 2 ]
print(ret)
g = ( name for list in names for name in list if name.count('e') == 2 )
for i in g:
    print(i)

【运行结果】:

['Jefferson', 'Wesley', 'Steven', 'Jennifer']
Jefferson
Wesley
Steven
Jennifer

【示例】:字典推导式:将一个字典的key和value对调

mcase = {'a': 10, 'b': 34}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)

【运行结果】:

{10: 'a', 34: 'b'}

【示例】:字典推导式:合并大小写对应的value值,将k统一成小写

mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {k.lower(): mcase.get(k.lower(),0) + mcase.get(k.upper(),0) for k in mcase}
print(mcase_frequency)

【运行结果】:

{'a': 17, 'b': 34, 'z': 3}

【示例】:集合推导式,自带结果去重功能

squared = {x**2 for x in [1, -1, 2]}
print(squared)

【运行结果】:

{1, 4}

4.9.3 表达式

【示例】:列表推导式

g = (i for i in range(10))
for i in g:
    print(i)

【运行结果】:

0
1
2
3
4
5
6
7
8
9

第5章 内置函数

参看内置函数思维导图。

5.1 匿名函数

5.1.1 匿名函数定义

匿名函数:为了解决那些功能很简单的需求而设计的一句话函数。

5.1.2 什么是匿名函数

def calc(n):
    return n*n

print(calc(10))
# 等价于
calc = lambda n:n*n
print(calc(10))

5.1.3 匿名函数格式

函数名 = lambda 参数 :返回值

# 参数可以有多个,用逗号隔开
# 匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束后的内容就是返回值
# 返回值和正常的函数一样可以是任意数据类型
# 匿名函数并不是真的不能有名字,匿名函数的调用和正常的调用也没有什么分别

5.1.4 匿名函数示例

【示例】:根据返回值判断最大值,返回值最大的那个参数是结果

dic = {'k1':10,'k2':100,'k3':30}
def func(key):
    return dic[key]
# 等价于
print(max(dic,key=func))
print(max(dic,key=lambda key:dic[key]))

【运行结果】:

k2
k2

【示例】:

res = map(lambda x:x**2,[1,5,7,4,8])
for i in res:
    print(i)

【运行结果】:

1
25
49
16
64

【示例】:

res = filter(lambda x:x>10,[5,8,11,9,15])
for i in res:
    print(i)

【运行结果】:

11
15

【示例】:现有两元组((‘a’),(‘b’)),((‘c’),(‘d’)),请使用python中匿名函数生成列表[{‘a’:’c’},{‘b’:’d’}]

ret = zip((('a'),('b')),(('c'),('d')))
ret = map(lambda t:{t[0]:t[1]},ret)
print(list(ret))

【运行结果】:

[{'a': 'c'}, {'b': 'd'}]

【示例】:请写出下面程序的输出结果

def multipliers():
    return [lambda x:i*x for i in range(4)]
print([m(2) for m in multipliers()])

【运行结果】:

[6, 6, 6, 6]

第6章 递归函数

6.1 递归函数定义

递归函数:在函数中调用自身函数。

6.2 什么是递归函数

def story():
    print("从前有座山")
    story()
    print("结束了!!!")

story()

6.2.1 递归的最大深度

最大递归深度默认是1000(是python从内存角度出发做得限制)

Tips:递归的错误,超过了递归的最大深度

RecursionError: maximum recursion depth exceeded while calling a Python object

6.2.2 修改递归函数的最大深度

import sys
sys.setrecursionlimit(2000)
n = 0
def story():
    global n
    n += 1
    print(n)
    story()
story()

6.3 递归函数的优缺点及使用场景

6.3.1 优缺点

  • 递归的缺点:占内存
  • 递归的优点:会让代码变简单

6.3.2 使用场景

递归次数不多的情况,如果递归次数太多,就不适合使用递归来解决问题。

6.4 如何查看递归

【示例】:

def age(n):
    if n == 4:
        return 40
    elif n > 0 and n < 4:
        return age(n+1) + 2

print(age(1))

【运行结果】:

46

【分析】:

# def age(1):
#     if 1 == 4:
#         return 40
#     elif 1 > 0 and 1 < 4:
#         return age(2) +2
#
# def age(2):
#     if 2 == 4:
#         return 40
#     elif 2 >0 and 2 < 4:
#         return age(3) + 2
#
# def age(3):
#     if 3 == 4:
#         return 40
#     elif 3 >0 and 3 < 4:
#         return age(4) + 2
#
# def age(4):
#     if 4 == 4:
#         return 40             # 返回40
#     elif n >0 and n < 4:
#         age(n+1) + 2
#
# def age(3):
#     if 3 == 4:
#         return 40
#     elif 3 >0 and 3 < 4:
#         return 40 + 2         # 返回42
#
# def age(2):
#     if 2 == 4:
#         return 40
#     elif 2 >0 and 2 < 4:
#         return 42 + 2         # 返回44
#
# def age(1):
#     if 1 == 4:
#         return 40
#     elif 1 > 0 and 1 < 4:
#         return 44 +2          # 返回46

6.5 二分查找算法

Tips:二分查找算法 必须处理有序的列表。

【初级版】:

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

def find(l,aim):
    mid_index = len(l) // 2
    if l[mid_index] < aim:
        new_l = l[mid_index+1:]
        find(new_l,aim)
    elif l[mid_index] > aim:
        new_l = l[:mid_index]
        find(new_l,aim)
    else:
        print('找到了',mid_index,l[mid_index])

find(l,66)

【进阶版】:解决初级版必须提前定义列表和无法返回值的问题

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

def find(l,aim,start=0,end=None):
    end = len(l) if end is None else end
    mid_index = (end - start) // 2 + start
    if start <= end:
        if l[mid_index] < aim:
            return find(l,aim,start=mid_index+1,end=end)
        elif l[mid_index] > aim:
            return find(l,aim,start=start,end=mid_index-1)
        else:
            return mid_index
    else:
        return '找不到这个值'

ret=find(l,44)
print(ret)
温馨提示:本文最后更新于2022-12-20 20:57:46,已超过494天没有更新。某些文章具有时效性,若文章内容或图片资源有错误或已失效,请联系站长。谢谢!
转载请注明本文链接:https://blog.leonshadow.cn/763482/1271.html
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享