之前总结了Python的基本元素,容器,控制流,接下来总结Python的盒子。
一、函数
1. 函数的格式:
def 函数名(参数): #参数可有可无
函数体
解决之前心中的疑惑:为何函数中的参数可以不赋值,就是一个单独的变量名。因为函数在调用的时候会有一个调用语句,这个时候才会传递一个数值给函数中的变量。
def greet_user(username):
'''显示简单的问候语''' #文档字符串:描述函数是做什么,三引号表示
print('Hello, ' + username.title() + '!')
greet_user('jesse')
2. 函数的一个主要目的是将需要多次执行的代码放在一起。
这也是在写代码过程中的一个原则,尽量消除重复,因为后面一旦发现重复代码需要修改的,会很麻烦。
另外,要关注一个函数是为了实现一个功能的一段代码,要时刻关注函数实现的功能。有时候甚至不需要关注函数体代码,只需要知道输入和输出就可以了。
3. 参数是传入到函数中,参与函数体代码执行的变量。可有可无,这个变量在函数执行结束之后就立刻销毁了。
一个函数可以接受任何数量(包括0)的任何类型的值作为输入变量,并且返回任何数量(包括0)的任何类型的结果。
4. 用def语句创建函数时,可以用return语句执行应该返回什么值。
return 函数返回值或者表达式(可以是任何类型);
如果函数使用了不带值的return语句,就返回None;如果函数不显式调用return函数 默认返回None。
1)返回简单值
def get_formatted_name(first_name, last_name):
'''返回整洁的姓名'''
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
Jimi Hendrix
调用函数的返回值时,需要提供一个变量用于存储返回值。
2)返回字典
函数可返回任何类型的值,包括列表和字典等较复杂的数据结构。
def build_person(first_name, last_name):
'''曉夞堦槩帤揟丆懘拞曪娷桳.堦槩恖揑怣懅'''
person = {'first': first_name, 'last': last_name}
return person
musician = build_person('jimi', 'hendrix') #调用函数的返回值,需要提供一个变量用于存储返回值。
print(musician)
{'first': 'jimi', 'last': 'hendrix'}
5. 局部和全局作用域
1)局部作用域:在被调用函数内赋值的变元和变量,处于该函数的局部作用域;
2)全局作用域:在所有函数之外赋值的变量,属于全局作用域;
3)局部变量:处于局域作用域的变量,成为局部变量;
4)全局变量:处于全局作用域的变量,成为全局变量;一个变量必是其中一种,不能即使局部的又是全局的。
5)一个函数被调用时,就创建了一个局部作用域;在这个函数内赋值的所有变量,存在于该局部作用域内。
函数返回时,局部作用域和变量就丢失了。
6)全局作用域不能使用局部变量;
7)局部作用域可以访问全局变量;
8)一个函数局部作用域中代码,不能使用其他局部作用域的变量;
9)在编写函数的时候,不鼓励使用全局变量,这样调用起来方便,只需要关注函数的输入和结果即可。
6. 如果需要在一个函数内修改全局变量,需要用global语句
def spam():
global eggs
eggs = 'spam'
eggs = 'global'
spam()
print(eggs)
spam
7. 区分一个变量是局部作用域还是全局作用域:
1)如果变量在全局作用域中使用(即在所有函数之外),它就总是全局变量。
2)如果在一个函数中,有针对该变量的global语句,它就是全局变量;
3)否则,如果该变量用于函数中的赋值语句,它就是局部变量;
4)但是,如果该变量没有用在赋值语句中,它就是全局变量。
8.参数
1)形参:定义函数中的参数;实参:调用函数的时候传递给函数的参数
2)传递参数
a. 位置实参,注意是实参
在调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此
最简单的关联方式是基于实参的顺序。这种关联方式称为位置实参
def describe_pet(animal_type, pet_name):
'''.帵.暔揑怣懅'''
print('\nI have a ' + animal_type + '.')
print('My ' + animal_type + ''s name is ' + pet_name.title() + '.')
describe_pet('hamster', 'harry')
I have a hamster.
My hamster's name is Harry.
b. 关键字实参,注意是实参
关键字实参是传递给函数的名称--值对,你直接在实参中将名称和值关联起来了。因此向函数传递实参的时候不会混淆
def describe_pet(animal_type, pet_name):
'''显示宠物的信息'''
print('\nI have a ' + animal_type + '.')
print('My ' + animal_type + ''s name is ' + pet_name.title() + '.')
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster')
c. 默认值参数,注意是形参
编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值,否则,将使用形参的默认值。
def describe_pet(pet_name, animal_type='dog'):
'''显示宠物的信息'''
print('\nI have a ' + animal_type + '.')
print('My ' + animal_type + ''s name is ' + pet_name.title() + '.')
describe_pet(pet_name='willie')
I have a dog.
My dog's name is Willie.
或者使用describe_pet('willie') 这时候实参是把值传递给了pet_name,因为后面的形参函数已经给出了默认值。
使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的实参。这让Python依然能够正确地解读位置实参。
d. 让实参变成可选的,注意是实参
在实际情况下,并不是任何一个形参都需要对应实参去传递值,所以可以将实参变成可选的
def get_formatted_name(first_name, last_name, middle_name=''):
'''返回整洁的姓名'''
if middle_name:
full_name = first_name + ' ' + middle_name + ' ' + last_name
else:
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)
Jimi Hendrix
John Lee Hooker
e. 传递任意数量的实参
形参名*toppings中的星号让Python创建一个名为toppings的空元组,
def make_pizza(*toppings):
'''打印顾客点的所有配料'''
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')
def make_pizza(*toppings):
'''概述要制作的比萨'''
print('\nMaking a pizza with the following toppings:')
for topping in toppings:
print('- ' + topping)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
Making a pizza with the following toppings:
- pepperoni
Making a pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
f. 结合使用位置参数和任意数量实参
def make_pizza(size, *toppings):
'''概述要制作的比萨'''
print('\nMaking a ' + str(size) +
'-inch pizza with the following toppings:')
for topping in toppings:
print('- ' + topping)
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
g. 形参**kwargs中的两个星号让Python创建一个名为kwargs的空字典,参数的名字是字典的键,对应参数的值是字典的值。
>>> def print_kwargs(**kwargs):
print('Keyword arguments:', kwargs)
>>> print_kwargs(wine='merlot', entree='mutton', dessert='macaroon')
Keyword arguments: {'dessert': 'macaroon', 'wine': 'merlot', 'entree': 'mutton'}
h. 传递列表
你经常会发现,向函数传递列表很有用,这种列表包含的可能是名字、数字或更复杂的对象(如字典)。将列表传递给函数后,函数就能直接访问其内容。下面使用函数来提高
处理列表的效率。
def greet_users(names):
'''向列表中的每位用户都发出简单的问候'''
for name in names:
msg = 'Hello, ' + name.title() + '!'
print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
Hello, Hannah!
Hello, Ty!
Hello, Margot!
1)在函数中修改列表
将列表传递给函数后,函数就可对其进行修改。在函数中对这个列表所做的任何修改都是永久性的,这让你能够高效地处理大量的数据。
# 首先创建一个列表,其中包含一些要打印的设计
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
# 模拟打印每个设计,直到没有未打印的设计为止
# 打印每个设计后,都将其移到列表completed_models中
while unprinted_designs:
current_design = unprinted_designs.pop()
#模拟根据设计制作3D打印模型的过程
print('Printing model: ' + current_design)
completed_models.append(current_design)
# 显示打印好的所有模型
print('\nThe following models have been printed:')
for completed_model in completed_models:
print(completed_model)
Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone case
2)禁止函数修改列表
有时候,需要禁止函数修改列表。
为解决这个问题,可向函数传递列表的副本而不是原件;这样函数所做的任何修改都只影响副本,而丝毫不影响原件。
function_name(list_name[:])
其中list_name是函数外的一个列表,通过list_name[:]向函数传递一个列表副本
9. 函数的优点之一是,使用它们可将代码块与主程序分离。通过给函数指定描述性名称,可让主程序容易理解得多。你还可以更进一步,将函数存储在被称为模块 的独立文件中,
再将模块导入 到主程序中。
10. 内部函数
内部函数是指:在函数中定义另外一个函数
>>> def outer(a, b):
def inner(c, d):
return c + d
return inner(a, b)
>>> outer(4, 7)
11
当需要在函数内部多次执行复杂的任务时,内部函数是非常有用的,从而避免了循环和代码的堆叠重复。
>>> def knights(saying):
def inner(quote):
return 'We are the knights who say: '%s'' % quote
return inner(saying)
>>> knights('Ni!')
'We are the knights who say:'Ni!''
11. 闭包
内部函数可以看作一个闭包,闭包是一个可以由另一个函数动态生成的函数,并且可以改变和存储函数外创建的变量的值。
12.函数编写指南
1)应该给函数指定描述性名称,且只在其中使用小写字母和下划线。
2)给模块命名也应该遵循上述约定
3)每个函数都应包含简要地阐述其功能的注释,该注释应紧跟在函数定义后面,并采用文档字符串格式。文档良好的函数让其他程序员只需阅读文档字符串中的描述就能够使用
它:他们完全可以相信代码如描述的那样运行;只要知道函数的名称、需要的实参以及返回值的类型,就能在自己的程序中使用它
4)给形参指定默认值时,等号两边不要有空格:
def function_name(parameter_0, parameter_1='default value')
5)对于函数调用中的关键字实参,也应遵循这种约定:
function_name(value_0, parameter_1='value')
6)如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一个函数在什么地方结束,下一个函数从什么地方开始
7)所有的import 语句都应放在文件开头,唯一例外的情形是,在文件开头使用了注释来描述整个程序。
二、模块
1. 我们先梳理一下结构,比如说单词、句子、段落、章,那对于Python而言,数据类型类似于单词、语句类似于句子,函数类似于段落,
模块类似于章。
2. import 语句允许在当前运行的程序文件中使用模块中的代码。
通过将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上。这还能让你在众多不同的程序中重用函数。将函数存储在独立文件中后,可与其他程
序员共享这些文件而不是整个程序。知道如何导入函数还能让你使用其他程序员编写的函数库。
导入模块的方法有多种,下面对每种都作简要的介绍。
1)导入整个模块
模块是扩展名为.py的文件,包含要导入到程序中的代码
一个名为pizza.py的模块:
def make_pizza(size, *toppings):
'''概述要制作的比萨'''
print('\nMaking a ' + str(size) +
'-inch pizza with the following toppings:')
for topping in toppings:
print('- ' + topping)
接下来,在另一个文件中导入模块,并调用函数:
import pizza
pizza.make_pizza(16, 'pepperoni') #注意调用的格式 模块名.函数()
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
2)导入特定的函数
导入特定函数格式如下:
from module_name import function_name0, function_name1等
from pizza import make_pizza
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
#注意和导入整个模块调用的格式差别
3)使用as给函数指定别名
如果要导入的函数的名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定剪短而独一无二的别名
from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')
4)使用as给模块指定别名
你还可以给模块指定别名。通过给模块指定简短的别名,让你能够更轻松的调用模块中的函数。
import pizza as p
p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
5)导入模块中的所有函数(为了代码便于理解,不推荐这种方法)
使用星号*运算符可让Python导入模块中的所有函数
from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
3.通常情况下,让解释器能正确的访问模块有两种方法:第一种:将模块放置在合适的位置;第二种:告诉解释器在哪里寻找模块:
1)将模块放置在正确的位置
只需要找出Python解释器从哪里查找模块,然后将自己的文件放置在那里即可。
2)告诉编译器去哪里找
有时候1)情况并不适用,比如以下几种情况,不希望将自己的模块填满Python解释器目录、没有Python解释器目录中存储文件的权限
、想将模块放在其他地方
标准方法:在PYTHONPATH环境变量中包含模块所在的目录。环境变量并不是Python解释器的一部分,它们是操作系统的一部分。
>>> import sys #这种方法并非标准方法
>>> sys.path.append('c:/python') #告诉解释器:除了从默认的目录中寻找之外,还需要从目录c:/python中寻找模块
完成这个步骤之后,就能导入自己的模块了
>>> import hello
Hello, world!
4. 导入模块的实际意义以及导入模块的次数:导入模块并不意味着导入时执行某些操作,它们主要用于定义,比如变量、函数和类等
此外,因为只需要定义这些东西一次,所以导入模块多次和导入一次的效果是一样的。
5. 将代码模块化的目的就是:代码重用,代码模块化之后,这个模块就可以在多个程序中多次使用。
6. 如何探究模块:
例子1. 导入模块,如上总结
import copy
例子2.查看模块包含的内容,使用dir()函数,会将对象的所有特性(模块的所有函数、类、变量等)列出
#一些以下划线_开始的名字,暗示其并不是为了在模块外部使用而准备的
[n for n in dir(copy) if not n.startswith('_')]
例子3. _all_变量,定义了模块的公共接口,更准确的说,它告诉解释器:从模块导入所有名字代表什么含义
from copy import *
这样,你就能使用_all_变量的四个函数。如果没有设置_all_,import * 默认会导入模块中所有不以下划线开头的所有全局变量。
例子3. help()函数
>>> help(copy.copy)
7. 标准库,作为一个单独知识点研究,总结,记忆。
三、包
1. 为了使Python应用更具可扩展性,可以把多个模块分组,称之为包。
2. 当模块存储在文件时,包就是文件所在的目录;为了让Python将其作为包对待,它必须包含一个命名为_init_.py的文件。
3. 为了将模块放置在包内,直接把模块放在包目录内即可。
例子1.建立一个名叫drawing的包,其中包含colors和shapes模块,需要创建如下目录:
1)~/python/ PYTHONPATH 的目录
2)~/python/drawing/ 包目录(drawing包)
3)~/python/drawing/_init_py 包代码(drawing模块)
4)~/python/drawing/colors.py colors模块
5)~/python/drawing/shapes.py shapes模块
import drawing
import drawing.colors
from drawing import shapes