Python学习笔记Chaper2

Python学习笔记Chaper2

一级目录

二级目录

三级目录

2.1函数

如果经学习过其他的高级语言,如C语言,java等,函数的概念不再赘述。

python中的函数和C中的函数概念相当,和java中的方法概念相当。

2.1.1调用函数

python有强大的函数库,可以供我们进行调用。

要调用一个函数,就要知道函数的名称和参数。在python交互环境中输入help(functionName)回车,即可知道该函数的使用方法,这一点类似于Matlab的语法,以abs函数为例:

image-20210121115223031

在进行函数调用时,若函数的参数不正确,就会报TypeError的错误。函数的参数不正确包括:

  • 参数类型错误
  • 参数数量错误

在上一章的再议input中,提到要将input()的字符串类型转换成int()类型才能进行后续计算,调用的就是int方法,和这里是一样的。

2.1.2定义函数

2.1.2.1函数的定义方法

在python中,定义一个函数要使用def语句,再依次写出函数名,括号,括号中的参数和冒号

如:

#记住python中的函数调用格式:
def myAbs(a):
    if a>=0:
        return a
    else:
        return -a
print(myAbs(-102))

在Python交互环境中定义函数时,注意Python会出现...的提示。函数定义结束后需要按两次回车重新回到>>>提示符下:

┌────────────────────────────────────────────────────────┐
│Command Prompt - python                           - □ x │
├────────────────────────────────────────────────────────┤
│>>> def my_abs(x):                                      │
│...     if x >= 0:                                      │
│...         return x                                    │
│...     else:                                           │
│...         return -x                                   │
│...                                                     │
│>>> my_abs(-9)                                          │
│9                                                       │
│>>> _                                                   │
│                                                        │
│                                                        │
└────────────────────────────────────────────────────────┘
2.1.2.2空函数的使用

如果想定义一个什么事也不做的空函数,可以用pass语句:

def nop():
    pass

pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。

pass还可以用在其他语句里,比如:

if age >= 18:
    pass

缺少了pass,代码运行就会有语法错误。

2.1.2.3参数类型的检查

调用函数时,如果参数个数不对,Python解释器会自动检查出来,并抛出TypeError

>>> my_abs(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: my_abs() takes 1 positional argument but 2 were given

让我们修改一下my_abs的定义,对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()实现:

#记住python中的函数调用格式:
def myAbs(a):
    if not isinstance(a,(int,float)):
        raise TypeError('bad operand type')
    if a>=0:
        return a
    else:
        return -a
print(myAbs(-102))
2.1.2.4python和其他语言的函数定义对比
  • java

    java中定义方法:

    /*[修饰符列表] 返回值类型 方法名 (形式参数列表){
    	方法体
    } 
    如:
    */
    public static void main(String[] args){
    	System.out.println("mian方法!")
    }
    

    上述的isinstance()函数就类似于java中的instanceof

  • C语言

    c语言中定义函数

    //返回值类型 函数名 (形式参数列表)
    int main(){
    	return xxx
    }
    

    可以看出python的函数定义和C语言的函数定义基本相同,只是要增加一个def关键字和冒号

    学习过C语言可以知道,C语言的return语句只能返回一个参数,但是python不同

    python的return语句一次可以返回多个值

    import math
    # return语句可以返回多个参数
    def move(x, y, step, angle=0):
      nx = x + step * math.cos(angle)
      ny = y - step * math.sin(angle)
      return nx, ny
    

    import math语句表示导入math包,并允许后续代码引用math包里的sincos等函数

    但实际上我们要知道,python的return实际上也只是返回了一个值:实际返回的是一个tuple元组。这和matlab的函数返回有些相似。

    import math
    def quadratic(a, b, c):
        x1=(-b+math.sqrt(b**2-4*a*c))/(2*a)
        x2=(-b-math.sqrt(b**2-4*a*c))/(2*a)
        return x1,x2
    print(quadratic(2,3,1))
    

    通过这个例子,想要表述的是了解python中的乘方应该如何表示,一开始使用了matlab中的写法,运行期间报错,正确的写法应为:

    n**m
    #表示n的m次方
    

2.1.3函数的参数

2.1.3.1默认参数

函数的参数数量和类型要正确,否则会报错,这一点我们不再赘述。这里想要说的是关于python的默认参数。其实在Matlab中也有默认参数,只不过要使用varargin参数表的形式。

回到正题:python的默认参数

假设我们编写了下面这样的程序:

def myPow(x,n):
    result=x
    while n>1:
        result=result*x
        n=n-1
    return result
print(myPow(5,3))

参数输入5和3时,程序是完全没有问题的,但是如果我们只输入5,根据上面参数错误的情况描述,会报错。考虑到我们经常要用到的是平方运算,我们可以进行这样的修改:当只输入一个参数时,默认进行平方运算,能否做到呢?这时候就需要使用默认参数了。

def myPow(x,n=2):
    result=x
    while n>1:
        result=result*x
        n=n-1
    return result
print(myPow(5))
print(myPow(5,3))

运行结果如下:

25
125

程序运行正常,这就是默认参数的用法。特别要注意以下几点:

  • 一是必选参数在前,默认参数在后,否则python的解释器会报错
  • 二是如何进行默认参数的选择,遵循下面的原则:
    • 变化大的参数作为必选参数
    • 变化小的参数作为默认参数

默认参数的好处:降低调用函数的难度

举个例子,我们写个一年级小学生注册的函数,需要传入namegender两个参数:

def enroll(name, gender):
    print('name:', name)
    print('gender:', gender)

这样,调用enroll()函数只需要传入两个参数:

>>> enroll('Sarah', 'F')
name: Sarah
gender: F

如果要继续传入年龄、城市等信息怎么办?这样会使得调用函数的复杂度大大增加。

我们可以把年龄和城市设为默认参数:

def enroll(name, gender, age=6, city='Beijing'):
    print('name:', name)
    print('gender:', gender)
    print('age:', age)
    print('city:', city)

这样,大多数学生注册时不需要提供年龄和城市,只提供必须的两个参数:

>>> enroll('Sarah', 'F')
name: Sarah
gender: F
age: 6
city: Beijing

只有与默认参数不符的学生才需要提供额外的信息:

enroll('Bob', 'M', 7)
enroll('Adam', 'M', city='Tianjin')

可见,默认参数降低了函数调用的难度,而一旦需要更复杂的调用时,又可以传递更多的参数来实现。无论是简单调用还是复杂调用,函数只需要定义一个。

def informationGet(name,grade,gender,age=6,city="黄山"):
    print("name:",name)
    print("grade:",grade)
    print("gender:",gender)
    print("age:",age)
    print("city:",city)
informationGet("张丽丽","大二","女","19")
informationGet("李华","一年级","男")

默认参数“坑”:

我们编写下面的代码:

def add_end(L=[]):
    L.append('END')
    return L
print(add_end([1,2,3]))
print(add_end([2,3,4,5]))
print(add_end(["猫在走猫步"]))
print(add_end(["鸟儿在飞翔","风筝在飞"]))
print(add_end())
print(add_end())
print(add_end())

运行结果如下:

image-20210121162039874

可以看出在不使用默认参数的情况下,我们如何调用都不会有问题,输出永远正确。

但是使用默认参数的情况下,似乎程序记住了’END’。原因如下:

Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了默认参数L的内容,则下次调用时,默认参数的内容就会上次改变的那个结果了,不再是函数定义时的[]了。

定义默认参数要牢记一点:默认参数必须指向不变对象!

修改如下:

#默认参数始终要指向  不变对象!!!!
def add_end(L=None):
    if L is None:
        L=[]
    L.append('END')
    return L
print(add_end([1,2,3]))
print(add_end([2,3,4,5]))
print(add_end(["猫在走猫步"]))
print(add_end(["鸟儿在飞翔","风筝在飞"]))
print(add_end())
print(add_end())
print(add_end())

为什么要设计strNone这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

2.1.3.2可变参数

可变参数意为参数的数量是可变的,可以是0,1,2,…个

给出例子:要求求出任意一串数据的和: a 2 + b 2 + . . . . + n 2 a^2+b^2+....+n^2 a2+b2+....+n2

要定义这个函数,必须要知道数据的个数,我们可以先把数据封装进一个list或者tuple,再从list或者tuple中提取数据进行计算:

def cal(numbers):
    sum=0
    for x in numbers:
        sum=sum+x**2
    return sum
print(cal([1,2,3,4,5]))

但是我们调用的时候必须组装出一个list或者tuple,即第六行cal([1,2,3,4,5])的步骤,但是如果使用可变参数的话,代码可以化简为:

#使用可变参数的求平方和
def cal2(*numbers):
    sum=0
    for x in numbers:
        sum=sum+x**2
    return sum
print(cal2(1,2,3,4,5))

但是如果已经有组装好的list或者tuple呢?我们直接将该list或者tuple传入,在list或者tuple名前加上*即可,此时表示接受到了一个tuple,代码如下:

#如果已经够造出一个list或者tuple,且要使用可变参数的话:
nums=[1,2,3,4,5]
print(cal2(*nums))
#直接采取在前面加上*号的方式即可
2.1.3.3关键字参数

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple

关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict

编写下面的代码:

#函数的关键字参数
#允许传入0个或任意个参数,这些参数在内部自动组成一个dict
def infoInput(name,age,**kw):
    print("name:",name)
    print("age:",age)
    print("others:",kw)
#特别注意这里的键值对的组成方法,要使用key=value的方式:
infoInput("Judy",17,school="西电")

关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到nameage这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

同时,和可变参数类似的是,我们也可以先组装出来一个dict,然后传进去,也要注意要使用key=value的方式。这里就要知道如何根据key寻找value了,直接使用索引,即key值放在方括号中即可,和赋值式子类似,直接返回value值。代码如下:

infoDict={"居住地":"黄山","性别":"女"}
infoInput("Judy",19,location=infoDict["居住地"],gender=infoDict["性别"])

上面写法若是键值对过多的话就变得有些复杂了,可以改成下面的简化版:

#键值对较多的情况下,下面的写法更方便:
extra={"居住地":"黄山","性别":"男","婚姻状态":"已婚","学历":"本科"}
infoInput("Mike",21,**extra)

代码运行结果:

name: Judy
age: 19
others: {'location': '黄山', 'gender': '女'}
name: Mike
age: 21
others: {'居住地': '黄山', '性别': '男', '婚姻状态': '已婚', '学历': '本科'}

**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra

2.1.3.4命名关键字参数

上面一小节讲述了关键字参数:允许传入任意个参数,在内部在动组装成一个dict。但是究竟传入了哪些,我们要进行检查:

def person(name,age,**kw):
    if 'city' in kw:
        #含有city参数
        pass
    if 'job' in kw:
        #含有job参数
        pass
    print("name:",name,"age:",age,"others:",kw)
person("WangEr",12,**{'city':'BeiJing','job':'student'})

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收cityjob作为关键字参数。这种方式定义的函数如下:

#关键字命名参数
#如果要限制只能接受job和city的参数,那么代码修改为:
def person(name,age,*,city,job):
    print("name:",name,"age:",age,"city:",city,"job:",job)
person("WangEr",12,**{'city':'BeiJing','job':'student'})

运行结果如下:

name: WangEr age: 12 city: BeiJing job: student

此时如果传入过多的参数,在编译阶段就会报错:got an unexpected keyword ‘birth’

image-20210121175208144

可以看出和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。

调用方式如下:

person("Amily",20,city="BeiJing",job="student")

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, *args, city, job):
    print(name, age, args, city, job)
2.1.3.5参数组合
#参数组合
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)
f1(1,2,3,*[1,2,3],**{"Mike":12,"Judy":18})
f2(1,3,d="fw",**{"Mike":5})#省略参数c

2.1.3递归函数

python的递归函数和C语言基本一样,不予赘述,给出下面的例子:

#编写一个递归函数
def fact(n):
    if n == 1:
        return 1
    else:
        return n*fact(n-1)
print(fact(5))
#参数组合
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)
f1(1,2,3,*[1,2,3],**{"Mike":12,"Judy":18})
f2(1,3,d="fw",**{"Mike":5})#省略参数c

2.1.3递归函数

python的递归函数和C语言基本一样,不予赘述,给出下面的例子:

#编写一个递归函数
def fact(n):
    if n == 1:
        return 1
    else:
        return n*fact(n-1)
print(fact(5))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blanche117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值