Python第6章复习笔记

6.1 什么是抽象

  • 隐藏细节:函数是实现抽象的主要方式之一。通过函数,我们可以将一组相关操作封装在一个定义明确的接口中,而不必关心其内部的具体实现。
  • 重用代码:通过创建具有明确功能的函数,我们可以在多处重用这些函数,而不必重复编写相同的代码。这是抽象帮助我们实现代码重用的重要方式。
  • 模块化:函数为我们提供了一种模块化的方法,使我们能够将大型程序分解为小的、可管理的、独立的部分。每个函数代表程序中的一个模块或操作
  • 易于维护和调试:通过抽象和函数化,我们可以将问题分解,使得每个部分都更容易维护和调试。如果每个函数都设计得很好,且具有明确的目的,那么找出和修复错误会变得更容易。
  • 参数化:函数允许参数化,这意味着我们可以通过传递不同的参数来改变函数的行为,而不必改变函数本身的代码。这是抽象的另一个重要方面,它使我们能够创建更为通用和灵活的代码。
  • 提高代码可读性:函数通过为一组操作提供描述性的名称,帮助提高代码的可读性。这是抽象帮助我们编写清晰、易读代码的另一种方式。

6.2 自定义函数

6.2.1 自定义函数

def greet_user():
    print('Hello!')
greet_user()
#运行结果
Hello!

使用关键字def来告诉Python,你要定义一个函数,向Python指出了你自己定义的函数名greet_user,还可以在括号内指出函数为完成工作需要什么样的信息,以冒号结尾
紧跟在第一行后面的所有缩进行构成了函数体
代码行print(‘Hello!’) 表明你要用这个函数完成什么工作
要使用这个函数,必须调用它,调用函数时可依次指定函数名以及括号括起的必要信息

6.2.2 访问文档字符串

第一种方法:函数名._ _ doc _ _,__doc__是函数的一个属性,属性名中的双下划线表示这是一个特殊的属性
第二种方法:help(函数名),特殊的内置函数help很有用,在交互式解释器中,可使用它获取有关函数的信息, 其中包含函数的文档字符串。

6.3 形参和实参

实参:可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值
形参:只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。

6.3.1 位置实参

def pet(type,name):
    print(f'I have a {type}')
    print(f'My {type}s name is {name}')
pet('dog','Leo')
pet('Leo','dog')
#运行结果
I have a dog
My dogs name is Leo
I have a Leo
My Leos name is dog

位置参数是基于它们在函数调用中出现的顺序传递给函数的参数。它们是在定义函数时列出的参数,没有默认值。当调用具有位置参数的函数时,必须按照函数定义中的顺序为每个位置参数提供值。

6.3.2 关键字参数

def pet(type,name):
    print(f'I have a {type}')
    print(f'My {type}s name is {name}')
pet(type='dog',name='Leo')
pet(name='Leo',type='dog')
#运行结果
I have a dog
My dogs name is Leo
I have a dog
My dogs name is Leo

关键字参数,就是我们在函数传递实参时,可以通过参数名指定我们具体传递的是哪个形参,但关键字参数的实参和形参的个数还必须是一一对应的。

6.3.3 默认值

def pet(name,type='dog'):
    print(f'I have a {type}')
    print(f'My {type}s name is {name}')
pet(name='Leo')
pet('Leo')
#运行结果
I have a dog
My dogs name is Leo
I have a dog
My dogs name is Leo
  1. 在编写函数时,可以给每个形参指定默认值,如果在调用函数中给形参提供了实参,Python将使用指定的实参值,否则,将使用形参的默认值
  2. 在使用默认值时,必须在形参列表中先列出没有默认值的形参,再列出有默认值的形参。这让Python依然能够正确的解读位置实参。

6.4 返回值

6.4.1 返回简单的值

def test(a):
    print(a)
    return
print(test(12))
#运行结果
12
None

数学意义上的函数总是返回根据参数计算得到的结果
在Python中,有些函数什么都不返回
所有的函数都有返回值,如果你没有告诉它们该返回什么,将返回None

6.4.2 返回任何类型的值

def person_name(first_name,last_name):
    person={'first':first_name,'last':last_name}
    return person
person1=person_name('王','老五')
print(person1)
#运行结果
{'first': '王', 'last': '老五'}
def generate_numbers(start,end):
    numbers=[]
    for i in range(start,end+1):
        numbers.append(i)
    return numbers
numbers_list=generate_numbers(1,10)
print(numbers_list)
#运行结果
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

函数可返回任何类型的值,包括列表和字典等较为复杂的数据结构

6.5 参数魔法

6.5.1 当参数为不可变数据类型——字符串、数、元组

def test(a):
    a=10
    print(a)
num=20
test(num)
print(num)
num=20
a=num
a=10
print(num)

上述两段代码效果相同
在函数内部给参数赋值对外部没有任何影响,参数存储在局部作用域内

6.5.2 当参数为不可变数据类型——列表、字典

def test(a):
    a[1]=100
    print(a)
numList=[1,2,3,4,5]
test(numList)
print(numList)
numList=[1,2,3,4,5]
a=numList
a[1]=100
print(numList)

上述两段代码效果相同
将同一个列表赋给两个变量时,这两个变量将同时指向这个列表

如果要在函数内部给参数赋值不会对外部产生影响怎么办?

def test(a):
    a[1]=100
    print(a)
numList=[1,2,3,4,5]
test(numList[:])
print(numList)
import copy
def test(a):
    a[1]=100
    print(a)
numList=[1,2,3,4,5]
test(copy.copy(numList))
print(numList)

上述两段代码效果相同
如果要禁止函数修改列表,即在函数内部给参数赋值不会对外部产生影响,可以采用切片方法和copy,deepcopy方法

6.5.3 收集参数——传递任意数量的实参

def shopping_list(*item):
    print(item)
shopping_list('apple')
shopping_list('pen','water','banana')
#运行结果
('apple',)
('pen', 'water', 'banana')

有时候你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参

6.5.4 收集参数——结合使用位置实参和任意数量的实参

def shopping_list(kind,*items):
    print(f'你已经买了{kind}种物品,它们分别是:')
    for item in items:
        print(f'{item}')
shopping_list(1,'apple')
shopping_list(3,'pen','water','banana')
#运行结果
你已经买了1种物品,它们分别是:
apple
你已经买了3种物品,它们分别是:
pen
water
banana

如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中

def shopping_list(*items,kind):
    print(f'你已经买了{kind}种物品,它们分别是:')
    for item in items:
        print(f'{item}')
shopping_list('apple',kind=1)
shopping_list('pen','water','banana',kind=3)
#运行结果
你已经买了1种物品,它们分别是:
apple
你已经买了3种物品,它们分别是:
pen
water
banana
  1. kind是一个仅限关键字参数,因为它位于*items之后。这意味着,当调用函数时必须使用关键字而不是位置来指定kind。
  2. 当调用一个函数时,必须先传递位置参数,然后是关键字参数。这是Python函数调用的一个基本规则:位置参数永远在关键字参数之前。

6.5.5 收集参数——使用任意数量的关键字实参

def bulid_profile(first,last,**user_info):
    user_info['first name']=first
    user_info['last name']=last
    return user_info
user_profile=bulid_profile('Li', 'Ming', location='Beijing',age=23,field='AI')
print(user_profile)
#运行结果
{'location': 'Beijing', 'age': 23, 'field': 'AI', 'first name': 'Li', 'last name': 'Ming'}

有的时候,你需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键值对——调用语句提供了多少就接受多少。

6.5.6 收集参数——混合使用

def print_params_4(x,y,z=3,*pospar,**keypar):
    print(x,y,z)
    print(pospar)
    print(keypar)
print_params_4(1,2,3,5,6,7,foo=1,bar=2)
#运行结果
1 2 3
(5, 6, 7)
{'foo': 1, 'bar': 2}

6.5.7 分配参数

前面介绍了如何将参数收集到元组和字典中,但用同样的两个运算符( *和**)也可执行相反的操作

def add(x,y):
    return x+y
d={'x':1,'y':2}
print(add(**d))
#运行结果
3

在调用 add 函数时,使用了 ** 操作符来解包字典 d。** 操作符会将字典的键-值对解包为关键字参数,就好像你直接传递了关键字参数一样。
** 操作符让 Python 解释器将字典的键作为参数名,将字典的值作为参数值。这是一个非常强大和灵活的功能,允许你动态构建参数列表并传递给函数。

6.6 命名空间和作用域

6.6.1 命名空间

在这里插入图片描述
命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。

命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。

我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。

在这里插入图片描述
一般有三种命名空间:

  • 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称
    BaseException、Exception 等等。
  • 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
  • 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

命名空间查找顺序:
Python 的查找顺序为:局部的命名空间 -> 全局命名空间 -> 内置命名空间。
如果找不到变量 ,它将放弃查找并引发一个 NameError 异常:
NameError: name ‘runoob’ is not defined。

6.6.2 作用域

什么是变量?
变量常被描述为可用于存储值的盒子,但它并没有准确的描述Python内部表示变量的方式, 一种好得多的定义是,变量是可以被赋值的标签,可以说变量指向特定的值,这几乎与使用字典时一样(字典中的键指向值),只是你使用的是”看不见”的字典

x=3
y=4
scope=vars()
print(scope)

实际上,有一个名为vars的内置函数,它返回这个不可见的字典:
这种“看不见的字典”称为命名空间或作用域

vars() 函数被调用,它返回一个字典,该字典包含当前作用域中所有的变量及其值。函数返回的字典的键是变量名(作为字符串),值是相应变量的值。

scope 不是字典的键,而是一个变量,它包含一个字典。这个字典的键是当前作用域中所有变量的名字(包括 scope 本身),而值是这些变量的值。
在这里插入图片描述
作用域就是一个 Python 程序可以直接访问命名空间的正文区域。在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。
Python 的作用域一共有4种,分别是:
有四种作用域:
L(Local):最内层,包含局部变量,比如一个函数/方法内部。
E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
G(Global):当前脚本的最外层,比如当前模块的全局变量。
B(Built-in): 包含了内建的变量/关键字等,最后被搜索。
规则顺序: L –> E –> G –> B。
在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

6.6.3 全局变量和局部变量

total=0
def sum(arg1,arg2):
    total=arg1+arg2
    print("函数内是局部变量:",total)
    return total
sum(10,20)
print("函数外是全局变量:",total)
#运行结果
函数内是局部变量: 30
函数外是全局变量: 0

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中

每个函数会创建一个新的作用域,函数中的局部变量只在该作用域中起作用·局部变量也会对应一个字典来存储。一般,函数内部可以读取全局变量,但不能修改。

全局变量和局部变量———global和nonlocal关键字

  • global 关键字: global关键字用于声明一个变量是全局变量,即使该变量在函数的局部作用域内也是如此。这允许你在函数内部修改全局变量的值。
  • nonlocal 关键字: nonlocal关键字用于声明一个变量是在包含它的最近的外部函数作用域中,而不是在全局作用域或本地作用域中。这允许你在内部函数中修改外部函数作用域的变量值。

6.7 递归

  • 递归:调用自身
  • 避免无穷递归:设置前置条件。不能永远调用下去,因为内存有限,永远调用会导致内存耗尽,程序终止。
  • 最小问题(基线条件): 满足这种条件时函数将直接返回一个值或者执行特定动作,不再进行下一层的调用。
  • 递归规则: 在没有达到最小情况下,如何调用下一层的自身。

经典案例:阶乘和幂

def factorial(n):
    if n==1:
        return 1
    else:
        return n*factorial(n-1)
def power(x,n):
    if n==0:
        return 1
    else:
        return x*pow(x,n-1)

6.8 函数式编程

6.8.1 map函数

def square(x):
    return x*x
numbers=[1,2,3,4]
squared=map(square, numbers)
print(list(squared))
#运行结果
[1, 4, 9, 16]

map() 函数接受一个函数和一个或多个可迭代对象作为参数,并返回一个迭代器,该迭代器通过将指定的函数应用于每个可迭代对象的项来产生新项。
在Python中,"可迭代对象"指的是可以作为序列进行迭代的对象,即你可以遍历该对象中的所有元素。
迭代器是另一个相关的概念,它们实际上是一个带状态的对象,知道如何获取它的下一个值。

6.8.2 filter函数

def is_even(x):
    return x%2==0
numbers=[1,2,3,4]
evens=filter(is_even, numbers)
print(list(evens))
#运行结果
[2, 4]

filter() 函数接受一个函数和一个可迭代对象,并返回一个迭代器,该迭代器只包含使指定函数返回 True 的项

6.8.3 reduce函数

from functools import reduce
def add(x,y):
    return x+y
numbers=[1,2,3,4]
total=reduce(add,numbers)
print(total)
#运行结果
10

reduce() 函数接受一个函数和一个可迭代对象,并通过从左到右应用指定的函数来累积可迭代对象的项,从而产生单个值。注意,reduce() 函数需要从 functools 模块导入。

6.8.4 lambda函数

numbers=[1,2,3,4]
squared=map(lambda x:x*x, numbers)
print(list(squared))
#运行结果
[1, 4, 9, 16]

lambda 表达式用于创建简单的匿名函数,它们通常用于短暂的操作,例如与 map、filter 和 reduce 一起使用。

6.9 总结

  • 抽象:抽象是隐藏不必要细节的艺术。通过定义处理细节的函数,可让程序更抽象。
  • 函数定义:函数是使用def语句定义的。函数由语句块组成,它们从外部接受值(参数),并可能返回一个或多个值(计算结果)。
  • 参数:函数通过参数(调用函数时被设置的变量)接收所需的信息。在Python中,参数有两类:位置参数和关键字参数。通过给参数指定默认值,可使其变成可选的。
  • 作用域:变量存储在作用域(也叫命名空间)中。在Python中,作用域分两大类:全局作用域和局部作用域。作用域可以嵌套。
  • 递归:函数可调用自身,这称为递归。可使用递归完成的任何任务都可使用循环来完成,但有时使用递归函数的可读性更高。
  • 函数式编程:Python提供了一些函数式编程工具,其中包括lambda表达式以及函数map、filter和reduce。
  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

The square of H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值