跳至主要內容

函数与变量

大约 7 分钟

函数与变量

函数

def hello():
    print('Hello,')

def world():
    print('world')
    print('!')

hello()
world()
world()
world()

过程式编程的起源与早期计算机的发展紧密相关。在最初的计算机中,程序员需要直接管理内存和处理器指令。

FORTRAN(1957年)是最早的过程式编程语言之一,它被设计用于科学计算和工程应用。随后,ALGOL(1958年)出现,对后续语言如C、Pascal和BASIC产生了深远影响。

刚刚我们一直看到的print就是一个函数,在后面加上()来调用它,

除了 python 自带的函数之外,我们也可以定义(define,缩写为def)新的函数。

例如,我们在这里定义了一个叫作hello的函数和一个叫作world的函数,然后调用了它们,这和刚开始的打印机的例子几乎一样。

相关信息

函数定义由def, 函数名, ()(参数列表), :,以及后面的函数内容构成。

函数在定义后并不会立即执行里面的内容,而是直到被调用时,才执行里面的内容,相当于给这部分内容起了个名字。

在被调用时跳转到这里,执行完毕又返回原来的地方

从始至终,同一时刻只有一段代码在执行

为了区分这种内容和其它内容, python 使用缩进(一定数量的空白符)来进行区分,并且严格要求一个文件内的缩进一致,规范的建议是,以四个空格为一个缩进。

等等,print函数里面可是能输入内容的啊!

可是,我们怎么在函数定义里使用输入的内容呢?

如果你是语言设计者,你会怎么做?

想几秒,然后看看答案:当然是取名字啦

这里定义了一个名叫area_of_a_circle的函数,在调用的时候打印对应半径的圆的面积

# 虽然没有任何机制能阻止你命名,但是习惯上使用全大写来命名常量(不会改变的量)
PI = 3.1415926

# 对于函数和其它变量的命名, python 建议使用全小写 + 下划线的方式
def area_of_a_circle(radius):
    print(PI * radius**2)

area_of_a_circle(2)
area_of_a_circle(3)
area_of_a_circle(4.5)

(这下终于看起来差不多了)

注意,这里的 radius 外面并没有引号!

并且只能在函数的内部这个范围使用,这个范围称为变量的作用域

函数可以接收不止一个参数

如果一个函数可以接收多个参数,可以使用,来分隔传入的多个值

如果想要定义一个可以接收多个参数的函数,也是同样的道理

参数需要能一一对应,如果数量不一致,则会报错

这是一个例子:

# 在逗号后加一个空格可以有更好的可读性
def area_of_a_rectangle(a, b):
    print(a*b)

area_of_a_rectangle(2, 3)
area_of_a_rectangle(1.5, 7.5)

print 可以接收任意数量的参数,并依次打印出来,默认以空格分隔

print(1)
print(1, 2)
print(1, 2, 3)
print(1, 2, 3, 4)
print('1 + 1 =', 1 + 1)
print('The approximate value', 'of', 'pi', 'is', 3.14)
怎么定义这样的函数呢

这确实不能简单地使用之前的方法

事实上,对于需要能接收不定数量的值的函数,不同的语言对此有不同的解决方案

就 python 而言,解决方案是:

  • 在接收值的时候,在一个变量前面加一个*,表示接收剩余的所有参数
  • 它得放在所有正常参数的后面,并且一个函数的参数列表中,只能有一个这样的参数

很显然,这个变量需要是一个存放着一列值的序列,在这里来说,会是一个元组

不过,目前你只需要知道,在求值时,能用*再次把它展开为一串值就行了

def my_print(*args):
    print(*args)

my_print('Hello,', 'world!')

变量

看看这个:

def hello(name):
    print('Hello,', name, '!')

print('Hello,', hello, '!')

你可能想问:

当函数里面的内容被执行时,函数的参数(这里是name),看起来就和hello这些函数名一模一样啊,都是没有引号的,可以随便取名字的东西

你的感觉是对的!

实际上,除了def:()这些python 规定的符号外,连print这些东西,都和namehello,没有任何区别

print(print)

这种名字,就叫作变量,函数的参数,对于里面的代码来说,也是一种变量

关于 python 中的命名

除了不能包含空格这种空白符,以及不能以数字开头外,怎么取名基本没有限制

你甚至能用中文取名

另外,变量名是大小写敏感的,这意味着fooFoo是不同的变量

当我们使用def创建hello,或者带参数函数被调用时(创建了name),变量就和实际的值联系了起来,这个过程叫定义(define)

当我们想读取一个未定义的变量时,python会报错:

print(foo)

事实上,有更加直接的定义方式,以等号为运算符,在左边放接收值的变量名,在右边放表达式

这个运算叫作赋值

运行一下下面的,然后再次运行上面的

foo = '233'

我们可以使用del(delete)来删除变量定义

del foo

当然直接写其它变量也行,不过,当我们重复定义变量时,会覆盖变量原有的值

bar = 'awa'
foo = bar

赋值不是数学中的相等

你不能使用'a' = 'b'这样的操作,因为'a'是具体的值,而不是可以绑定的变量

python 中的变量只是存储实际值的位置,或者说,它只是指向这个值

我们会在后面更加深刻地认识到这一点

和函数调用非常类似,你可以用,来隔开两边的内容,这使得我们可以方便地一次定义多个变量

a, b = 'hello,', 'world!'
print(a, b)

python 中的函数和其它的值一样可以随便传递(甚至内置函数也是!),这并不是所有语言都有的

f = print
del print
print('awa')
def print(text):
    f('this is my print:', text)

print('awa')

玩完记得还原回去

print = f

挑战: 交换 a 与 b 的值

python 中的交换方式非常酷!

a = 1
b = 2
# 在这里填入代码...
print(a, b)

准确来说,这是

(a, b) = (b, a)

的简写,只是在最外层时,括号可以略去

这种操作是 python 中的自动打包解包

与函数调用类似,但是比函数调用更加灵活

参考答案
a, b = b, a

挑战: 斐波那契数列

若函数内有变量的定义,那么这个变量就只会在函数内生效

哪怕外面有同名变量也是一样

除非使用global来声明它是全局的

a = 1
b = 1
def fibo():
    global a, b
    print(a)
    # 在这里填入代码...

fibo()
fibo()
fibo()
fibo()
fibo()
fibo()
fibo()

python 函数内,除了参数之外的变量的值,取决于函数定义时这些变量指向的值,而不是运行时

这叫作静态作用域

与之相对的是动态作用域,变量指向的值在函数被调用时才确定

早期的编程语言大多是动态作用域的,但现代的编程语言大多是静态作用域的,其中的好处在之后能够体会到

函数里面也可以套函数,这时可以使用nonlocal来声明变量来自于外面的函数

参考答案
a, b = b, a+b

挑战: 最后一项

写一个函数,能够接收不定数量的参数(>=1),并且打印出最后一个参数

这可能需要发挥一点想象力

def get_last(*args):
    # 在这里填入代码...

get_last(1)
get_last(1, 2)
get_last(1, 2, 3, 4, 5, 6, 7)

你甚至可以用

a, b, *c = '今天天气真好'

来截取字符串!

不过,截取出来的东西可能要下一节才能讲了

参考答案
def get_last(*args):
    *others, last = args
    print(last)
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.0.0-alpha.10