专栏名称: VPointer
软件开发
目录
相关文章推荐
财宝宝  ·  电炸惊魂。 ... ·  2 天前  
财宝宝  ·  分享图片 -20250314101312 ·  2 天前  
51好读  ›  专栏  ›  VPointer

Python学习之路7-函数

VPointer  · 掘金  ·  · 2018-05-30 02:09

正文

Python学习之路7-函数

《Python编程:从入门到实践》笔记。

本章主要介绍Python中函数的操作,包括函数的概念,定义,如何传参等,最后还有小部分模块的概念。

1. 定义函数

1.1 一般函数

函数是带名字的代码块,该代码块是完成特定工作的固定代码序列。如果程序中多次出现相同或相似的代码块,则应将这段代码提取出来,编写成函数,然后多次调用。通过编写函数可以避免重复工作,使程序的编写、阅读、测试和修复更容易。**请使用描述性的函数名来命名函数,以大致表明函数的功能,这样即使没有注释也能容易理解。函数名应尽量只有小写字母和下划线。**以下是两个最基本的函数,有参数与无参函数:

# 定义无参函数
def greet_user1():
    """显示简单的问候语"""
    print("Hello!")

# 定义有参函数
def greet_user2(username):
    """显示简单的问候语"""
    print("Hello, " + username.title() + "!")

# 调用函数
greet_user1()
greet_user2("jesse")

# 结果:
Hello!
Hello, Jesse!

在调用函数前,必须先定义函数!即函数的定义部分必须在调用语句之前。 上述代码中的三引号字符串叫做 文档字符串 ,他们既可以被用作代码注释,也可用于自动生成有关程序中函数的文档。

实参和形参 这两个概念经常被搞混,函数定义中的参数叫做形参,比如上述函数 greet_user2(username) 中的 username 就是形参;传递给函数的参数叫做实参,比如在调用 greet_user2("jesse") 时的 "jesse" 就是实参。

1.2 空函数

如果想定义一个什么都不做的函数,可以使用 pass 语句

def do_nothing():
    pass

如果为了让程序能跑起来,但暂时又不写这个函数,可以使用 pass 语句。这里 pass 用作占位符。

2. 传递参数

2.1 位置参数(必选参数)

这就是要求实参的顺序和形参的顺序相同。

# 代码:
def describe_pet(animal_type, pet_name):
    """显示宠物的信息"""
    print("\nI have a " + animal_type + ".")
    print("My " + animal_type + "'s name is " + pet_name.title() + ".")

describe_pet("hamster", "harry")
describe_pet("dog", "willie")

# 结果:
I have a hamster.
My hamster's name is Harry.

I have a dog.
My dog's name is Willie.

对于位置参数,应该注意实参的传递顺序,如果顺序不对,结果会出乎意料:有可能报错,如果不报错,函数所要表达的意思可能改变。

# 代码:
describe_pet("willie", "dog")

# 结果:
I have a willie.           # 尴尬
My willie's name is Dog.

2.2 关键字参数(传实参时)

如果函数的形参过多,则很难记住每个位置的参数是用来干什么的,如果用 键值对的方式传递实参 ,这个问题就能迎刃而解,这就是关键字参数。在传递参数时,直接将形参与实参关联,这样就不用在意实参的位置,依然以上述代码为例,函数定义不变:

# 代码:
describe_pet(animal_type="hamster", pet_name="harry")
describe_pet(pet_name="harry", animal_type="hamster")

# 结果:
I have a hamster.
My hamster's name is Harry.

I have a hamster.
My hamster's name is Harry.

请注意,这是一种 传递参数 的方法!在 调用函数 时使用!

2.3 默认参数(定义函数时,形参)

编写函数时可以为每个形参指定默认值,给形参指定了默认值之后,在调用函数时可以省略相应的实参。使用默认值可以简化函数调用,也可清楚地指出函数的典型用法。比如上述 describe_pet() 函数如果给形参 animal_type 指定默认值 “dog” ,则可以看出这个函数主要是用来描述狗这种宠物的。

# 代码:
def describe_pet(pet_name, animal_type="dog"):
    """显示宠物的信息"""
    print("\nI have a " + animal_type + ".")
    print("My " + animal_type + "'s name is " + pet_name.title() + ".")


describe_pet(pet_name="willie")
describe_pet("happy")
describe_pet("lili", "cat")

# 结果:
I have a dog.
My dog's name is Willie.

I have a dog.
My dog's name is Happy.

I have a cat.
My cat's name is Lili.

在函数调用时,如果给形参提供了实参,Python将使用指定的实参;否则将使用形参的默认值。 注意 :默认参数是在 函数定义 时使用!在定义函数时带有默认值的形参必须在没有默认值的形参后面!

还有一点值得注意:**默认参数必须指向不变对象!**请看以下代码:

# 代码:
def add_end(temp=[]):
	"""在传入的列表最后添加“end”"""
    temp.append("end")
    return temp


print(add_end([1, 2, 3]))
print(add_end(["a", "b", "c"]))
print(add_end())
print(add_end())
print(add_end())

# 结果:
[1, 2, 3, 'end']
['a', 'b', 'c', 'end']
['end']
['end', 'end']
['end', 'end', 'end']

当给这个函数传递了参数时,结果是正确的,而且,在没有传递参数且第一次调用时,返回结果也是正确的,然而,没有传递参数且第二次、第三次调用时,结果则成了问题。这是因为,Python在函数定义的时候,默认参数的值就被计算了出来,形参只要不指向新的值,它就会一直指向这个默认值,但如果这个默认值是个可变对象,就会出现上述情况。 要修正上述例子,可以使用 None str 之类的不变对象。如下:

def add_end(temp=None):
    """在传入的列表最后添加“end”"""
    if temp is None:
        temp = []
    temp.append("end")
    return temp

print(add_end())
print(add_end())

# 结果:
['end']
['end']

补充--设计不变对象的原因 : ①对象一旦创建则不可修改,可以减少因修改数据而产生的错误; ②由于对象不可修改,在多任务环境下不需要加锁,同时读不会出错。所以,我们在设计一个对象时,能设计成不变对象则设计成不变对象。

3. 返回值

3.1 返回简单值

函数并非总是直接显示输出,它可以处理一些数据并返回一个或一组值。在Python的函数中,使用 return 语句来返回值。以下是一个参数可选的带有返回值的函数例子:

# 代码:
def get_formatted_name(first_name, last_name, middel_name=""):
    """返回标准格式的姓名"""
    if middel_name:
        full_name = first_name + " " + middel_name + " " + last_name
    else:
        full_name = first_name + " " + last_name

    return full_name.title()


musician = get_formatted_name("jimi", "hendrix")
print(musician)

musician = get_formatted_name("john", "hooker", "lee")
print(musician)

# 结果:
Jimi Hendrix
John Lee Hooker

3.2 返回字典

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

# 代码:
def build_person(first_name, las_name, age=""):
    """返回一个字典,其中包含一个人的信息"""
    person = {"first": first_name, "last": las_name}
    if age:
        person["age"] = age
    return person


musician = build_person("jimi", "hendrix", age=27)
print(musician)

# 结果:
{'first': 'jimi', 'last': 'hendrix', 'age': 27}

3.3 返回多个值

return 语句后面用逗号分隔多个值,则可返回多个值:

# 代码:
def return_mult():
    return 1, 2

a, b = return_mult()
print("a = " + str(a) + "\nb = " + str(b))

# 结果:
a = 1
b = 2

但其实这是个假象,其实函数返回的是一个元组(Tuple),只是最后对元组进行了解包,然后对 a b 进行了平行赋值。

# 代码:
print(return_mult())

# 结果:
(1, 2)

如果函数返回多个值,但有些值并不想要,则这些位置的值可以用下划线 _ 进行接收:

def return_mult():
    return 1, 2, 3
    
a, _, _ = return_mult()

4. 传递列表

将列表传递给函数,函数可以直接访问其内容或对其进行修改。用函数处理列表可以提高效率。 以下代码是一个打印程序,将未打印的设计在打印后转移到另一个列表中,此代码中未使用函数:

# 代码:
# 未打印列表
unprinted_designs = ["iphone case", "robot pendant", "dodecahedron"]
completed_models = []

# 模拟打印过程,知道没有未打印的设计为止,并将已打印的设计移动到“完成列表”
while unprinted_designs:
    current_design = unprinted_designs.pop()

    # 模拟打印过程
    print("Printing model: " + current_design)
    completed_models.append(current_design)

print("\nThe following models have been printed:")
for completed_model in completed_models:
    print(completed_model)

# 结果:
Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone case

The following models have been printed:
dodecahedron
robot pendant
iphone case

现在用两个函数来重组这些代码:

# 两个函数:
def print_models(unprinted_designs, completed_models):
    """模拟打印过程,知道没有未打印的设计为止,并将已打印的设计移动到“完成列表”"""
    while unprinted_designs:
        current_design = unprinted_designs.pop()

        # 模拟打印过程
        print("Printing model: " + current_design)
        completed_models.append(current_design)


def show_completed_models(completed_models):
    print("\nThe following models have been printed:")
    for completed_model in completed_models:
        print(completed_model)

# 主程序代码:
unprinted_designs = ["iphone case", "robot pendant", "dodecahedron"]
completed_models = []

print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)

从以上代码可以看出,使用了函数后,主程序变为了短短四行。 相比于没有使用函数的代码,使用了函数后代码更易读也更容易维护。 在编写函数时,尽量每个函数只负责一项功能,如果一个函数负责的功能太多,应将其分成多个函数。同时,函数里面还能调用另一个函数;函数里也能再定义函数!







请到「今天看啥」查看全文