当前位置:博客首页 > Python > 正文

第10课 python函数式编程(lambda、items、sorted、filter、isinstance)

作者: Jarvan 分类: Python 发布时间: 2019-04-20 09:45 百度已收录

一、函数概念和调用

1.1 概念

在程序设计中,函数是指用于进行某种计算的一系列语句的有名称的组合。定义一个函数时,需要指定函数的名称并写下一系列程序语句。之后,就可以用名称来“调用”这个函数。

1.2 调用

函数名加括号就可以调用函数,如果函数需要传递参数,那么就传入对应的参数即可。

二、定义函数(创建函数)

2.1 定义函数

def hello(): # foo是函数的名称
    print("hello world")
hello()

函数的命名规范跟变量的命名规范一样,开始只能是字母或下划线,后面可以是数字、字母和下划线。注意的是函数名称不要跟系统的关键字以及自带的内建函数重名。

2.2 参数和返回值

函数的参数就是一个变量名,这个变量名可以在函数的内部使用,它的值是从外面传入进来的。定义函数时的变量名一般称为形参,调用函数时传入的值叫实参。

def hello(name):
    print("hello {}".format(name))
# 上面的那么为形参
hello("Jarvan")
# 此处的Jarvan为实参

函数的参数可以有很多个,传入函数参数的数量一定要跟定义函数时一致,顺序也是要对应,如果顺序不对应,那么可能得到的结果就不是预期的那样了

def jisuan(start, end):
    num_list = range(start, end)
    total = 0
    for i in num_list:
        total += i
    print("从{}加到{}={}".format(start, end-1, total))
jisuan(1,101)
# 计算1加到100

函数的参数可以给定默认值,给有默认值的参数必放在没有默认值参数列表的后面。如果有默认值,那么在函数调用的时候可以不填,不填的话就使用默认的值

def add(start, end, step=2): # 有参数没有返回值
    num_list = range(start, end, step)
    total = 0
    for i in num_list:
        total += i
    print("从%d加到%d=%d" % (start, end, total))
add(1,101)
# 因为step为默认值,所以此处不填也可以

# 输出结果为2500

使用return语句返回内容到函数外,return语句可以返回所有的数据类型,可以返回一个或多个。如果没有写return语句,函数的默认返回值是None

def myname(): # 没有参数,有返回值
    return "Jarvan"

def add(a, b): # 有参数有返回值
# 返回a+b的值
    return a + b

def append(a, b): # 有参数有返回值
    a.append(b)
    # 将b添加到a中,然后返回a
    return a

def sizeyunsuan(a, b): # 有参数有返回值
    add = a + b
    sub = a - b
    mux = a * b
    div = a / b
    # 这里看起来时返回了多个数值,但其实是将这些返回值组成了一个元组进行返回的
    return add, sub, mux, div

关键字参数和非关键字参数:

在函数声明的时候,如果我们给定了参数一个默认值,那么这个参数也称为关键字参数,同时我们在调用函数的时候也可以通过变量名来给对应的参数赋值,不管这个参数是否有默认值。但是需要注意的是,没有指定变量名的参数必需是在前面的
在函数声明和调用时没有指定变量名的参数就是非关键字参数

def add(start, end, step=1): # 有参数没有返回值
    num_list = range(start, end, step)
    total = 0
    for i in num_list:
        total += i
    print("从%d加到%d=%d" % (start, end, total))
add(1,10,step=2)
# 传入的参数1,10因为没有指定变量名的参数,所以它们是非关键字参数。
# 而step我们给了它一个默认值,所以它是关键字参数

接收可变长参数

当我们不能确定用户到底输入多少个参数的时候,那么我们就不能在函数定义的时候确定参数的个数,这样的话就不能很好的处理用户的输入。python提供了一种很好的办法来解决这个问题。使用arg来接收多个非关键字参数,用*kargs来接收多个关键字参数。

在Python中,有两种可变长常数可选,分别是:

  • 元组变长参数,参数名前面有一个星号
  • 字典变长参数,参数名前面有两个星号

元组可变长参数

def sum3(*nums):
    print('nums: ',nums)

    sum = 0
    for num in nums:
        sum += num

    print('sum = ',sum)
    
a = 11
b = 22
c = 33
sum3(a,b,c)

字典可变长参数

def sum4(a=0, **nums):
    print('nums: ', nums)

    sum = a
    for key in nums:
        sum += nums[key]

    print('sum = ', sum)

sum4(a=11, b=22, c=33)

# 如果我们按照元组可变长参数的方式给sum4传递参数会发生什么呢?比如我们a=11, b=22, c=33,然后sum4(a,b,c)则会报错

Python可变长参数

参考地址: https://www.jianshu.com/p/fe0eb1e90bb4

2.3 函数的四种类型

没有参数,没有返回值
没有参数,有返回值
有参数,没有返回值
有参数,有返回值

三、匿名函数lambda

当我们需要实现一些很小的功能的时候,如果使用def声明一个函数就会显得比较复杂了,这种情况下就可以使用lambda来为我们创建一些小的功能函数

lambda语法:

lambda [参数列表]: 表达式
说明:参数列表是可选的,就是说可以不填写参数,表达式就是函数的返回值,一般如果有参数的话,那么表达式中也会用到参数。表达式的结果就是函数的返回值。

s = lambda : True 
# 返回True

s1 = lambda x, y: x + y 
# 返回x+y的值

l1 = list(filter(lambda x : True if x % 3 ==0 else False,range(1,101)))
# 计算100以内3的倍数,用到了匿名函数lambda()、filter()函数、以及三元操作符

def comp(a,b):
    z = a if a > b else b
    print(z)
comp(2,4)
# 函数与三元操作符结合举例

d1 = {'a': 10, 'b': 23, 'c':3, 'd': 9}
dict1 = sorted(d1.items(),key = lambda m:m[1],reverse=True)
print(dict1)
# 涉及到了items()用法,Python 字典 items() 方法以列表返回可遍历的(键, 值) 元组数组。

# 以下为items()方法的用法
d1 = {'a': 10, 'b': 23, 'c':3, 'd': 9}
for k,v in d1.items():
    print(k,":",v)
# 以上输出结果为
#a : 10
#b : 23
#c : 3
#d : 9

Python filter() 函数

filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

扩展阅读 : http://www.runoob.com/python/python-func-filter.html

Python3 字典 items() 方法


Python 字典 items() 方法以列表返回可遍历的(键, 值) 元组数组。 语法:dict.items()

https://www.runoob.com/python3/python3-att-dictionary-items.html

四、函数里面定义函数

python允许函数里面再定义函数,在函数里面定义的函数只能在函数体里面使用,而不能在外面使用, 要想在外面使用里面定义的函数,那么必须把里面的函数返回出去才能使用

def func():
    def add(x, y):
        return x + y
    return add
add = func()
print(add(2, 4))
# 因为add()函数在func()函数里面,所以需要return返回出来,才能使/用

五、变量作用域

变量的作用域是定义为其声明在程序里的可用范围,就是我们所说的变量的可见性,也就是说这个变量在哪里可以用,哪里不能用。

python变量的作用域主要有两种:全局作用域和局部作用域,拥有全局作用域的变量又叫全局变量,拥有局部作用域的变量叫局部变量。

python变量的作用域是对于单个python文件而言的,因为当涉及到多个文件或者模块的时候,需要引入另一个概念“命名空间”。(讲到python模块和包的时候再讲)

一般的,在单个python文件里,如果变量声明在函数体之外的,都是全局变量,全局变量的作用域在声明它的那一行开始,一直到文件的结束。如果是在函数体内声明的变量叫局部变量,局部变量只能在函数体内使用。

如果函数体内的局部变量名称与全局变量的名称相同,那么局部变量将会覆盖全局变量,也就是说会优先使用局部的,而不会使用全局的

# 全局变量a
a = 10
def foo():
#局部变量a
    a = 8
    b = 12
    print(a + b)
foo()
#此处输出的是全局变量a
print(a)

循环语句和条件判断语句中定义的变量也是全局变量, 但是不建议这么做,在循环语句和条件判断语句外使用在其中定义的变量会报一个警告信息(变量可能未定义就被引用,主要原因在于条件语句和循环语句都是需要满足一定的前提下才会执行的,因此就会存在条件不满足的情况,所以才会报警告)。

a = 0
while a < 10:
    a += 1
    b = 20
for x in range(3):
    y = x + 1
if a > 0:
    num = 100 # 如果num在else语句中也定义了,那么就没事
else:
    # num = 10
    snum = 100
print(a)
print(b)
print(x)
print(y)
print(num)
print(snum)

# 以上输出结果其他正常,但是变量snum会报错:NameError: name 'snum' is not defined 原因是if语句中else语句未满足,所以snum未定义就被引用了,因此而报错

要想在函数内明确使用的是全局变量而不是重新定义个局部变量,那么就需要使用global关键字声明函数内的这个变量是全局变量不是局部变量。 global语句语法: global 变量名 变量名可以是一个或多个,每个名字直接用逗号隔开。

a = 10
b = 20
def foo():
    global a,b
    a = 100
    b = 40
def func():
    global a, b
    a += 1
    b += a
    return b
foo()
print(func())

#输出结果为141

六、闭包

如果在一个内部函数里,对外在作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量。

def foo():
    x = [0]
    def fun():
        x[0] += 1
        return x[0]
    return fun

add_one = foo()
print(add_one())
print(add_one())
print(add_one())
print(add_one())
print(add_one())

# 输出结果为1,2,3,4,5

闭包在实际的运用比较少,这里大家只是作为了解就可以了。如果还不理解的也没有关系,先把这个知识点放下,等自己的经验积累到一定程度之后再回头来进行理解。

七、递归

递归就是函数直接或间接地调用自身以进行循环的函数。递归是颇为高级的话题,并且在python中相对少见,但是它是一项应该了解的有用的技术,而且在我们的爬虫开发中还是经常用到的。 递归案例:计算阶乘 一个数的阶乘等于它和之前所有数的乘积,比如3的阶乘等于3*2*1

def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n-1)
print(factorial(5))

 #执行流程
 #5 * factorial(4)
 #4 * factorial(3)
 #3 * factorial(2)
 #2 * factorial(1)
 #1
 #开始返回
 #1
 #1 * 2
 #1 * 2 * 3
 #1 * 2 * 3 * 4
 #1 * 2 * 3 * 4 * 5

但是用递归来实现阶乘,其实并不是一个很好的方法,因为完全可以用循环来实现。用递归实现只是为了说明递归的原理。

def foo(n):
    result = 1
    while n > 1:
        result *= n
        n -=1
    return result
print(foo(5))

递归在爬虫中的应用,在爬虫中无法避免的就是进行网络请求,那么网络请求无法避免的就是可能会有各种请求失败问题,那么对于请求失败的话我们需要进行重新请求,如果使用while循环的话,代码看起来就没那么友好了。所以一般情况下都是使用递归的方式进行的。下面是一个简单的例子:

import urllib2
    def get_html(url, retries=5):
        try:
            req = urllib2.urlopen(url, timeout=5)
        except urllib2.HTTPError:
            html = None
            if retries > 0:
                return get_html(url, retries - 1)
        else:
            html = req.read()
        return html
from urllib import request
def download(url,encoding='utf-8'):
    html= request.urlopen(url).read()
    return html.decode(encoding)

# 这句语句的意思是只有直接运行该py文件时,才执行下面的if语句
if __name__ == '__main__':
    curl = "https://ask.seowhy.com/question/68697"
    source = download(curl)
    print(source)
# 封装一个简单的网页爬虫

八、生成器

python生成器是一个带yield语句的函数,return语句只返回一次,但是一个生成器能暂停执行并返回一个中间结果–那就是yield语句的功能,返回一个值给调用者并暂停执行。当调用生成器的next()方法时,它会准确的从离开的地方继续。 当函数没有更多的返回值时就会抛出StopIteration异常。

def myrange(n):
    x = 0
    while n > 0:
        n -= 1
        yield x
        x += 1
f = myrange(5)
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
# 输出结果为0,1,2,3,4,以及StopIteration

扩展阅读:Python中yield的理解与使用

https://blog.csdn.net/qq_33472765/article/details/80839417

作业

作业1:熟悉python函数的各种声明方法以及函数的调用

已练习

作业2:熟悉函数关键字参数和非关键字参数的区别并知道怎么使用

已练习

作业3:熟悉python变量作用域,并知道global关键字的作用和用法。

已练习

作业4:熟悉递归函数的使用,并使用递归的方法实现一个计算N层汉诺塔移动一共需要多少步的函数。

汉诺塔相关资料:

https://baike.baidu.com/item/%E6%B1%89%E8%AF%BA%E5%A1%94/3468295

汉诺塔小游戏:

http://www.4399.com/flash/109504_1.htm
def hannuota(n):
    if n == 1:
        return 1
    else:
        result = 2**n - 1
        return result
print(hannuota(2))
# 方法一

def hannuota(n):
     if isinstance(n, int) and n>=1:
         if n==1:
             return 1
         return 2*hannuota(n-1)+1
     return -1 #如果不符合条件则提示一下

if __name__ == '__main__':
    ret = hannuota(5)
    print(ret)
# 方法二 递归

语法:isinstance(object,type)

作用:来判断一个对象是否是一个已知的类型。

作业5:学习正则表达式语法,熟悉正则表达式的元字符代表的含义,并能自己书写简单的正则表达式。

发表评论