Python基础-4 函数

Python-4 函数

一、函数作用

代码出现大量重复操作时,考虑将操作重构为函数(Functions)。

例如,我们想做一个记账本,记录日期、地点和消费内容及金额。

不用函数时,我们要这样写:

print("5月10号")
print("去水果店")
print("买苹果,花费5.2元")

print("5月11号")
print("去水果店")
print("买橘子,花费6元")

print("5月12号")
print("去超市")
print("买牛奶,花费60元")

看起来很多重复的地方,我们把核心内容抽象出来,然后封装成函数。

记录日期、地点和消费内容及金额,这些内容会改变,我们把它们作为参数传递给函数((date, place, item, price)作为参数传入函数)。

def record_consume(date, place, item, price):

定义函数后,我们只需要用函数名(参数)方式调用

record_consume("5月10日", "水果店", "苹果", 5.2)

二、函数定义

2.1 函数的定义和调用

def record_consume(date, place, item, price):
    """记账函数,传入参数日期,地点,物品,价格"""
    print(date)
    print(f"去{place}")
    print(f"买{item},花费{price}元")

record_consume("5月10日", "水果店", "苹果", 5.2)

可以看出函数定义的语法:

定义 函数使用关键字 def,后跟函数名与括号内的形参列表。函数语句从下一行开始,并且有4空格缩进。

def 函数名(参数列表):
    函数内容

函数内的第一条语句是字符串时,该字符串就是文档字符串,也称为 docstring,利用文档字符串可以自动生成文档,还可以让开发者在浏览代码时直接查阅文档;最好养成在代码中加入文档字符串的好习惯。


函数执行的过程:

参数传递.png

有实际值的实参 传递给 函数中形式上占位的参数(形参)


具体细节:(初次阅读时可跳过)

函数在 执行 时使用函数局部变量符号表,所有函数变量赋值都存在局部符号表中。

引用变量的查找顺序从从内到外的。首先,在局部符号表里查找变量,然后是外层函数局部符号表,再是全局符号表,最后是内置名称符号表。因此,尽管可以引用全局变量和外层函数的变量,但最好不要在函数内直接赋值(除非是 global 语句定义的全局变量,或 nonlocal 语句定义的外层函数变量)。

在调用函数时会将实际参数(实参)引入到被调用函数的局部符号表中;因此,实参是使用 按值调用 来传递的(该 是对象的 引用 而不是对象的值)。当一个函数调用另外一个函数时,会为该调用创建一个新的局部符号表。


上面我们定义了一个没有返回值的函数,

下面来看一个有返回值的函数,该函数使用return 返回一个列表。

def distance_to_avg(scores):
    """计算到平均值的差值"""
    avg = sum(scores) / len(scores)
    return [x - avg for x in scores]


scores = [90, 80, 70, 80, 90]
distances = distance_to_avg(scores) # 将返回的列表赋值给distances变量
print(distances)

2.2 函数参数

2.2.1 默认值参数

如果参数中某些值通常固定,可以为它设置默认值。

语法:

def 函数名(必选参数,默认值参数=默认值):

例如:我们有一个计算列表均值或总和的函数,我们通常使用均值功能,因此将mood设置默认值为‘avg’

def calculate_list_sta(list_a, mood='avg'):
    if mood == 'avg':
        return sum(list_a) / len(list_a)
    elif mood == 'sum':
        return sum(list_a)
    else:
        return -1

调用时默认模式就是'avg',只有模式不是'avg'的时候需要传mood参数。

scores = [90, 80, 70, 80, 90]
avg = calculate_list_sta(scores)
s = calculate_list_sta(scores,"sum")
print(avg,s)

关于默认值的细节:

默认值在定义作用域里的函数定义中求值:

i = 5

def f(arg=i):
    print(arg)

i = 6
f()  # 5

默认值只计算一次,使用列表、字典等可变对象时,会产生累积效果。

def f(a, L=[]):
    L.append(a)
    return L

print(f(1)) #[1]
print(f(2)) #[1, 2]
print(f(3)) #[1, 2, 3]

调用f(2)的时候,L不再赋值为[],而是使用已有的L=[1]。

如果不想在后续调用之间共享默认值,应该使用下面方法:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

默认值为None,在函数内赋值[]。

2.2.2 关键字参数

可用key=value 形式关键字参数调用函数。

例如:

print(calculate_list_sta(scores, 'sum'))
print(calculate_list_sta(list_a=scores, mood='sum'))
print(calculate_list_sta(mood='sum', list_a=scores))
print(calculate_list_sta(mood='sum',scores)) # 错误,位置参数必须在关键字参数前

2.2.3 *name 和 **name形式参数

*name 接收一个元组,该元组包含形参列表之外的参数。

**name接收一个字典。

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    for kw in keywords:
        print(kw, ":", keywords[kw])
cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

"Limburger"传给kind,剩下的被传给arguments,下面三行的key=value形式被当成字典,传给*keywords。

2.2.4 特殊参数

通常,参数传递有上面的两种方式,位置参数,关键字参数。

通过/和*标记,可以限制传参方式。

/前面仅限位置参数,

*后面仅限关键字参数

def standard_arg(arg):
    print(arg)

standard_arg(2)
standard_arg(arg=2)


def pos_only_arg(arg, /):
    print(arg)

pos_only_arg(1)
pos_only_arg(arg=1)    #报错,只能通过位置传参,不能用关键字传参


def kwd_only_arg(*, arg):
    print(arg)

kwd_only_arg(3)     #报错,只能关键字传参
kwd_only_arg(arg=3)


# /前面仅位置传参,*后面仅关键字,standard不受约束
def combined_example(pos_only, /, standard, *, kwd_only):
    print(pos_only, standard, kwd_only)

combined_example(1, 2, kwd_only=3)  
combined_example(1, standard=2, kwd_only=3)

限制传参方式可以避免潜在的传参冲突问题:

def foo(name, **kwds):
    return 'name' in kwds
foo(1, **{'name': 2}) # 冲突,1会先传递给name,{'name':2}会以关键字方式再传递给name

使用位置限定就可以避免,现在name只能从通过位置参数传递。

def foo(name, /, **kwds):
    return 'name' in kwds
print(foo(1, **{'name': 2}))

2.2.5 任意实参列表

*arg可以传递可变数量的实参。

def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

通常,可变参数arg放在最后。因为它会把剩余的参数全部读入。arg后面的只能是关键字参数。

def concat(*args, sep="/"):
    return sep.join(args)

concat("earth", "mars", "venus")
concat("earth", "mars", "venus", sep=".")

2.3 解包实参列表

使用*解包(unpack),将参数从列表中取出:

list(range(3, 6))            # 正常调用,使用两个参数

args = [3, 6]
list(range(*args))            # 使用解包(unpack),两个参数从列表中取出

同样,字典可以用 ** 操作符传递关键字参数:

def parrot(voltage, state='a stiff', action='voom'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.", end=' ')
    print("E's", state, "!")

d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
parrot(**d)

2.4 Lambda 表达式

lambda 关键字用于创建小巧的匿名函数。例如:lambda a, b: a+b 函数返回两个参数的和。在语法上,它只能是单个表达式。在语义上,它只是常规函数的语法糖。

最常见的用法是用在排序中,将其作为排序的关键字函数。

pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda pair: pair[1])
pairs

三、提升函数可读性

3.1 文档字符串

文档字符串用来说明对象用途。

第一行为摘要。大写字母开头,句号结尾。

第二行空白。

后面可以包含若干段落,描述对象的使用等。

def my_function():
    """Do nothing, but document it.

    No, really, it doesn't do anything.
    """
    pass

print(my_function.__doc__)
#输出:
Do nothing, but document it.

    No, really, it doesn't do anything.

3.2 函数注解

注解(annotations)用来说明数据类型。以字典形式存放在函数的annotations属性中,不会影响函数的其他部分。

语法:形参后跟:标注参数类型,参数列表后用 -> 返回值类型。

def 函数名(形参: 参数类型, 关键字参数: 参数类型 = '默认值') -> 返回值类型:

def f(ham: str, eggs: str = 'eggs') -> str:
    print("Annotations:", f.__annotations__)
    print("Arguments:", ham, eggs)
    return ham + ' and ' + eggs

3.3 编码风格

当代码变长、变复杂时,统一、可读性高的编码风格非常重要。

PEP 8 (Python Enhancement Proposal,python增强建议)是Python风格指南。内容比较多,可以去官网查看:https://peps.python.org/pep-0008/ 后面我也会整理一部分常用的。

另外补充一个技巧,如果你用的是PyCharm,可以通过Reformat(快捷键:ctrl+alt+enter)进行格式化代码,优化代码排版。


image.png


本文思维导图:

函数.png

关注公众号一只大鸽子,Python从入门到进阶。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Python 中的函数是由若干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元,可以完成一定的功能...
    秋幻旎苏阅读 609评论 0 0
  • 4. 其他流程控制工具 除了刚刚介绍过的 while 语句,Python中也有其他语言常用的流程控制语句,只是稍有...
    miskid阅读 211评论 0 0
  • 本系列文章是我学习Python3.9的官方tutorial[https://docs.python.org/3/t...
    方雲阅读 372评论 0 0
  • [译]The Python Tutorial#More Control Flow Tools 除了刚才介绍的whi...
    理查德成阅读 395评论 2 3
  • 二、判断题1、Python是一种跨平台、开源、免费的高级动态编程语言。(对)2、Python 3.x完全兼容Pyt...
    圣咸鱼阅读 13,330评论 0 3