关于参数 (上)

本文为只字不差打字版 原文链接:https://github.com//the-craft-of-selfteaching 作者:李笑来

之前就提到过,从结构上来看,每个函数都是一个完整的程序,因为一个程序,核心构成部分就是输入、处理、输出:

  • 它可以有输入——即,它能接收外部通过参数传递的值;
  • 它可以有**处理——即,内部有能够完成某一特定任务的代码;尤其是,它可以根据“输入”得到输出;
  • 它可以有输出——即,它能向外部输送返回值……

所以,在我看来,有了一点基础知识之后,最早应该学习的是“如何写函数”——这个起点会更好一些。

这一章的内容,看起来会感觉与 Part1.E.4函数的那一章部分重合。但这两章的出发点不一样:

  • Part1.E.4函数的那一章,只是为了让读者有“阅读”函数说明文档的能力;
  • 这一章,是为了让读者能够开始动手写函数给自己或别人用……

为函数取名

哪怕一个函数内部什么都不干,它也得有个名字,然后名字后面要加上圆括号(),以明示它是个函数,而不是某个变量。

定义一个函数的关键字是def,以下代码定义了一个什么都不干的函数:

def do_nothing():
     pass

do_nothing()

为函数取名(为变量取名也一样)有些基本的注意事项:

  • 首先不能以数字开头。能用在名称开头的有,大小写字母和下划线_;
  • 其次,名称中,不能有空格,要么使用下划线连接词汇,如,do_nothing,要么使用Camel Case,如 doNothing——更推荐使用下划线;
  • 再次,名称不能与关键字重合——以下是Python的Keyword List:
- Python Keyword List -
and as assert async await
break class continue def del
elif else except False finally
for from global if import
in is lambda None nonlocal
not or pass raise return
True try while with yield

你随时可以用以下代码查询关键字列表:

from IPython.core.interactivershell import InteractiveShell
InteractiveShell.ast_node_interactivity ="all"

import keyword
keyword.kwlist
keyword.iskeyword('if')

['False',
'None',
'True',
'and',
'as',
'assert',
'async',
'await',
'break',
'class',
'continue',
'def',
'del',
'elif',
'else',
'except',
'finally',
'for',
'from',
'global',
'if',
'import',
'in',
'is',
'lambda',
'nonlocal',
'not',
'or',
'pass',
'raise',
'return',
'try',
'while',
'with',
'yield']

关于更多为函数、变量取名所需要的注意事项,请参阅:

不接受任何参数的函数

在定义函数的时候,可以定义成不接受任何参数;但调用函数的时候,依然需要写上函数名后面的圆括号():

def do_nothing():
print('This is a hello message from do_something().')

do_nothing()

This is a hello message from do_something ().

没有 return 语句的函数

函数内部,不一定非要有 return 语句——上面 do_something()函数就没有 return 语句。但如果函数内部并未定义返回值,那么,该函数的返回值是None,当None 被当做布尔值对待的时候,相当于False。

这样的设定,使得函数调用总是可以在条件语句中被当做判断依据:

def do_something():
    print('This is a hello message from do_something().')

if not do_something():#由于该函数名称的缘故,这一句代码的可读性很差……
    print("The return value of 'do_something()'is None.")

This is a hello message from do_something().
The return value of 'do_something()' is None.
if not do_something(): 翻译成自然语言,应该是,“如果 do_something() 的返回值是 ‘非真’,那么:……”

接收外部传递进来的值

让我们写个判断闰年年份的函数,取名is_leap(),它接收一个年份为参数,若是闰年,则返回True,否则返回False。

根据闰年的定义:

  • 年份应该是4的倍数;
  • 年份能被100整除但不能被400整除的,不是闰年。

所以,相当于要在能被4整除的年份中,排除那些能被100整除却不能被400整除的年份。

def is_leap(year):
    leap = False
    if year % 4 == 0:
        leap = True
        if year % 100 == 0 and year % 400 != 0:
            leap = False
    return leap


is_leap(7)
is_leap(12)
is_leap(100)
is_leap(400)

#另外一个更为简洁的版本,理解它还练脑子的
#cpython/Lib/datetime.py
def _is_leap(year):
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
_is_leap(300)

函数可以同时接受多个参数。比如,我们可以写个函数,让它输出从大于某个数字到小于另外一个数字的斐波那契数列;那就需要定义两个参数,调用他的时候也需要传递两个参数:

def fib_between(start,end):
    a,b = 0,1
    while a < end:
        if a >= start:
            print(a,end='')
        a,b=b,a+b

fib_between(100, 10000)

[144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]

变量的作用域

下面的代码,经常会让初学者迷惑:

def increase_one(n):
    n += 1
    return n

n =1
print(increase_one(n))
#print(n)

2

当increase_one(n)被调用之后,n的值究竟是多少呢?

输出结果是1.

在程序执行过程中,变量有全局变量(Global Variables)和局域变量(Local Variables)之分。

首先,每次某个函数被调用的时候,这个函数会开辟一个新的区域,这个函数内部所有的变量,都是局域变量。也就是说,即便那个函数内部某个变量的名称与它外部的某个全局变量——只是名称相同而已。
其次,更为重要的是,当外部调用一个函数的时候,准确地将,传递的不是变量,而是那个变量的值。也就是说,当 increase_one(n) 被调用的时候,被传递给那个恰好名称也叫n的局域变量,是全局变量n的值,1.
而后,increase_one(n) 函数的代码开始执行,局域变量n经过n+=1之后,其中存储的值是2,而后这个值被 return 语句返回,所以,print( increase_one(n) )所输出的值势函数被调用之后的返回值,即,2.
然而,全局变量n的值并没有改变,因为局部变量n(它的值还是1)只不过是名字相同而已,但它们并不是同一个变量。

以上的文字,可能需要反复阅读若干遍;几遍下来,消除疑惑,以后就彻底没问题了;若是这个疑惑并未消除,或者关键点并未消化,以后则会反复被这个疑惑所坑害,浪费无数时间。

不过,有一种情况要格外注意——在函数内部处理被传递进来的值是可变的容器(比如,列表)的时候:

def be_careful(a,b):
    a = 2
    b[0]='What?!'

a = 1
b = [1, 2, 3]
be_careful(a, b)
a, b

(1, ['What?!', 2, 3])

所以,一个比较好的习惯是,如果传递进来的值是列表,那么在函数内部对其操作之前,先创建一个它的拷贝:

def be_careful(a,b):
    a = 2
    b_copy = b.copy()
    b_copy[0]='What?!'


a = 1
b = [1, 2, 3]
be_careful(a, b)
a, b

(1, [1, 2, 3])

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

推荐阅读更多精彩内容