Python基础教程-第6章-函数

6.1 自定义函数

函数执行特定的操作并返回一个值1,你可以调用它。一般而言,要判断某个对象是否可调用,可使用内置函数callable。

import math
x = 1 
y = math.sqrt
callable(x)
# False
callable(y)
# True

使用def来定义函数。

def hello(name):
	return 'Hello, ' + name + '!'

6.1.1 给函数编写文档

在有些地方,如def语句后面(以及模块和类的开 头,这将在第7章和第10章详细介绍),添加这样的字符串很有用。放在函数开头的字符串称为 文档字符串(docstring),将作为函数的一部分存储起来。
特殊的内置函数help很有用。在交互式解释器中,可使用它获取有关函数的信息,其中包含 函数的文档字符串。

def square(x):
    'Calculates the square of the number x.' 
    return x * x
# 访问函数文档字符串
square.__doc__
# 'Calculates the square of the number x.' 
help(square)
# Help on function square in module __main__:

# square(x)
#    Calculates the square of the number x.

6.1.2 其实并不是函数的函数

在Python中,有些函数什么都不返回。什么都不返回的函数不包含return语句,或者包含return语句,但没 有在return后面指定值。

def test():
    print('This is printed') 
    return
x = test()
# This is printed
print(x)
# None

由此可知,所有的函数都返回值。如果你没有告诉它们该返回 什么,将返回None。

6.2 参数魔法

在def语句中,位于函数名后面的变量通常称为形参,而调用函数时提供的值称为实参,在很重要的情况下,我会将实参称为值

6.2.1 我能修改参数吗

函数通过参数获得了一系列的值,你能对其进行修改吗?如果这样做,结果将如何?参数不过是变量而已,行为与你预期的完全相同。在函数内部给参数赋值对外部没有任何影响。

def try_to_change(n):
    n = 'Mr. Gumby'
name = 'Mrs. Entity'
try_to_change(name)
name
# 'Mrs. Entity'

在try_to_change内,将新值赋给了参数n,但如你所见,这对变量name没有影响。在函数内部重新关联参数(即给它赋值)时,函数外部的变量不受影响。参数存储在局部作用域内.
但如果参数为可变的数据结构(如列表)呢?

def change(n):
    n[0] = 'Mr. Gumby'
names = ['Mrs. Entity', 'Mrs. Thing']
change(names)
names
# ['Mr. Gumby', 'Mrs. Thing']

而在这个示例中,修改了变量关联到的列表。这是因为将同一个列表赋给两个变量时,这两个变量将同时指向这个列表。 就这么简单。下面这个示例说明了这种情况。

names = ['Mrs. Entity', 'Mrs. Thing']
n = names # 再次假装传递名字作为参数 >>> 
n[0] = 'Mr. Gumby' # 修改列表
names
# ['Mr. Gumby', 'Mrs. Thing']
使用函数来修改数据结构(如列表或字典)是一种不错的方式

抽象的关键在于隐藏所有的更新细节,为此可使用函数。

def init(data): 
	data['first'] = {} 
	data['middle'] = {} 
	data['last'] = {}
storage = {}
init(storage)
# storage
# {'first': {}, 'middle': {}, 'last': {}}

6.2.3 关键字参数和默认值

有时候,参数的排列顺序可能难以记住,尤其是参数很多时。为了简化调用工作,可指定参 数的名称。 然而,关键字参数最大的优点在于,可以指定默认值。

def hello_1(greeting, name):
	print('{}, {}!'.format(greeting, name))
hello_1(greeting='Hello', name='world')
def hello_3(greeting='Hello', name='world'): 
	print('{}, {}!'.format(greeting, name))
hello_3()
# Hello, world!
hello_3('Greetings', 'universe')
# Greetings, universe!

6.2.4 收集参数

有时候,允许用户提供任意数量的参数很有用。为此,应允许用户提供任意数量的姓名。实际上,这实现起来并不难。
请尝试使用下面这样的函数定义:

def print_params(*params): 
	print(params)
print_param('Testing')
# 'Testing',
print_param('Testing','Hello')
# 'Testing','Hello'
def print_params_2(title, *params):   # 因此星号意味着收集余下的位置参数。
    print(title)
    print(params)
# Params:
# (1, 2, 3)

与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况 下你需要做些额外的工作:使用名称来指定后续参数。

def in_the_middle(x,*y,z):
    print(x,y,z)
in_the_middle(1,2,3,4,z=6)
# 1 (2, 3, 4) 6

星号不会收集关键字参数。要收集关键字参数,可使用两个星号。

def print_params_3(**params): 
    print(params)
print_params_3(x=1,y=2,z=3)
# {'x': 1, 'y': 2, 'z': 3}
def print_params_4(x, y, z=3, *pospar, **keypar): 
    print(x, y, z)
    print(pospar) 
    print(keypar)
print_params_4(1, 2, 3, 5, 6, 7, foo=1, bar=2)
# 1 2 3
# (5, 6, 7)
# {'foo': 1, 'bar': 2}

6.2.5 分配参数

def add(x, y): 
    return x + y
params = (1, 2)
add(*params)
# 3
def hello_3(greeting='Hello', name='world'): 
	print('{}, {}!'.format(greeting, name))
params = {'name': 'Sir Robin', 'greeting': 'Well met'}
hello_3(**params)
# Well met, Sir Robin!

6.3 作用域

在函数内使用的变量称为局部变量(与之相对的是全局变量)。参数类似于局部变量,因此参数与全局变量同名不会有任何问题。
如果有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量遮住了。必要时可使用globals()[‘parameter’]来访问它。

def combine(parameter):
	print(parameter + globals()['parameter'])
parameter = 'berry'
combine('Shrub')
# Shrubberry

在函数内部给变量赋值时,该变量默认为 局部变量,除非你明确地告诉Python它是全局变量。那么如何将这一点告知Python呢?

x = 1
def change_global():
	global x 
	x=x+1
change_global()
# x = 2

Python函数可以嵌套,即可将一个函数放在另一个函数内。

6.4 递归

通常递归包括两个部分:

  • 基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。
  • 递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。
# 阶乘的递归
def factorial(n):
	if n == 1:
		return 1
	else:
		return n * factorial(n-1)
# factorial(10) = 3628800

# 幂函数的递归
def power(x, n): 
    if n == 0:
        return 1 
    else:
        return x * power(x, n - 1)
# power(2,3) = 8

6.4.2 二分查找

这里的关键是,这种算法自然而然地引出了递归式定义和实现。先来回顾一下定义,确保你知道该如何做。

  • 如果上限和下限相同,就说明它们都指向数字所在的位置,因此将这个数字返回。
  • 否则,找出区间的中间位置(上限和下限的平均值),再确定数字在左半部分还是右半部分。然后在继续在数字所在的那部分中查找。
def search(sequence, number, lower=0, upper=None): 
    if upper is None: 
        upper = len(sequence) - 1
    if lower == upper:
        assert number == sequence[upper]
        return upper 
    else:
        middle = (lower + upper) // 2 
        if number > sequence[middle]:
            return search(sequence, number, middle + 1, upper) 
        else:
            return search(sequence, number, lower, middle)
seq = [34, 67, 8, 123, 4, 100, 95] 
seq.sort()
search(seq, 34)
# 2         
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值