当前位置: 首页 > news >正文

第六章 Python-函数基础

第六章 函数基础

6.1 初识函数

6.1.1 什么是函数

函数就是为了去实现某种特定的功能而存在的

在Python当中,旧可以将函数理解为一大段功能代码的集合,也是为了实现某些功能。重点在于封装某些特定的功能。增强代码的重要性

6.1.2 函数的定义

def 函数名():代码块

6.1.3 函数的调用

函数()  # 函数在定义的时候不会执行内容代码,在调用的时候才会执行

6.1.4 函数的实例

def func():print("你打开了一个函数")
# 函数名称funcfunc()

你打开了一个函数

6.1.5 函数的应用场景

在这里插入图片描述

  • 桥接真机网络,云朵是WIN10电脑,IP地址 为192.168.0.10,其他三台CE的IP地址如图,且都开启telnet,认证模式是Password,密码是huawei@123
import telnetlib
import timeip = "192.168.0.1"
pwd = "huawei@123"
tn = telnetlib.Telnet(ip, port=23)
tn.read_until("Password:".encode("utf-8"))
tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))
time.sleep(0.5)
tn.write(b"display ip int br\n")
time.sleep(0.5)
res = tn.read_very_eager().decode("utf-8")
print(res)
  • 输入结果如下:
Info: The max number of VTY users is 5, the number of current VTY users online is 1, and total number of terminal users online is 2.The current login time is 2024-03-21 01:50:37.
<CE1>display ip int br
*down: administratively down
!down: FIB overload down
^down: standby
(l): loopback
(s): spoofing
(d): Dampening Suppressed
The number of interface that is UP in Physical is 3
The number of interface that is DOWN in Physical is 0
The number of interface that is UP in Protocol is 2
The number of interface that is DOWN in Protocol is 1
Interface                   IP Address/Mask    Physical Protocol VPN           
GE1/0/0                     192.168.0.1/24     up       up       --            
MEth0/0/0                   unassigned         up       down     --            
NULL0                       unassigned         up       up(s)    --            
<CE1>
  • 将上述代码封装成函数进行调用:
import telnetlib
import timedef telnet_connect():ip = "192.168.0.1"pwd = "huawei@123"tn = telnetlib.Telnet(ip, port=23)tn.read_until("Password:".encode("utf-8"))tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))time.sleep(0.5)tn.write(b"display ip int br\n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)
telnet_connect()

输入结果如下:

Info: The max number of VTY users is 5, the number of current VTY users online is 1, and total number of terminal users online is 2.The current login time is 2024-03-21 01:54:17.
<CE1>display ip int br
*down: administratively down
!down: FIB overload down
^down: standby
(l): loopback
(s): spoofing
(d): Dampening Suppressed
The number of interface that is UP in Physical is 3
The number of interface that is DOWN in Physical is 0
The number of interface that is UP in Protocol is 2
The number of interface that is DOWN in Protocol is 1
Interface                   IP Address/Mask    Physical Protocol VPN           
GE1/0/0                     192.168.0.1/24     up       up       --            
MEth0/0/0                   unassigned         up       down     --            
NULL0                       unassigned         up       up(s)    --            
<CE1>

6.2 函数的参数

6.2.1 形参和实参

在我们构建函数式,可以能会存在一些情况需要让函数里面的某些值根据需求做出改变,需要为函数提供一个参数

import telnetlib
import timedef telnet_connect(ip,pwd):  # 创建函数的时候,定义的参数叫做形参tn = telnetlib.Telnet(ip, port=23)tn.read_until("Password:".encode("utf-8"))tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))time.sleep(0.5)tn.write(b"display ip int br\n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)
telnet_connect("192.168.0.1","huawei@123")  # 在调用函数的时候,传入的参数叫做实参,形参接受实参全送过来的数值,然后应用于函数的内部变量
  • 输出如下:
Info: The max number of VTY users is 5, the number of current VTY users online is 1, and total number of terminal users online is 2.The current login time is 2024-03-21 02:03:19.
<CE1>display ip int br
*down: administratively down
!down: FIB overload down
^down: standby
(l): loopback
(s): spoofing
(d): Dampening Suppressed
The number of interface that is UP in Physical is 3
The number of interface that is DOWN in Physical is 0
The number of interface that is UP in Protocol is 2
The number of interface that is DOWN in Protocol is 1
Interface                   IP Address/Mask    Physical Protocol VPN           
GE1/0/0                     192.168.0.1/24     up       up       --            
MEth0/0/0                   unassigned         up       down     --            
NULL0                       unassigned         up       up(s)    --            
<CE1>

6.2.2 位置参数和关键字参数

  • 以下案例中,使用的参数是未知参数,IP对应192.168.0.1,pwd对应的时huawei@123
import telnetlib
import timedef telnet_connect(ip,pwd):tn = telnetlib.Telnet(ip, port=23)tn.read_until("Password:".encode("utf-8"))tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))time.sleep(0.5)tn.write(b"display ip int br\n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)telnet_connect("192.168.0.1","huawei@123")  # 位置参数的前后顺序必须和函数起始定义的相同
  • 关键字参数:以下例子中,函数调用是使用的参数通过关键字直接标明参数的值,不需要对参数的前后顺序进行对应
import telnetlib
import timedef telnet_connect(ip, pwd):  # 设置两个形参tn = telnetlib.Telnet(ip, port=23)tn.read_until("Password:".encode("utf-8"))tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))time.sleep(0.5)tn.write(b"display ip int br\n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)telnet_connect(pwd="huawei@123", ip="192.168.0.1")  # 关键字参数通过关键字直接关联对应的参数值,不需要进行位置对应。

6.2.3 默认参数

import telnetlib
import timedef telnet_connect(ip, pwd="none"):  # 定义pwd默认参数为none,如果存在默认参数,则使用默认参数,否则使用参数调用时指定的实参。tn = telnetlib.Telnet(ip, port=23)tn.read_until("Password:".encode("utf-8"))tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))time.sleep(0.5)tn.write(b"display ip int br\n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)telnet_connect(pwd="huawei@123", ip="192.168.0.1")  # 关键字参数通过关键字直接关联对应的参数值
telnet_connect(ip="192.168.0.1")  # 此处pwd实参可以不指定值,使用上述函数中定义的默认值

6.2.4 动态参数(可变长参数)

  • 可变长位置参数*
    • 可变长位置参数可以接受任何数量的位置参数,并将接收到的位置参数打包成一个元组,如果想要用到具体的数据,旧可以通过元组的索引进行提取,但是可变长位置参数不能够接受关键字参数,如果传入关键字参数就会报错。
    • 可变长位置参数,形成==元组==
import telnetlib
import timedef telnet_connect(*args):  # args是一个元组,函数调用时写入的参数都会成为元组中的参数。括号中可以是*加任何字符串ip = args[0]pwd = args[1]tn = telnetlib.Telnet(ip, port=23)tn.read_until("Password:".encode("utf-8"))tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))time.sleep(0.5)tn.write(b"display ip int br\n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)telnet_connect("192.168.0.1", "huawei@123")  # 只能传入位置参数,不能使用关键字参数
  • 可变长关键字参数**
    • 可变长关键字参数可以接受任意数量的关键字参数,并将接收到的关键字参数打包成一对键值对,其中变量名为key,变量值为value。如果使用到具体的数据,旧可以通过字典的索引进行提取。但是可变长位置参数不能接受位置参数,否则会报错。
import telnetlib
import timedef telnet_connect(**kwargs):  # **kwargs是一个字典,函数调用时写入的参数名称都会成为字典中的key。ip = kwargs["ip"]  # 将字典中索引“ip”的值放入到ip变量中pwd = kwargs["pwd"]tn = telnetlib.Telnet(ip, port=23)tn.read_until("Password:".encode("utf-8"))tn.write(pwd.encode("utf-8") + "\n".encode("utf-8"))time.sleep(0.5)tn.write(b"display ip int br\n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)telnet_connect(ip="192.168.0.1", pwd="huawei@123")
  • 可变长位置参数+可变长关键字参数
def telnet_connect(*args, **kwargs):  # 同时使用*args可变长位置参数,和**kwargs可变长关键字参数print(args)print(kwargs)telnet_connect("123", ip="192.168.0.1", pwd="huawei@123")

6.2.5 注意事项

1、可变长位置参数与可变长关键字参数混合使用时,一定要确保位置参数在前,关键字参数在后

(*args, **kwargs)

2、普通参数与可变长参数混合使用时,一定要 确保普通参数在前,可变长参数在后

(a1, a2, *args, **kwargs)

3、默认参数与可变长参数混合使用时,一定要保障默认参数在**kwargs之前

(a1=1, *args, a2=1, **kwargs)

6.3 函数的返回值

6.3.1 返回值的基本概念

函数式为了实现某种功能,在执行完函数之后,也要有一个相应的结果。当然这个结果可以包含很多类型,例如一个登录的函数在执行完之后可以返回成功或者失败,例如是一个连接的函数,那么在执行完之后,可以返回一个链接的对象,例如一个认证函数,那么在执行完之后,可以返回认证通过的函数。

6.3.2 返回值的特点

  • 返回值可以是任意类型,如果函数中没有写return,则默认返回None
def func():print("Hello")v1 = func()
print(v1)

Hello
None

  • 在函数中未写返回值或直接return或者return none,执行函数获取的返回值都是none
def func():print("Hello")return Nonev1 = func()
print(v1)

Hello
None

  • return后面的值如果是逗号,则默认将返回值转换为元组再返回。
def func():print("Hello")return 1, 2, 3v1 = func()
print(v1)

Hello
(1, 2, 3)

  • 函数一旦遇到return就会立即退出函数(终止函数中的所有代码)
def func():print("Hello")print("world")return 1, 2, 3print("你好")
print(5)v1 = func()
print(v1)

5
Hello
world
(1, 2, 3)

6.3.3 返回值的应用场景

  • 执行完某个功能后返回我们想要的结果
    • 执行完登录函数返回等于成功还是失败
    • 执行完发邮件函数返回发送成功还是失败
    • 执行完函数返回一个客户端对象
    • 做完数据处理函数之后返回处理后的结果
  • 用于终止函数的执行

6.4 内存地址

6.4.1查看内存地址

name = "张三"
print(id(name))
data_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(id(data_list))def func():passprint(id(func))

2896618084416
2896617787776
2896617988768

6.4.2 函数传递内存地址

  • 函数在进行参数传递是,传递的是内存地址,如下所示:
def func(a1):print(id(a1))v1 = 123
print(id(v1))
func(v1)

140716010592504
140716010592504

  • 传递内存地址的好处

    • 可以节约内存空间

      • 由于传递的时内存空间,因此不需要重新的创建一块内存空间
    • 对于一些可变类型的数据可以通过函数来进行增删改查

    def func(a1):a1.append(666)v1 = []
    func(v1)
    print(v1)
    

    666

  • 深拷贝:通过深拷贝将参数形成一个新的存储空间,但是不是所有的数据都会形成新的存储空间

import copydata_list = [1, 2, 3, [1, 2, 3]]
new_list1 = data_list  # 浅拷贝,拷贝内存地址,对data_list进程操作也会影响new_list1的值
new_list2 = copy.deepcopy(data_list)  # 深拷贝,new_list2的另外生成一个内存地址
print(data_list)
print(id(data_list))
print(new_list1)
print(id(new_list1))
print(new_list2)
print(id(new_list2))

[1, 2, 3, [1, 2, 3]]
1932613767552
[1, 2, 3, [1, 2, 3]]
1932613767552
[1, 2, 3, [1, 2, 3]]
1932616004736

import copydef func(a1):a1.append(666)v1 = [123, "1"]
print(id(v1))
print(v1)
v2 = copy.deepcopy(v1)
func(v2)
print(id(v2))
print(v2)

1964457056448
[123, ‘1’]
1964457390080
[123, ‘1’, 666]

  • 深浅拷贝详解

    1. 深浅拷贝一般说的都是可变类型,例如列表,集合,字典。因为不可变类型在进行深浅拷贝时无意义,内部都不会去拷贝,而是让拷贝前后的数据指向同一块内存地址。
    import copyv1 = "zhangsan"
    print(id(v1))
    print(v1)
    v2 = copy.deepcopy(v1)
    print(id(v2))
    print(v2)
    

    1794713974832
    zhangsan
    1794713974832
    zhangsan

    1. 可变的数据类型在浅拷贝时,拷贝的只是数据类型本身,而对于内部的不可变数据依然采用指向同一块内存地址。
    import copydata_list = [1, 2, 3, [4, 5, 6]]
    new_list = copy.deepcopy(data_list)
    print(data_list)
    print(new_list)
    print(id(data_list))  # 旧列表内存地址
    print(id(new_list))  # 新列表内存地址相比于旧列表内存地址已发生改变
    print(id(data_list[0]))  # 旧列表固定元素
    print(id(new_list[0]))  # 固定元素的内存地址不会改变
    print(id(data_list[3])) 
    print(id(new_list[3]))  # 可变元素的内存地址发生了改变
    

    [1, 2, 3, [4, 5, 6]]
    [1, 2, 3, [4, 5, 6]]
    2404065005952
    2404067898496
    140716109613496
    140716109613496
    2404065007808
    2404067899776

    1. 对于深拷贝而言,针对可变类型,无论在那一层都会深拷贝一份
    import copydata_list = [1, 2, 3, [4, 5, [6]]]
    new_list = copy.deepcopy(data_list)
    print(id(data_list[3][2]))
    print(id(new_list[3][2]))
    

    1903829244096
    1903832187200

    1. 虽然元组时不可变类型,但是元组中如果有可变的数据类型,也会被深拷贝,元组的内存地址也会改变。
    import copydata_list = (1, 2, 3, [4, 5, [6]])
    new_list = copy.deepcopy(data_list)
    print(id(new_list))
    print(id(data_list))
    print(id(data_list[3][2]))
    print(id(new_list[3][2]))
    

    1587521149008
    1587523862208
    1587520854208
    1587523829952

    import copydata_list = (1, 2, 3)  # 当元组中不存在可变元素时,深拷贝后的内存地址不变。
    new_list = copy.deepcopy(data_list)
    print(id(new_list))
    print(id(data_list))
    

    2120299757760
    2120299757760

    1. 对于可变的数据类型,在做深拷贝的时候,无论在那一层,拷贝前后的内存地址都会发生变化,如果对于不可变的数据类型,但是内部存在可变类型的元素,那么在做深拷贝的时候,内存地址也会发生变化。

6.4.3 函数返回内存地址

def func():a1 = 10print(id(a1))return a1v1 = func()
print(v1)
print(id(v1))

140716111842008
10
140716111842008

  • 首先定义一个名为func的函数
  • 通过func执行该函数,在函数内部定义了一个变量a1,指向一块内存地址,此时引用计数器为1,之后将内存地址返回出来复制给v1,此时a1和v1都指向该内存地址,此时引用计数器为2
  • 在函数执行完毕之后,函数内部的变量a1会被释放,所以最中只有v1指向这块内存地址,最终的引用计数器为1

6.4.4 内存驻留机制

>>> v1 = "zhangsan"
>>> v2 = "zhangsan"
>>> v3 = "王十六"
>>> v4 = "王十六"
>>> print(id(v1))
1792624298800
>>> print(id(v2))  #内存地址相同,可能是常用的变量,每次运行结果都是相同的。
1792624298800
>>> print(id(v3))  
1792621930544
>>> print(id(v4))  # 不常用的变量
179262193070

在python中,对于一些使用比较高频的值,事先为这些值预留出一端内存空间,之后当定义变量,只要变量等于这个值,都会直接指向这块内存地址。

6.4.5 默认参数的内存地址

在函数创建时,如果存在默认参数,则在创建函数的过程中就会为该默认参数生成一块内存地址,如果后续没有传参,那么默认就会指向这块内存地址,如果存在传参,那么默认参数会指向新的内存地址

def func(a1, a2=[1, 2, 3]):a2.append(a1)print(a2) 打印a2func(a1=1)  # a2没有传递参数,使用默认参数,a2指向内存地址块1
func(a1=2)  # a2没有传递参数,a2指向内存地址块1
func(a1=3)  # a2没有传递参数,a2指向内存地址块1
func(a1=4, a2=[1, 2, 3])  # a2传递了参数,不使用默认参数,a2指向新的内存地址块2
func(a1=5)  # a2没有传递参数,a2指向内存地址块1

[1, 2, 3, 1]
[1, 2, 3, 1, 2]
[1, 2, 3, 1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 1, 2, 3, 5]

6.5 解包

  • 传递实参的时候,将参数从字典或者元组中取出,并传递给函数,函数再将其重组为元组或者字典。函数内部将参数逐个从元组或字典中取出并传递给函数内的变量执行,该传参过程称为解包。
import telnetlib
import timedef telnet_connect(*args, **kwargs): # *args传递可变长位置参数,**kwargs传递可变长关键字参数print(args)  # args接收的时一个元组print(kwargs)  # kwargs接收的是一个字典ip = args[1]sysname = kwargs["sysname"]tn = telnetlib.Telnet(ip, port=23)print("{}登录成功".format(sysname))tn.read_until(b"Username:")tn.write("python\n".encode("utf-8"))tn.read_until(b"Password:")tn.write("Huawei@123\n".encode("utf-8"))time.sleep(0.5)tn.write(b"no \n")time.sleep(0.5)res = tn.read_very_eager().decode("utf-8")print(res)switch_info1 = [("CE1", "192.168.0.1", "python", "Huawei@123"), ("CE2", "192.168.0.2", "python", "Huawei@123")]
switch_info2 = [{"sysname": "CE1", "ip": "192.168.0.1", "username": "python", "password": "Huawei@123"}]
telnet_connect(*switch_info1[0], **switch_info2[0])  # 将参数从字典或者元组中逐个取出,并传递给函数,函数再将其重组为元组或者字典。将参数逐个从元组或字典中取出并传递给函数的过程称为解包。在位置参数前加“*”,在关键字参数前加“**”

输出结果如下:

('CE1', '192.168.0.1', 'python', 'Huawei@123')
{'sysname': 'CE1', 'ip': '192.168.0.1', 'username': 'python', 'password': 'Huawei@123'}
CE1登录成功Warning: The initial password poses security risks.
The password needs to be changed. Change now? [Y/N]:n
Info: The max number of VTY users is 5, the number of current VTY users online is 1, and total number of terminal users online is 2.The current login time is 2024-03-25 22:44:40.The last login time is 2024-03-25 22:21:11 from 192.168.0.10 through Telnet.
<CE1>

6.6 函数名

  • 函数起始页可以认为是一个变量,那么函数名就相当于是变量名,而函数本身相当于变量值,函数名指向的就是函数本身,因此函数也可以被当做是一个元素
def func():return "哈哈"info_list = ["hello", 123, func, func()]  # 函数可以作为元素
print("type(info_list[2]", type(info_list[2]))  # 2号元素“func”的类型是是函数
print("type(info_list[3]", type(info_list[3]))  # 3号元素“func()”的类型是字符串,时函数的返回值
print("info_list[2]", info_list[2])  # “func”返回的是函数的内存地址,是函数本身
print("info_list[3]", info_list[3])  # “func()”返回的是函数的返回值
v1 = info_list[2]()  # 字符串本身是函数名,后续加上(),则可以作为函数进行执行。
print(v1)

type(info_list[2] <class ‘function’>
type(info_list[3] <class ‘str’>
info_list[2] <function func at 0x0000029129794D60>
info_list[3] 哈哈
哈哈

  • 函数本身是可以被哈希的,因此对于一些必须可哈希的数据类型,也可以将函数作为其中的元素。因此:

    • 函数本身可以作为集合的元素,但是集合元素不能重复
    • 函数本身可以作为字典的key,示例如下:
    def func1():print("Hello")def func2():print("123")d1 = {func1: "函数1", func2: "函数2"}
    print(d1[func1])
    print(d1[func2])
    

    函数1
    函数2

  • 由于函数名的本质,因此在编写程序的时候,需要注意以下几点

    • 定义多个相同函数名,后定义的会覆盖之前定义的
    • 不要定义与python内部函数相同名称的函数
  • 函数也可以被当成是参数传递和返回

def func():print("123")return 123v1 = func()
v2 = 100
v3 = v1 + v2
print(v3)

123
223

相关文章:

  • JS【详解】Map (含Map 和 Object 的区别,Map 的常用 API,Map与Object 的性能对比,Map 的应用场景和不适合的使用场景)
  • mysql 删除重复数据 关联自己 关联子查询 delete
  • 掌握ASPICE标准:汽车软件测试工程师的专业发展路径
  • vue 笔记02
  • C++ | Leetcode C++题解之第117题填充每个节点的下一个右侧节点指针II
  • 大模型中GPTs,Assistants API, 原生API的使用场景?
  • 数据分析中的列与行交换技巧
  • 【Android14 ShellTransitions】(一)开篇
  • 【乐吾乐3D可视化组态编辑器】模型类型与属性
  • IP 分片过程及偏移量计算
  • 多模态大模型:系统、趋势与问题
  • 对于个人而言,大数据时代如何更好地管理自己的信息?
  • Python中使用“import”还是“from ... import”导入模块
  • 双机多网口配置同网段地址,可以通过目的IP确定接收数据的网卡吗?
  • C#算法(15)—求四边形的外接矩形
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • 3.7、@ResponseBody 和 @RestController
  • Bytom交易说明(账户管理模式)
  • eclipse(luna)创建web工程
  • emacs初体验
  • es6--symbol
  • Flannel解读
  • Hibernate【inverse和cascade属性】知识要点
  • input实现文字超出省略号功能
  • Javascript设计模式学习之Observer(观察者)模式
  • JS数组方法汇总
  • Mysql5.6主从复制
  • Promise面试题,控制异步流程
  • Python 反序列化安全问题(二)
  • Python进阶细节
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • 浮动相关
  • 将回调地狱按在地上摩擦的Promise
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • kubernetes资源对象--ingress
  • 从如何停掉 Promise 链说起
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • # 数据结构
  • #WEB前端(HTML属性)
  • $.proxy和$.extend
  • (1)无线电失控保护(二)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第14章泛型第2节(泛型类的类构造函数)
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (SERIES10)DM逻辑备份还原
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (九)信息融合方式简介
  • (六)Flink 窗口计算
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (转)LINQ之路
  • (转)VC++中ondraw在什么时候调用的
  • .Net 6.0 Windows平台如何判断当前电脑是否联网