第十章、Python函数基础

时间:2022-09-08 11:25:24

第十章、Python函数基础

一、函数的基础概念:

  • 函数是python为了代码最大程度地重用和最小化代码冗余而提供的基本结构

  • 函数是一种设计工具,它能让程序员将复杂的系统分解为可管理的部件

  • 函数用于将相关功能打包并参数化

  • 在python中可以创建4种函数

    • (1)全局函数:定义在模块

    • (2)局部函数:嵌套于其它函数中

    • (3)lambda函数:表达式,如需多次调用

    • (4)方法:与特定数据类型关联的函数,并且只能与数据类型关联一起使用

  • python提供了很多内置函数

二、创建函数:

  def functionName(arg1,arg2,...):
    suite
     return 
  def 函数名(参数):
      ...
      函数体
      ...
      返回值

函数的定义主要有如下要点:

  • def:表示函数的关键字

  • 函数名:函数的名称,日后根据函数名调用函数

  • 函数体:函数中进行一系列的逻辑计算,如:发送邮件、计算出 [11,22,38,888,2]中的最大数等...

  • 参数:为函数体提供数据

  • 返回值:当函数执行完毕后,可以给调用者返回数据。

一些相关的概念:

  • def是一个可执行语句

    • 因此可以出现在任何能够使用语句的地方,甚至可以嵌套于其他语句,例如if或while中

  • def创建了一个对象并将其赋值给一个变量名(即函数名),函数体则存在内存

  • return用于返回结果对象,其为可选;无return语句的函数自动返回None对象

    • 返回多个值时,彼此间使用逗号分隔,且组合为元组形式返回一个对象

    • 函数一旦执行到return,函数就终止了,如果return下面还有执行语句则终止

  • def语句运行之后,可以在程序中通过函数后附加()进行调用

三、函数作用域:

python创建、改变或查找变量名都是在名称空间中进行,在代码中变量名被赋值的位置决定了其能被访问到的范围,函数定义了本地作用域,而模块定义了全局作用域

  • 1.全局变量全部用大写表示

  • 2.全局变量都可以被访问,函数内部的变量则为本地作用域

  • 3.在函数内如果要修改全局变量,需要global

  • 4.特殊:字典,列表可以在函数内修改,但是不能重新赋值

  NAME = 'tomcat'def f1():
      age = 18
      global NAME
      NAME = 'xiao'
      print(age,NAME)
  ​
  def f2():
      age = 27
      print(age,NAME)
  ​
  f1()
  f2()

四、函数的参数:

默认情况下,参数通过其位置进行传递,从左至右,这意味着,必须精确地传递和函数头部参数一样多的参数,但也可以通过关键字参数、默认参数或参数容器等改变这种机制     

(1)普通参数:定义函数时从左至右     

(2)默认参数:定义函数时是使用"name=value"的语法直接给变量一个值,从而传入的值可以少于参数个数    

(3)指定参数:调用函数时指定"name形式参数=value实际参数"的语法通过参数名进行匹配     

(4)动态参数:定义函数时形式参数中收集任意多基于普通参数       

  定义函数时使用* :收集普通参数,返回元组,*args       

  定义函数时使用:收集指定参数,返回列表,kwargs     

(5)动态参数解包:调用函数时,使用**开头的参数,从而传递任意多基于普通或指定参数

 

注意:定义函数时

1.混用普通参数和默认参数,应当把默认参数写到右侧   

2.混用有默认和无默认值的参数时,无默认值放左侧

4.1、返回值

函数是一个功能块,该功能到底执行成功与否,需要通过返回值来告知调用者。

以上要点中,比较重要有参数和返回值:

  def 发送短信():
         
      发送短信的代码...
     
      if 发送成功:
          return True
      else:
          return False
     
  while True:
         
      # 每次执行发送短信函数,都会将返回值自动赋值给result
      # 之后,可以根据result来写日志,或重发等操作
     
      result = 发送短信()
      if result == False:
          记录日志,短信发送失败...

4.2、参数

没有参数的情况

  #!/usr/bin/env python
  # -*- coding:utf-8 -*-
'''
  1.需要开启邮箱服务sendmail
  2.邮箱服务器需要开启SMTP服务
  '''def sendmail():
      try:
          import smtplib
          from email.mime.text import MIMEText
          from email.utils import formataddr
  ​
          msg = MIMEText('邮件内容', 'plain', 'utf-8')
          msg['From'] = formataddr(["发件人", 'pythonxiao@126.com'])
          msg['To'] = formataddr(["收件人", '329275108@qq.com'])
          msg['Subject'] = "邮件主题"
  ​
          server = smtplib.SMTP("smtp.126.com", 25)
          server.login("pythonxiao@126.com", "xiaozhiqi2016")
          server.sendmail('pythonxiao@126.com', ['329275108@qq.com', ], msg.as_string())
          server.quit()
      except:
          return False
      else:
          return True
  ​
  ret = sendmail()
  if ret == True:
      print("发送成功")
  else:
      print("发送失败")

有参数的情况:

  def email(mail):
      import smtplib
      from email.mime.text import MIMEText
      from email.utils import formataddr
  ​
      msg = MIMEText('邮件内容', 'plain', 'utf-8')
      msg['From'] = formataddr(["测试",'asdasd@126.com'])
      msg['To'] = formataddr(["走人",'asdasdasd@163.com'])
      msg['Subject'] = "主题"
  ​
      server = smtplib.SMTP("smtp.126.com", 25)
      server.login("wdfgfghfgh@126.com", "123456")
      server.sendmail('asdasdas@126.com', [mail,], msg.as_string())
      server.quit()
  ​
  email("6087414@qq.com")

当执行这个脚本的时候会给邮箱6087414@qq.com发送邮件。

注:上面的邮箱地址等都是随便写的,请自行更改

从上面例子可以看出有参数的函数表现的非常灵活可扩展!

 

函数的有三中不同的参数(定义函数时):

  • 普通参数

  • 默认参数

  • 动态参数

  指定参数和动态参数解包是发生在调用函数

1、普通参数

  (1)# xxoo为普通参数也叫形式参数,简称:形参
  def f1(name):
      print(name)
  ​
  # 'test' 叫做函数的实际参数,简称:实参
  f1('tomcat')

2、默认参数

  (2)# name 是普通参数,ab=5是默认参数
  def f1(name,ab=5):
      print(name,ab)
  ​
  # 指定参数
  f1('tomcat',12)
  ​
  # 使用默认参数
  f1('tomcat')
  ​
  注意:默认参数需要放在参数列表最后

3、动态参数

动态参数把接收过来的实际参数当作一个元组,每一个参数都是元组中的一个元素。

第一种动态参数

定义第一种动态参数需要在参数前面加上一个*

  >>> def ret(*args):
  ...  print(args,type(args))
  ...
  >>> ret(11,22,33)
  (11, 22, 33) <class 'tuple'>

第二种动态参数

定义第二种动态参数需要在参数前面加上两个*号,给参数传参的时候是一个key对应一个value的,相当于一个字典的键值对,而且返回的类型就是字典类型。

使用两个星号可以将参数收集到一个字典中,参数的名字是字典的键,对应参数的值是字典的值。

  >>> def ret(**kwargs):
  ...  print(kwargs,type(kwargs))
  ...
  >>> ret(k1=123,k2=456)
  {'k1': 123, 'k2': 456} <class 'dict'>

第三种动态参数

第三种又称为万能的动态参数,如下实例:

  >>> def ret(*args,**kwargs):
  ...  print(args,type(args))
  ...  print(kwargs,type(kwargs))
  ...
  >>> ret(11,222,333,k1=111,k2=222)
  (11, 222, 333) <class 'tuple'>
  {'k1': 111, 'k2': 222} <class 'dict'>

1.5、函数式编程:

以后写函数按照下面这个例子来编写,规范,这是一个登录注册的函数编程

  • 1.函数与函数间隔2行

  • 2.函数需要注释,便于记忆

  • 3.函数名需要见名知意

 

db文件格式

  admin|123
  xiao|12345

代码

  #!/usr/bin/env python
  # coding: utf8
  ​
  ​
  def datainput():
      username = input("input your username:")
      password = input("input your password:")
      return username,password
  ​
  ​
  def login(username,password):
      f = open('db','r')
      for line in f:
          data = line.strip().split('|')
          if data[0] == username and data[1] == password:
              return True
      return False
  ​
  ​
  def register(username,password):
      f = open('db','r')
      for line in f:
          user = line.strip().split('|')
          #print(user)
          if username in user:
              return False
      else:
          f = open('db','a')
          userdata = "\n" + username + "|" + password
          f.write(userdata)
          f.close()
          return True
  ​
  ​
  def main():
      num  = input("1.登录,2.注册:")
      if int(num) == 1:
          data = datainput()
          result = login(data[0],data[1])
          if result:
              print('登录成功')
          else:
              print('登录失败')
      elif int(num) == 2:
          data = datainput()
          ret = register(data[0],data[1])
          if ret:
              print("注册成功")
          else:
              print("注册失败,用户已存在")
  ​
  ​
  main()

五、lambda表达式

对于简单的函数,也存在一种简便的表示方式,即:lambda表达式

  # ###################### 普通函数 ######################
  # 定义函数(普通方式)
  def func(arg):
      return arg + 1
      
  # 执行函数
  result = func(123)
      
  # ###################### lambda ######################
      
  # 定义函数(lambda表达式)
  my_lambda = lambda arg : arg + 1
      
  # 执行函数
  result = my_lambda(123)

lambda表达式会自动return返回值,条件为真返回True,条件为假返回False.

六、函数递归

所谓递归其实就是函数本身调用函数,直到满足指定条件之后一层层退出函数, 例如

从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……’”

  • 利用函数编写一个斐波那契数列

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368

斐波那契数列就是前面给两个数相加得到后面一个数,依次往后

代码如下

  #!/usr/bin/env python
  # _*_ coding: utf-8 _*_
def Counter(n1, n2):
      if n1 > 10000:  # 当要计算的值大于10000就退出
          return
      print("Counter:", n1)  # 输出当前计算到那个值了
      n3 = n1 + n2  # 第一个值加上第一个值等于第三个值
      Counter(n2, n3)  # 调用计数器函数,此时第一个值是调用函数传过来的最后一个值,而第二个值是计算出来的第三个值
  ​
  ​
  Counter(0, 1)  # 调用计数器函数

输出结果

/usr/bin/python3.5 /home/Python/Documents/PycharmProjects/blogcodes/斐波那契.py
Counter: 0
Counter: 1
Counter: 1
Counter: 2
Counter: 3
Counter: 5
Counter: 8
Counter: 13
Counter: 21
Counter: 34
Counter: 55
Counter: 89
Counter: 144
Counter: 233
Counter: 377
Counter: 610
Counter: 987
Counter: 1597
Counter: 2584
Counter: 4181
Counter: 6765

Process finished with exit code 0
  • 利用递归获取斐波那契数列中的第10个数,并将该值返回给调用者

代码:

  #!/usr/bin/env python
  # _*_ coding: utf-8 _*_
def Counter(Index, Start, End):
      print("第%d次计算,第一个数字是%d,第二个数字是%d" % (Index, Start, End))
      if Index == 10:  # 如果要计算的值是10就退出
          return Start
      N = Start + End  # N等于第一个数加上第二个数
      Number = Counter(Index + 1, End, N)  # 继续调用计数器函数,End相当与传给函数的第一个数,N是传给函数的第二个数
      return Number
  ​
  ​
  result = Counter(1, 0, 1)
  print("得出的数字是:", result)

输出结果

/usr/bin/python3.5 /home/Python/Documents/PycharmProjects/blogcodes/递归.py
第1次计算,第一个数字是0,第二个数字是1
第2次计算,第一个数字是1,第二个数字是1
第3次计算,第一个数字是1,第二个数字是2
第4次计算,第一个数字是2,第二个数字是3
第5次计算,第一个数字是3,第二个数字是5
第6次计算,第一个数字是5,第二个数字是8
第7次计算,第一个数字是8,第二个数字是13
第8次计算,第一个数字是13,第二个数字是21
第9次计算,第一个数字是21,第二个数字是34
第10次计算,第一个数字是34,第二个数字是55
得出的数字是: 34

Process finished with exit code 0