揭秘python函数:编程艺术的核心力量(2)

简介: 揭秘python函数:编程艺术的核心力量(2)


前言

前面我们学习了 python 函数的基本使用,那么今天我将继续带大家深入了解 python 函数的更多操作。

变量的作用域

变量分为局部变量和全局变量,两种变量的作用域不同。

  • 局部变量是指定义在函数体内部的变量,只在函数体的内部生效。
  • 全局变量是指在函数的内部和外部都能生效的变量。

局部变量

def test1():
    a = 100  # 这里a属于局部变量
    print(a)

当出了函数体之后,局部变量 a 会被销毁。

def test1():
    a = 100
    print(a)
print(a)


全局变量

a = 100
def test1():
    print(f'在函数内部使用全局变量{a}')
test1()
print(f'在函数外部使用全局变量{a}')

在函数体外部修改全部变量,全局变量会被修改,那么如果我们在函数体内部修改全局变量会怎样呢?

a = 100
def test1():
    a = 200
    print(a)
test1()
print(a)

可以发现:在函数体内部修改全局变量只是修改的是函数体内部的变量,实际上全局变量并未改变,也就是说函数体内部修改的其实还是局部变量,那么如何在函数体内部修改全局变量呢?


global 变量 声明此变量为全局变量,然后对全局变量进行修改。

a = 100
def test1():
    global a
    a = 200
    print(a)
test1()
print(a)

函数的返回值

前面我们说过的函数的返回值都是一个数据,那么如果我们想要返回多个数据该怎么办呢?


  1. 使用逗号 , 来分割多个数据,但是这多个数据最总会被合并为一个元组返回。
  2. 以列表、元组、字典、集合的形式返回多个数据。


以逗号 , 形式返回

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


以列表形式返回

def test1():
    return [1,2,3]
print(test1())


以元组的形式返回

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

以集合的形式返回

def test1():
    return {1,1,2,3}
print(test1())


对此,我们可以根据需要来选择以哪种形式返回数据

函数的参数

函数传参的方式有四种:


  1. 位置传参
  2. 关键字传参
  3. 缺省传参
  4. 不定长传参

1.位置参数

调用函数的时候,根据函数定义的参数的位置顺序和数量进行传参。

def test1(name,age,gender):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1('zhangsan',18,'man')

当传入的参数的数量与函数定义时的参数数量不同时,会报错。

def test1(name,age,gender):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1('zhangsan',18)

传参时,顺序与定义时参数顺序不同,可以运行通过,但代码无意义。

def test1(name,age,gender):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1(18,'zhangsan','man')

2.关键字参数

通过“键 = 值” 的形式加以制定,可以让函数更加清晰、容易使用,同时也不需要遵守传参的顺序。

def test1(name,age,gender):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1(gender = 'man',name = 'zhangsan',age = 18)

使用关键字传参时,可以有位置参数,但是位置参数必须在关键字参数之前。位置参数必须要和函数定义时的参数的位置对应。

def test1(name,age,gender):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1('zhangsan',gender = 'man',age = 18)

当位置参数与函数定义时参数不对应时,会报错。

def test1(name,age,gender):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1('man',name = 'zhangsan',age = 18)

3.缺省参数

缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)。


在函数定义的时候,我们可以为参数指定默认值,这样当我们不为这个参数传入值的时候,这个参数就会使用默认值,如果我们为这个参数传入数据,这个参数也会变成我们传入的数据。

def test1(name,age,gender = 'man'):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1('zhangsan',18)

def test1(name,age,gender = 'man'):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1('zhangsan',18,'woman')
test1(age = 19,name = 'lisi')

一旦你开始在函数定义中使用默认参数,那么所有在该默认参数后面的参数都必须是默认参数。换言之,所有无默认值的参数(位置参数)都必须出现在带有默认值的参数(关键字参数)前面。

def test1(name,age = 18,gender):
    print(f'姓名:{name} 年龄:{age} 性别:{gender}')
test1('zahngsan','man')

4.不定长参数

不定长参数也叫可变参数。用于不确定调用的时候会传递多少个参数(不传参也可以)的场景。此时,可用包裹(packing)位置参数,或者包裹关键字参数,来进行参数传递,会显得非常方便。


1)包裹位置传递

def 函数名(*args):

这个 * 不可省略,是包裹位置传递的标志,后面的args可以更换,但是实际生活中建议使用args,因为 python 底层也是使用的args。


使用位置包裹传递的参数会被合并成一个元组传入函数中。

def test1(*args):
    print(args)
test1()
test1(1)
test1(1,2)

2)包裹关键字传递

def 函数名(**args):

传入参数时,以 “键 = 值” 的形式传递。并且以包裹关键字传递的形式传参的时候,这些参数会被合并为一个字典传入参数中。

def test1(**args):
    print(args)
test1(name = 'zhangsan',age = 18,gender = 'man')

上面的包裹位置传递和包裹关键字传递都是一个组包的过程,我们也可以进行拆包过程。

拆包

1)元组拆包

def test1():
    return 100,200
a,b = test1()
print(a)
print(b)

这里注意:拆包过程中,接收的变量的数量要与元组中的元素个数相同。

2)字典拆包

字典拆包,变量中存储的是字典的 key ,我们可以根据拆包获得的 key ,来操作 value 值

dict1 = {'name':'zhangsan','age':18,'gender':'man'}
a,b,c = dict1
print(a)
print(b)
print(c)
print(dict1[a])
print(dict1[b])
print(dict1[c])

交换变量的值

在 python 中,常见的交换两个变量的值有两种方法。

  1. 采用中间值的方法。
  2. a,b = b,a


采用中间值的方法

a = 100
b = 200
c = 0
c = a
a = b
b = c
print(a)
print(b)

采用中间值c来存储其中一个数据,防止在交换过程中被替换掉。

b,a = a,b

a = 100
b = 200
a,b = b,a
print(a)
print(b)

引用

在Python中,引用对于对象的处理有着举足轻重的作用。


对象赋值:在Python中,当你创建一个对象并给它赋一个变量名时,这个变量名只是指向那个对象的引用,而并非对象本身。例如,当你创建一个列表a = [1, 2, 3],a实际上是对列表[1, 2, 3]的引用。


函数参数传递:Python使用引用传递(pass-by-reference)方式向函数传递参数。所以,如果你在函数内部改变了传入对象的值,那么在函数外面的那个对象的值也会随之改变。需要注意的是,对于不可变类型(如整数、字符串、元组),由于其不可变的特性,函数内部对其进行的操作不会影响外部实际对象。


共享和复制:Python中对象的赋值,实际上是在创建一个新的引用,而不是创建一个全新的对象。例如,当你写b = a,你实际上只是创建了一个新的引用b,并没有创建一个新的列表。这就是所谓的浅复制(shallow copy)。如果你要创建一个对象的深度副本(deep copy),即一个全新的对象,可以使用copy模块的deepcopy函数。尤其是对于复杂的数据结构如列表、字典或者自定义的对象,理解Python中的引用模型非常重要。


这里第三点我们先做了解,后面再给大家讲解。

在了解引用之前,我们需要了解在 python 中,哪些数据类型是可变数据类型,哪些类型是不可变类型。

可变数据类型和不可变数据类型

可变类型

  • 列表
  • 字典
  • 集合

不可变类型

  • 整型
  • 浮点型
  • 字符串
  • 元组

我们用一段代码来了解什么是引用。

id() 函数可以知道引用所引用的内容的地址。

a = 1
b = a
print(id(a))
print(id(b))


a 引用的是 1 ,b 引用 a 引用的引用。

a = 1
b = a
a = 2
print(a)
print(b)
print(id(a))
print(id(b))

这里因为 int 类型是不可变类型,所以当 a 的值由1变为2的时候,会另外开辟一个空间存储2,然后引用 a 指向该地址。


当引用指向的类型是可变类型时,会不会发生这种情况呢?

a = [1,2,3]
b = a
a.append(4)
print(a)
print(b)
print(id(a))
print(id(b))

我们看到,当引用的是可变数据类型时,当改变数据内容时,引用的地址不会改变。

我们可以将引用作为参数传递参数。

def test1(a):
    print(id(a))
    a = 2
    print(id(a))
a = 1
print(id(a))
test1(a)
print(id(a))
print(a)

在这里,函数内部的引用 a 其实是一个临时变量,并且 a 引用的数据类型是 int 型,是不可变类型,当改变临时变量 a 的值时,会额外创建一个值为 2 的 int 类型的内存,然后临时变量 a 指向该内存,当出了函数时,临时变量 a 会被销毁,打印的 a 的值还是之前的值。


当传入的引用引用的数据类型是可变类型时

def test1(a):
    print(id(a))
    a[0] = 4
    print(id(a))
a = [1,2,3]
print(id(a))
test1(a)
print(id(a))
print(a)


相关文章
|
1天前
|
Python
python中定义函数时使用位置参数
【7月更文挑战第25天】
13 7
|
2天前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
11 4
|
1天前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
【7月更文挑战第26天】在网络的数字宇宙中,Python Socket编程是开启网络世界大门的钥匙。本指南将引领你从基础到实战,成为网络世界的建筑师。
6 2
|
2天前
|
开发者 Python
Python Socket编程:不只是基础,更有进阶秘籍,让你的网络应用飞起来!
【7月更文挑战第25天】在网络应用蓬勃发展的数字时代,Python凭借其简洁的语法和强大的库支持成为开发高效应用的首选。本文通过实时聊天室案例,介绍了Python Socket编程的基础与进阶技巧,包括服务器与客户端的建立、数据交换等基础篇内容,以及使用多线程和异步IO提升性能的进阶篇。基础示例展示了服务器端监听连接请求、接收转发消息,客户端连接服务器并收发消息的过程。进阶部分讨论了如何利用Python的`threading`模块和`asyncio`库来处理多客户端连接,提高应用的并发处理能力和响应速度。掌握这些技能,能使开发者在网络编程领域更加游刃有余,构建出高性能的应用程序。
10 3
|
1天前
|
消息中间件 网络协议 网络安全
Python Socket编程:打造你的专属网络通道,基础篇与进阶篇一网打尽!
【7月更文挑战第26天】在网络编程领域,Python以简洁语法和强大库支持成为构建应用的首选。Socket编程为核心,实现计算机间的数据交换。
8 1
|
2天前
|
Python
Python中使用函数参数
【7月更文挑战第23天】
12 2
|
9天前
|
Python
告别低效!Python并查集:数据结构界的超级英雄,拯救你的编程人生!
【7月更文挑战第18天】并查集,数据结构超级英雄,用于不相交集合的合并与查询。Python实现包括初始化、查找根节点和合并操作。应用广泛,如社交网络分析、图论问题、集合划分等。示例代码展示了解决岛屿数量问题,统计连通的“1”单元格数。掌握并查集,提升编程效率,解决复杂问题。
27 6
|
5天前
|
存储 算法 搜索推荐
告别低效编程!Python算法设计与分析中,时间复杂度与空间复杂度的智慧抉择!
【7月更文挑战第22天】在编程中,时间复杂度和空间复杂度是评估算法效率的关键。时间复杂度衡量执行时间随数据量增加的趋势,空间复杂度关注算法所需的内存。在实际应用中,开发者需权衡两者,根据场景选择合适算法,如快速排序(平均O(n log n),最坏O(n^2),空间复杂度O(log n)至O(n))适合大规模数据,而归并排序(稳定O(n log n),空间复杂度O(n))在内存受限或稳定性要求高时更有利。通过优化,如改进基准选择或减少复制,可平衡这两者。理解并智慧地选择算法是提升代码效率的关键。
|
8天前
|
存储 开发者 Python
从理论到实践:Python中Trie树与Suffix Tree的完美结合,开启编程新篇章!
【7月更文挑战第19天】在编程实践中,Trie树和Suffix Tree优化了字符串处理。Trie树用于快速拼写检查,如在构建词库后,能高效判断单词是否存在。Suffix Tree则助力文本相似度检测,找寻共同子串。通过Python示例展示了Trie树插入和搜索方法,并指出Suffix Tree虽复杂但能提升性能。结合两者,实现复杂功能,展现数据结构的强大。
24 3