(5.1)Python的模块化开发

Python正式实习的第二天学习内容较多,故发多篇Blog进行记录。
和大多数高级程序语言一样,Python也鼓励程序员使用模块化开发,即对于函数的使用。
一、内建函数
Python也内建了许多函数可供调用,例如求绝对值函数与强制数据类型转换函数

abs(-5)
str(100

二、定义函数的规则
与C++不同,在Python中自定义一个函数有以下规则。
1、定义函数前无需声明返回值,而是统一使用def语句起头,再依次写出函数名、括号、括号中参数、括号与冒号:
2、不使用大括号定义函数体的范围,而是在缩进块中编写函数体。
3、函数内可以使用return语句返回值,如果在函数运行完后仍未出现return语句,则会自动调用return None

def my_abs(x):
    if x>=0: 
        return x
    else:
        return -x

在调试中,如果某个函数暂时没有想好怎么写,可以先用空函数代替,不可直接空着下一行。

def kksk(x):
    pass

与C++类似,Python在调用函数的时候会首先检查参数,如果未找到合适的重载函数,则会抛出异常。
然而,Python无法检查所传入的参数是否符合类型,因此需要在函数体内进行合法参数类型的判断。

def my_abs(x):
    if not isinstance(x, (int, float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

与C++不同,Python允许函数返回多个值,不过其实现方式为返回一个tuple,在tuple中储存需要返回的多个值。

import math#默认参数
def move(x,y,step,angle=0):
    nx=x+step*math.cos(angle)
    ny=y-step*math.sin(angle)
    return [nx,ny]
move(1,5,1)

输出[2.0, 5.0]
三、函数的参数
Python中函数的参数灵活性非常大,共有位置参数、默认参数、可变参数、关键字参数、命名关键字参数几大类。
位置参数即为最基本的参数类型,函数在定义的时候确定传入参数的数据类型、数量与顺序,在调用的时候依次对应,将变量传给函数体内的形参。如果数量或者顺序不对应则会报错。

def test_func(a,b):
    (x,y,z)=a
    print(x,y,z,b)
test_func((2,3,4),9)

和C++相同,Python允许在定义阶段对参数取默认值,在调用的时候如果没有提供该参数将会使用默认参数,否则使用调用时的参数。

def test_func(a,b=5):
    (x,y,z)=a
    print(x,y,z,b)
test_func((2,3,4))

同样的,默认参数只允许从右往左设置,这点和C++相同。

Python允许传入可变数量的参数,即可变参数。

def calc(*numbers):
    sum=0
    for n in numbers:
        sum=sum+n*n
    return sum
calc(1,2,3,4)

这样在进行处理的时候,函数将把调用函数的若干个参数作为一个tuple处理。
如果已有list或者tuple,在调用的时候加上*即可

nums = [1, 2, 3]
calc(*nums)

关键字参数允许函数在传入必选参数之外额外传入一个dict。

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
person('Bob', 35, city='Beijing')
person('Adam', 45, gender='M', job='Engineer')

输出为

name: Bob age: 35 other: {'city': 'Beijing'}
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

需要注意的是,如果将dict作为参数传入函数,那么在函数内进行处理的时候,并不会影响函数外的dict。这点类似于C++中的形参与实参,实际传入的dict为原dict的一份拷贝。
命名关键字参数与关键字参数相似,其允许向函数传入一个dict,并且其只接受特定的键值。

def person(name, age, *, city, job):
    print(name, age, city, job)
person('Jack', 24, city='Beijing', job='Engineer')

如果在函数的定义中已经有了一个可变参数,那么命名关键字参数不再需要使用*分割,直接写即可。

def person(name, age, *args, city, job):
	print(name, age, args, city, job)

命名关键字参数允许进行初始化。

def person(name, age, *, city='Beijing', job):
	print(name, age, args, city, job)

因为Python拥有如此多的参数类型,因此参数的顺序有着严格的要求。
参数定义的顺序必须为:必选参数(位置参数)、默认参数、可变参数、命名关键字参数、关键字参数。

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

四、其他特殊函数
Python中的函数允许递归,并且同其他语言一样,递归次数如果过多将会导致栈溢出。

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)
fact(2000)

map接受一个函数与一个Iterable作为参数。
map将把Iterable中的每个元素传入函数,把所得到的返回值组成一个新的Iterable并返回。
这个传入的函数需要接受一个参数。

def f(x):
    return x*x
r=map(f,[1,2,3,4,5,6,7,8,9,10])
list(r)

reduce与map类似,接受一个函数与一个Iterable作为参数。
reduce将把Iterable中的前两个元素传入函数,并将得到的返回值与下一个元素传入函数,以此类推,最终返回一个值。
这个传入的函数需要接受两个参数。

from functools import reduce
def add(x,y):
    return x+y

reduce(add,[1,2,3,4,5])

filter函数接受一个函数与一个iterable作为参数,这个传入的函数接受一个参数,并返回一个bool值。fiter将把iterable的每个元素依次带入函数,如果返回值为真则将其抛弃。最终返回一个经过筛选后的iterable。

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))

sorted可以对所有的iterable进行排序,其接受四个参数,分别为iterable、比较函数、比较键值、排序规则。
在使用比较键值的时候,一般用虚拟函数来获得。

result2=sorted(result,key=lambda x:x.num)

sorted与sort函数不同,sorted不改变原排序序列的顺序,而是返回一个排序后的序列。

Python当中类似于C++中函数指针的操作为返回函数,即直接把函数作为返回值返回。

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
lazy_sum(1,2,3,4,5)()

注意最后一行,直接写lazy_sum(1,2,3,4,5)是不可行的,这样只会得到一个用于求和的函数,即sum,需要再进行一次调用函数。

返回函数的时候并不会调用函数内的内容,只有当调用的时候才会。因此下面的写法是错误的。

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3= count()
print(f1(),f2(),f3())

应该改成

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs
f1,f2,f3=count()
print(f1(),f2(),f3())

对于一些较为简单的函数,Python提供了一种快速定义的方法,即匿名函数lambda。
匿名函数只能有一个表达式,表达式的结果即为返回值,例如上文中的sorted函数。

result2=sorted(result,key=lambda x:x.num)

所用判断键值即为匿名函数,返回x这个类中num的数值。

五、装饰器
装饰器的本质为一个python函数,它的返回值也为一个函数对象,主要作用是为函数添加额外的功能。在这里只简单整理一下普通装饰器的用法。

我们定义一个用于打印文本的装饰器

def school(func):
    def s_name():
        print("Test123")
        return func()
    return s_name

这里使用了返回函数。我们使用装饰器,在school的基础上进行修改。

@school
def school2():
    print("Test456")
school2()

所输出为

Test123
Test456

这种装饰器的写法和下面这种使用返回函数是一样的。

def school3():
    print("Test456")
school(school3)()

可以看到,在添加了@school后,所定义的函数相当于把该函数作为school函数的参数传入。

对于装饰器更深层次的理解,可以看这一篇文章辅助掌握。
知乎回答:如何理解Python装饰器?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值