Python基础教程(第三版)读书笔记(6)

抽象

抽象和结构

给你一个任务,对1、2、3… 、n之间的数求和,这个问题一个循环就可以解决。

>>> for i in range(1, n+1):
	sum += i

这很简单,但是如果需要多次使用呢?如果按照上面那样,代码就会很乱。

>>> for i in range(1, n+1):
	sum += i
	.....
>>> for i in range(1, n+1):
	sum += i
	.....
>>> for i in range(1, n+1):
	sum += i
	.....

这样代码会很多,另外也有大量重复的代码,很没有必要。那么真正的程序员会怎么做呢?

>>> sum(1,n)
	.....
>>> sum(1,n)
	.....
>>> sum(1,n)
	.....

上面这种形式就是抽象,只具体编写了这个程序独特的部分(获得求和范围并打印结果)。这样在需要在多个地方计算求和时,这样做可节省很多精力。
抽象可节省人力,但实际上还有个更重要的优点: 抽象是程序能够被人理解的关键所在(无论对编写程序还是阅读程序来说,这都至关重要。)Python中提供了抽象这种“功能”,也就是函数

自定义函数

def语句

函数执行特定的操作并返回一个值(也可不返回),调用的时候提供参数(可没有)。可以使用内置函数callable判断某个对象是否可调用。

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

那么如何定义函数呢,Python中使用def(define)语句定义函数。下面是之前求和函数的定义。

>>> def sum(start, end):
	sum = 0
	for i in range(start, end+1):
		sum += i
	return sum

sum函数有两个参数,分别是求和范围的开始和结束。求得结果后,使用return语句返回结果。return语句可以结束函数。

给函数编写文档

我们还可以给函数编写文档,以确保其他人能够理解,可以添加注释。或者在def语句后面添加独立的字符串。放在函数开头的字符串称为文档字符串,将作为函数的一部分存储起来。下面是一个演示:

>>> def square(x):
	'Calculates the square of the number of x.'
	return x * x

可以使用.doc 访问文档字符串。

>>> square.__doc__
'Calculates the square of the number of x.'

使用内置函数help可以获取有关函数的文档字符串。

>>> help(square)
Help on function square in module __main__:

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

参数魔法

关于修改参数

和c一样,Python中位于函数名后面的变量称为形参,调用函数时提供的值称为实参。
通常我们是不能修改参数的,因为作用域的问题。

>>> def try_to_change(n):
	n = 'Mr. Gumby'

	
>>> name = 'Mr. Yang'
>>> try_to_change(name)
>>> name
'Mr. Yang'

参数并没有改变。如果参数是元组或者字符串这种不可变的参数,那么在函数中本就无法影响到参数的值。但是如果使用的是列表这种可变的数据结构,那么可能会有这种情况

>>> def change(n):
	n[0] = 'Mr. Gumby'
	pass

>>> names = ['Mrs.Entity', 'Mrs.Ting']
>>> change(names)
>>> names
['Mr. Gumby', 'Mrs.Ting']

列表改变了,为什么会这样呢?我们不用函数模仿一下这个函数的过程。

>>> names = ['Mrs.Entity', 'Mrs.Ting']
>>> n = names # 再次假装传递名字作为参数。
>>> n[0] = 'Mr. Gumby'
>>> names
['Mr. Gumby', 'Mrs.Ting']

可以看出,这种现象的原因是,常规复制将另一个名称关联到列表。

>>> names is n
True

若想避免这种情况,要使用深复制,创建列表的副本。

>>> names = ['Mrs.Entity', 'Mrs.Ting']
>>> n = names[:]
>>> n is names
False
>>> n[0] = 'Mr.Gumby'
>>> n
['Mr.Gumby', 'Mrs.Ting']
>>> names
['Mrs.Entity', 'Mrs.Ting']

使用这种方法将不会影响到names。因为参数n包含的是副本,所以原始列表是安全的。

>>> change(names[:])
>>> names
['Mrs.Entity', 'Mrs.Ting']

另外如果参数是不可变的,但是需要修改参数的值,应该从函数返回所有需要的值。

>>> def inc(x):
	return x+1

>>> foo = 10
>>> foo = inc(foo)
>>> foo
11

关键字参数和默认值

前面学习的都是位置参数,形参和实参依次对应。位置比名称重要。举个例子

>>> def hello_1(greeting, name):
	print('{}, {}!'.format(greeting, name))

	
>>> def hello_2(name, greeting):
	print('{}, {}!'.format(name, greeting))

	
>>> hello_1('Hello', 'world')
Hello, world!
>>> hello_2('Hello', 'world')
Hello, world!

当参数很多时,参数的排列顺序就不是很好记了,这时候可以指定参数的名称。这样参数的顺序就无关紧要了。

>>> hello_1(greeting='Hello', name='world')
Hello, world!

像这样使用名称指定的参数称为关键字参数。可以澄清各个参数的作用。另外还可以给参赛设置默认值

>>> def hello_3(greeting='Hello', name='world'):
	print("{}, {}!".format(greeting, name))

	
>>> hello_3() # 关键字参数,可以指定默认值。
Hello, world!

给参数指定默认值后,调用函数时可不提供它。

>>> hello_3('Greeting')
Greeting, world!
>>> hello_3('jk')
jk, world!
>>> hello_3(name='Gumby')
Hello, Gumby!

收集参数

当参数过多时,使用函数会报错。 不过Python提供了收集函数的符号星号( * )。

>>> def print_params(*params):
	print(params)

	
>>> print_params('Testing')
('Testing',)

前面有星号的参数将被放在元组中。

>>> print_params(1, 2, 3)
(1, 2, 3)

如果没有可供收集的参数,params将是一个空元组。

>>> print_params()
()

带星号的参数可以放在其他位置,但是和赋值时不同,这是需要使用名称来指定后续参数。

>>> def in_the_middle(x, *y, z):
	print(x, y, z)

>>> in_the_middle(1, 2, 3, 4, 5, 6, z=7)
1 (2, 3, 4, 5, 6) 7

星号不会收集关键字参数。所以需要在后面指定后续参数。要收集关键字参数,可使用两个星号。

>>> def print_params_3(**params):
	print(params)

>>> print_params_3(x=1, y=2, z=3)
{'x': 1, 'y': 2, 'z': 3}

这种情况得到的是一个字典而不是元组。

分配参数

*号可以收集参数,也可以分配参数。

>>> params = (1, 2)
>>> def add(x, y):
	return x + y
	
>>> add(*params)
3

只有在定义函数(允许可变数量的参数)或调用函数时(拆分字典或序列)使用,星号才能发挥作用。
下面看一个使用参数的综合实例。

def story(**kwds):
    return 'Once upon a time, there was a' \
           '{job} called {name},'.format_map(kwds)
def power(x, y, *others):
    if others:
        print('Received redundant parameters:', others)
    return pow(x, y)
def interval(start, stop=None, step=1):
    'Imitates range() for step > 0'
    if stop is None:
        start, stop = 0, start    # 如果没有给参数stop指定值,就调整参数start和stop的值。
    result = []

    i = start                     # 从start开始往上数。
    while i < stop:               # 数到stop位置
        result.append(i)          # 将当前数的数附加到result末尾
        i += step
    return result

测试一下

>>> print(story(job='King', name='Gumby'))
Once upon a time, there was aKing called Gumby,
>>> params = (5,) * 2
>>> power(*params)
3125
>>> interval(3, 12, 4)
[3, 7, 11]
>>> interval(3)
[0, 1, 2]

作用域

每个函数调用时都将创建一个命名空间作用域(“看不见的字典”)如果要在局部变量中访问相同名的全局变量,使用函数globals(),这个函数会返回一个包含全局变量的字典。(locals 返回一个包含局部变量的字典)

>>> def combine(parameter):
	print(parameter + globals()['parameter'])

	
>>> parameter = 'berry'
>>> combine('Shrub')
Shrubberry

也可以使用global 告诉Python变量是全局变量。

>>> x = 1
>>> def change_global():
	global x
	x += 1

>>> change_global()
>>> x
2

小结

  • 抽象:抽象是隐藏不必要细节的艺术。通过定义处理细节的函数,可让程序更抽象。
  • 函数定义:函数是使用def语句定义的。函数由语句块组成,它们从外部接受值(参数),并可能返回一个或多个值(计算结果)
  • 参数:函数通过参数(调用函数时被设置的变量)接收所需的信息。在Python中,参数有两类:位置参数和关键字参数。通过给参数指定默认值,可使其变成可选的。
  • 作用域:变量存储在作用域(也叫命名空间)中。在Python中,作用域分两大类:全局作用域和局部作用域。作用域可以嵌套。
  • 递归:函数可调用自身,这称为递归。可使用递归完成的任何任务都可以使用循环来完成,但有时使用递归函数的可读性更高。
  • 函数式编程:Python提供了一些函数式编程工具,其中包括lambda表达式以及filter和reduce。

学习的新函数:

函数 描述
map(func, seq[, seq, …]) 对序列中的所有元素都执行函数
filter(func, seq) 返回一个列表,其中包含对其执行函数时结果为真的所有元素
reduce(func, seq[, initial]) 等价于func(func(func(seq[0], seq[1]), seq[2]), …)
sum(seq) 返回seq中所有元素的和
apply(func[, args[, kwargs]]) 调用函数(还提供要传递给函数的参数)

注: 小结摘抄于Python基础教程(第三版)

发布了14 篇原创文章 · 获赞 8 · 访问量 226

猜你喜欢

转载自blog.csdn.net/weixin_43582715/article/details/104028345