函数

函数

编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”要写出高质量的代码首先要解决的就是重复代码的问题。可以将重复代码定义为函数,只需要反复调用函数即可,避免代码冗余。

1. 定义函数

使用def关键字告诉Python要定义一个什么名称的函数

紧跟在def后面的所有缩进行构成了函数体

例如:

def greet_user():
  """显示简单的问候语"""
  print('hello!')
  
greet_user()

2. 向函数传递信息

def greet_user(username):
  """显示简单的问候语"""
  print(f'hello!,{username.title()}')
  
greet_user('jesse')

2.1 实参和形参

  • 在函数greet_user()定义的时候,变量username是一个形参,即函数完成工作所需的信息
  • 在代码greet_user('jesse')中,'jesse'是一个实参,即调用函数传递给函数的信息

3. 传递实参

3.1 位置实参

  • 调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参,这种关联方式称为位置实参

    例如:

    def dscribe_pet(animal_type,pet_name):
    		"""显示宠物的信息"""
    		print(f"\n I have a {animal_tyep}")
    		print(f"\n My pet's name is {pet_name.title()} ")
    		
    describe_pet('hamster','harry')
    

3.1.1 多次调用函数

  • 可以根据需要调用函数任意次数

    例如:

    def dscribe_pet(animal_type,pet_name):
    		"""显示宠物的信息"""
    		print(f"\n I have a {animal_tyep}")
    		print(f"\n My pet's name is {pet_name.title()} ")
    		
    describe_pet('hamster','harry')
    describe_pet('cat','jesse')
    

3.1.2 位置实参的位置很重要

  • 使用位置实参来调用函数时,如果实参的位置不正确,结果可能出乎意料

3.2 关键字实参

  • 关键字实参是传递给函数的名称值对

  • 关键字实参`无需考虑函数调用中的实参顺序

    例如:

    def dscribe_pet(animal_type,pet_name):
    		"""显示宠物的信息"""
    		print(f"\n I have a {animal_tyep}")
    		print(f"\n My pet's name is {pet_name.title()} ")
    		
    describe_pet(animal_type = 'hamster',pet_name = 'harry')
    describe_pet(pet_name = 'jesse',animal_type = 'cat')
    

3.3 默认值

  • 编写函数时,可给每个形参指定默认值

  • 在调用函数的时候,如果给形参传递了实参的话,函数就使用实参,否则就是使用形参默认值

    例如:

    def dscribe_pet(pet_name , animal_type = 'dog'):
    		"""显示宠物的信息"""
    		print(f"\n I have a {animal_tyep}")
    		print(f"\n My pet's name is {pet_name.title()} ")
    		
    describe_pet(pet_name = 'harry')
    describe_pet(pet_name = 'Jesse' , animal_type = 'cat')
    

    给函数指定了动物类型的默认值,因此将宠物名字与动物类型形参的位置调换一下,这样调用函数时如果只传递宠物名字就直接关联到第一个形参宠物名字

    如果需要指定宠物的类型,直接describe_pet(pet_name = 'Jesse' , animal_type = 'cat'),显式地给宠物类型的形参传递了实参,Python就忽略默认值

3.4 等效的函数调用

def dscribe_pet(pet_name , animal_type = 'dog'):
		"""显示宠物的信息"""
		print(f"\n I have a {animal_tyep}")
		print(f"\n My pet's name is {pet_name.title()} ")
 
"""一条名为willie的小狗"""
describe_pet('willie')
describe_pet(pet_name = 'willie')
"""一只名为harry的仓鼠"""
describe_pet('harry','hamster')
describe_pet(pet_name = 'harry',animal_type = 'hamster')
describe_pet(animal_type = 'hamster',pet_name = 'harry')

3.5 避免实参错误

4. 返回值

4.1.返回简单值

def get_formatted_name(fist_name,last_name):
		"""返回整洁的姓名"""
    full_name = f"{first_name} {last_name}"
    return full_name.title()
  
musician = get_formatted_name('jimi','hendrix')
print(musician)

在需要大量存储名和姓的大型程序中,像get_formatted_name这样的函数非常有用,可以分别存储名和姓

4.2. 让实参变成可选的

def get_formatted_name(fist_name,last_name,middle_name=''):
		"""返回整洁的姓名"""
    if middle_name:
    		full_name = f"{first_name} {middle_name} {last_name}"
    else:
      	full_name = f"{first_name} {last_name}"
        
    return full_name.title()
  
musician = get_formatted_name('jimi','hendrix')
print(musician)

中间名这个形参是可选的,可以没有,也可以有,只要将中间名的这个形参的默认值设置为空即可

4.3.返回字典

  • 函数可以返回任何类型的值,包括列表字典等复杂的数据结构

    例如:

    def build_person(first_name, last_name, age = None):
      """返回一个字典,字典内容为一个人的信息"""
      person = {
        				'firstname':first_name, 
        				'lastname':last_name,
      					}
      if age:
        person['age'] = age
      
      return person
    
    musician = build_person('jimi','hendrix', age=27)
    print(musician)
    

4.4.结合使用函数和while循环

def get_formatted_name(fist_name,last_name):
		"""返回整洁的姓名"""
    full_name = f"{first_name} {last_name}"
    return full_name.title()
  
while True:
    print("\nPlease tell me your name:")
    print("\nenter 'q' at any time to quit")

    f_name = input("first name:")
    if f_name == 'q':
        break

    l_name = input("last name:")
    if l_name == 'q':
        break   

    musician = get_formatted_name(f_name, l_name)
    print(musician)

5. 传递列表

def greet_users(names):
  """向列表中每位用户发出简单问候"""
  for name in names:
    msg = f'Hello,{name.title()}'
    print(msg)
    
  names = ['hannah','jesse','tom']
  greet_users(names)
    

5.1在函数中修改列表中的值

  • 将列表传递给函数后,函数可以对列表进行修改

  • 在函数中对列表的修改都是永久性

    例如:

    def print_content(unprinted, printed):
      
      while unprinted:
        content = unprinted.pop()
        print(f'printing:{content}')
        printed.append(content)
       
    def show_printed(printed):
      for p in printed:
        print(p)
        
    unprinted = ['a','b','c']
    printed = []
    print_content(unprinted,printed)
    show_printed(printed)
    

​ 每个函数都应该只负责一项具体的工作

5.2 禁止函数修改列表

  • 假设像前面的示例那样,将未打印的内容打印后都放在了已打印的列表中,原来的未打印的列表中为空,有时候需要把未打印的列表中的内容保留,这时就需要向函数传递列表的副本,来确保原本的列表保留下来

  • 传递给函数列表的副本使用切片表示法function_name(list_name[:])

6. 传递任意数量的实参

  • 有时不知道函数需要接受多少个实参,Python允许函数从调用语句中收集任意数量的实参

  • 例如:

    def make_pizza(*toppings):
      """打印顾客点的所有配料"""
      print(toppings)
    
    
    make_pizza('mushrooms', 'cheese', 'green peppers')
    

    *toppings中的'*'让Python创建了一个名为toppings的空元组,并将所有接受到的值封装在这个元组中

    def make_pizza(*toppings):
      """打印顾客点的所有配料"""
      for topping in toppings:
      		print(topping)
    
    
    make_pizza('mushrooms', 'cheese', 'green peppers')
    

6.1 结合使用位置实参和任意数量实参

  • 如果想要让函数接受任意数量的实参,需要把接受任意数量实参的形参放在最后

    例如:

    def make_pizza(size, *toppings):
      """打印顾客点的pizza信息"""
      print(f"make {size} inch pizza with the following toppings:")
      for topping in toppings:
            print(topping)
    
    
    make_pizza(6, 'mushrooms', 'cheese', 'green peppers')
    
    

    注意:通常会看到通用形参名\*args,它也收集任意数量的位置实参

6.2 使用任意数量的关键字实参

  • 有时候,需要接受任意数量的实参,但是预先不知道会传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键值对——调用语句给多少就接受多少

    例如:

    def build_profile(first, last, **user_info):
      """创建一个字典,包含我们知道的有关用户的一切"""
      user_info['first_name'] = first
      user_info['last_name'] = last
      return user_info
    
    user_profile = build_profile('albert','einstein',
                  location = 'princeton',
                  field = 'physics')
    print(user_profile)
    

    **user_info中的两个'*'让Python创建了一个名为user_info的空字典,并将收到的所有名称值对都放在这个字典里面

    注意:看到形参名\**kwargs,用于收集任意数量的关键字实参

7.将函数存储在模块中

  • 使用函数的一个优点是将代码块和主程序分离,让主程序更容易理解
  • 还可以更进一步,将函数存储在模块文件中,在将模块导入到主程序中

7.1 导入整个模块

  • 模块是扩展名为.py的文件,包含要导入的函数代码主体
  • 使用import 模块名 导入整个模块,会将模块中的所有函数导入到主程序中
  • 导入整个模块后,使用模块名.函数名用来使用模块中任意一个函数

7.2 导入特定的函数

  • 从模块中导入特定的函数:from 模块名 import 函数名
  • 从模块中导入任意数量的函数:from 模块名 import 函数名1,函数名2,函数名3 使用','将要导入的函数名分隔开,可以导入任意数量的函数

7.3 使用as给函数指定别名

  • 如果从模块中导入的函数名称与主程序中的名称相冲突,或者要导入的函数名称太长,可以使用as给要导入的函数起别名,类似于外号
  • 用法:from 模块名 import 函数名 as 别名

7.4 使用as给模块起别名

可以使用as给模块起一个简洁的别名,进而可以轻松的调用函数

用法:import 模块名 as 别名

例如:

import pizza as p 
p.make_pizza(16,'pepperoni')

给模块pizza起一个别名p,调用pizza中的函数时更为方便

7.5 导入模块中的所有函数

  • 使用'*'运算符可以让Python导入模块中的所有函数
  • 用法:from 模块名 import *

8. 函数编写指南

  • 函数名称应该描述性强,并且只能在函数名称中使用小写字母和下划线
  • 每个函数都应该包含简明阐述函数功能的注释,注释应该紧跟在函数定义后面,并采用文档字符串格式
  • 给形参指定默认值时,‘=’两边不要有空‘,对于函数调用的关键字实参,也应遵守这种规定
  • PEP8建议代码长度不要超过79个字符
  • 如果形参很多,导致函数定义的语句超过79字符,可在函数定义中输入左括号后按下回车键,并在下一行按两次Tab键,从而将形参列表和只缩进一层的函数体分隔开

9. __name__变量

  • '_name_'(前后各有两个下划线)是Python的内置函数,是每个Python模块必备的属性
  • 一般而言,所有代码不会放在同一个文件里面,需要在不同模块内写入不同的函数。为了使用这些函数,你需要从别的模块中导入这些函数,'_name_'可以判断出这些函数的代码是被直接执行还是被导入到其他文件中去了

参考资料[https://zhuanlan.zhihu.com/p/57309137]

posted @ 2021-12-01 16:38  写代码的小灰  阅读(45)  评论(0)    收藏  举报