Python安全开发之函数编程【C08课】

  • A+
所属分类:安全开发

Python安全开发之函数编程【C08课】

  



           



1
课程目标


  1. 掌握函数的定义

  2. 掌握函数的规定语法

  3. 掌握函调用

  4. 掌握函数的参数

  5. 掌握函数的返回值

  6. 掌握异常处理


2
核心知识



2-1


     函数的定义


python的代码运行是从上往下运行,用很长的一段代码来实现一个功能。

如果在另一个位置又需要完成这个功能,不断的复制粘贴代码显然不符合python的编程思想。

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段,还能提高代码的重复利用率。

python自带许多内置函数,比如print()打印函数,str()函数可以将任意对象转换成字符串类型。

下面介绍如何自己完成某一个功能的函数,对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。这也是python开发效率高的一个重要原因。



2-2


    函数的语法

函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。

1def a():
2    print('我是函数A,我开始运行')

如上就是一个函数的固定格式,定义的函数名为a。



2-3


    函数的调用

写完一个函数后,运行代码函数的内容并不会运行,必须要手动调用才会运行,这也方便我们可以在任何位置调用这个函数然后再运行。

 1def a():
2    print('我是函数A,我开始运行')
3
4运行结果>>>我是函数A,我开始运行
5
6def a():
7    print('我是函数A,我开始运行')
8a()
9
10运行结果>>>我是函数A,我开始运行

函数的运行方法即 函数名加上括号即可运行。



2-4


    函数传递参数

Python的函数定义非常简单,但灵活度却非常大。

下面介绍python函数传参,传参指的是将你传入的数据放到函数中执行。

1def a(b):
2    print('我是函数A,我传入的参数是:{}'.format(b))
3b=1
4a(b)
5
6运行结果>>>我是函数A,我传入的参数是:2

非常的灵活与简便。

Python函数支持默认参数,即可以给函数的参数指定默认值。当该参数没有传入相应的值时,该参数就使用默认值。

1def a(b,c=1)
2    print('B的数值为:{}'.format(b))
3    print('C的数值为:{}'.format(c))
4a(2)
5B的数值为:2
6C的数值为:1

当我们调用a(2),就相当于调用a(2,1)。

  1. 设置默认参数时,必选参数在前,默认参数在后,否则Python的解释器会报错;

  2. 定义默认参数要牢记:默认参数必须指向不可变对象!




2-5


    函数返回结果

函数不仅能处理一段业务逻辑,还能返回一个结果,具体语法如下:

1def a(b):
2    return b
3c = a(1)
4print(c)
5
6运行结果>>>1

返回使用的return,上面代码的功能是返回传入的值。

需要注意,return一但返回,这个函数就算运行完毕了,也就是说后面代码都不会继续运行,相当于使用return中断函数。



2-6


    异常的处理

python使用try和except来处理python程序在运行中出现的异常和错误,比如你将数字与字符串相加就会出现异常,那么使用try+except来处理异常

固定的格式如下:

1try:
2    print(100+'学习')
3except:
4    print('代码出错')
5运行结果>>>代码出错

除了出错处理,还要知道出错原因,使用Exception即可获取出错原因

1try:
2    运行代码1
3except Exception as e:
4    print('错误原因为:{}'.format(str(e)))

异常处理常用在网络请求失败,打开文件异常等许多情况下。


3
应用场景


  1. 当你掌握函数后,在你以后几乎所有的代码中,都会使用到函数式编程

  2. 使用函数能将功能块更加独立开来,同时代码更加简介直观,还可以反复多次调用代码块

  3. 举个最小的案例,你在写网络爬虫的时候,可以定义多个函数,分别负责抓数据,洗数据,保存数据的功能,最后调用分配即可



4
总结归纳


  1. 了解函数的定义,即将代码块集成在一个点上,需要的时候直接调用这个点就能调用这个代码块

  2. 函数的固定格式,使用def()开头,加冒号

  3. 函数的调用方法,即当你写好函数后,使用()即可调用运行函数

  4. 函数的参数传递,写函数的时候,提供传递参数的位置,可以让函数功能更加多样化

  5. 函数正常是返回None,如果你使用关键词return返回数据,那么这个函数就可以返回需要的数据,return还能直接中断函数后面的代码运行

  6. 使用try/except做异常处理,当你遇到可能存在异常的时候,比如错误的数据相加,删除不存在的文件,网络请求超时等,使用异常处理就能让你的异常位置得到处理,不会直接报错停止运行代码


5
拓展知识

普通传参的小坑

1def test(a=[]):
2    a.append('A')
3    return a
4print test()
5print test()
6print test()

返回的结果:

1['A']
2['A', 'A']
3['A', 'A', 'A']

原因:传入的参数是一个空的列表,Python遇到函数的时候会把函数读到内存中,默认参数会被创建。虽然函数没有执行,但是其中赋值创建变量,并且创建好了。a始终没有改变,指向的内存方向都是同一个内存。

使用id()可以查看函数内部在内存中的地址。

1def test(a=[]):
2    a.append('A')
3       print(id(a))
4    return a
5print test()
6print test()
7print test()

返回结果:

153854536
2['A']
353854536
4['A', 'A']
553854536
6['A', 'A', 'A']

可以看到指向内存的位置都没变。

这里引申一下Python的可变对象和不可变对象,dict,list是可变对象,str,int,tuple,float是不可变对象。意思就是可变对象的值改变后对应的内存地址没改变。但是不可变对象改变数值后,就会创立一个新的对象并且占用新的内存空间。所以一般如果想要把一个列表的所有元素拼接成一个字符串的话,遍历这个列表没然后使用''.join()方法,节省内存。

如果想要每次都是变成空的列表可以这样写。

1def test_new(a=None):
2    if a is None:
3        a=[]
4    a.append('A')
5    print id(a)
6    return a
7print(test_new())
8print(test_new())
9print(test_new())

记住,对应的内存地址任然是一样的。
返回结果:

151934344
2['A']
351934344
4['A']
551934344
6['A']

动态参数

Python的动态参数有*args与**kwargs。*args表示任何多个无名参数,它是一个tuple。**kwargs表示关键字参数,它是一个dict。

*args的用法

1def fun(*args):
2    for x in args:
3        print(x)
4fun(1,2,3)

返回结果:

11
22
33

当然还可以传入列表,但是传入列表的时候必须要在前面加上*号。比如:

1li=[1,2,3,4,5]
2fun(*li)

返回结果:

11
22
33
44
55

**kwargs的用法

1def fun_test(**kwargs):
2    for x in kwargs:
3        print (x,kwargs[x])
4fun_test(k1=1,k2=[1,2,3])

返回结果:

1k2 [1, 2, 3]
2k1 1

当然还可以传入字典,使用方法和上面一致,也是加上两个*号。

递归定义

递归函数:这个函数在他的内部调用了自身。函数自己调用自己,实现递归。

递归特性:

  1. 记住所有的递归函数都有一个退出条件

  2. 相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)。

  3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

普通算法实现高斯求和:

1def sum(munber):
2    total=0
3    for i in range(1,munber+1):
4        total+=i
5    return total
6print(sum(100)s)

运行结果:

15050

如果使用递归方法:

1def sum_number(number):
2    if number==0:
3        return False
4    return sum_number(number-1)+number
5    # 第一次是sum_number(99)+100的值
6    # 第二次是sum_number(98)+99的值
7    # 最后一个是sum_number(0)
8print(sum_number(100))

运行结果:

15050

递归小坑

在写百度子域名采集的时候,因为百度经常会返回一些失败的页面,所以需要对访问百度的函数进行递归操作,直到返回正确的页面。但是发现在递归函数得到正确页面,返回的结果却是None,找了一下资料,才明白递归的本质原理是酱紫的:

主要的原因是函数调用中的堆栈进出顺序,比如:

 1运行递归函数1
2函数1不是想要的结果...
3运行递归函数2
4函数2不是想要的结果....
5运行递归函数3
6递归函数3是想要的结果...
7准备返回函数3的结果
8函数3的结果返回到函数2
9函数2没有想要的结果....
10返回None

解决方法是返回递归函数的返回结果

1即在函数1不是想要的结果.....
2执行函数2的时候.....
3返回函数1

用代码写出来就是

1def test(x):
2    if x<10:
3        x+=1
4        test(x)
5    else:
6        return x

改成

1def test(x):
2    if x<10:
3        x+=1
4        return test(x)
5    else:
6        return x


往期推荐

Python实现404页面识别实践

Python常用技巧整理

手把手教你用python3打造一个内网资产扫描器

Python识别CMS与批量资产收集拓展

Sqlmap速查表/功能移植/Python批量检测SQL注入

Nmap/Masscan/Python/端口服务检测/速查表



本文始发于微信公众号(安全研发):Python安全开发之函数编程【C08课】

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: