Python学习笔记总结(二)函数和模块

1、函数
函数的做用:能够计算出一个返回值,最大化代码重用,最小化代码冗余,流程的分解。python

一、函数相关的语句和表达式
语句        例子
Calls        myfunc(‘diege','eggs',meat=‘lit’) #使用函数
def,return,yield      def adder(a,b=1,*c):
                       return a+b+c[0]
global        changer():
                global x;x='new'
lambda        Funcs=[lambad x:x**2,lambad x:x*3]sql

二、编写函数
def建立了一个对象并将其赋值给某一个变量名。
return将一个结果对象发送给调用者。
函数是经过赋值(对象引用)传递的。
参数经过赋值传递给函数。
global声明了一个模块级的变量并被赋值。
参数,返回值以及变量并非声明
def <name>(arg1,age2,...,agrN):
    <statements>
     return <value
函数主体每每都包含了一个return语句(不是必须的),若是它没有出现,那么函数将会在控制流执行完函数主体时结束,技术上,没有返回值的函数自动返回了none对象。
return能够在函数主体中的任何地方出现。它表示函数调用的结束,并将结果返回至函数调用。

不要尝试判断传入参数的对象类型,这样实质上破坏了函数的灵活性,把函数限制在特定的类型上
数据库

三、做用域
做用域:变量定义以及查找的地方。
本地变量:在函数内定义的变量为本地变量。
全局变量:在文件内[模块内]函数外定义的变量为全局变量
Python建立,改变或者查找变量名都是在所谓的命名空间(一个保存变量名的地方,模块中为__main__)中进行。做用域这个术语指的就是命名空间。
也就说,在代码中变量名被赋值的位置决定了这个变量名能被访问到的范围
一个函数全部变量名都与函数的命名空间相关联。
*def内定义变量名def内使用,是本地变量
*def之中的变量名与def以外的变量名不发生冲突,使用别处相同的变量名也没问题。
函数定义了本地做用域,而模块定义了全局做用域。两做用域关系。
*内嵌的模块是全局做用域:对于外部的全局变量就成为了一个模块对象的属性
*全局做用域的做用范围仅限单个文件:不要被全局迷惑,这里的全局是一个文件的顶层的变量名,仅对这个文件内部的代码而言是全局。
Python中,没有一个无所不包的情景文件做用域。替代的方法是,变量名由模块文件隔开,必须精准地导入一个模块文件才可以使用这文件中
定义的变量名,
*每次对函数的调用都建立了一个新的本地做用域。
*赋值的变量名废除声明为全局变量,不然均为本地变量。
*所用的变量名均可以概括为本地,全局,或者内置。(内置:ptyhon预约义的__builtin__模块提供的)

变量名解析:LEGB原则
对一个def语句
*变量名引用分为三个做用域进行查找:首先查找本地,而后是函数内(若是有),以后全局,最后内置。
*默认状况下,变量名赋值会建立或改变本地变量
*全局声明将赋值变量名映射到模块文件内部的做用域。

global
global语句包含关键字global
*全局变量是位于模块文件内部顶层的变量名
*全局变量若是是在函数内部被赋值的话,并需通过声明
*全局变量名在函数的内部不通过声明也能够被引用

四、传递参数
参数传递:传递给函数做为其输入对象的方式
*参数的传递是经过自动将对象赋值给本地变量来实现的。
*在函数内部的参数名的赋值不会影响调用者。
*改变函数的可变对象参数的值也许会对调用者有影响。
换句话说,由于参数是简单的经过赋值进行对象的传递,函数可以改变传入的可变对象,所以其结果会影响调用者。
*不可变参数是“经过值”进行传递。
像整数和字符串这样的对象是经过对象引用而不是拷贝进行传递的,可是由于你不管如何都不可能在原处改变不可变对象,实际的效果就是很像建立了一份拷贝。
可变对象是经过“指针”进行传递的。

避免可变参数的修改
在Python中,默认经过引用(也就是指针)进行函数的参数传递。若是不想在函数内部在原处的修改影响传递给它的对象。那么,可以简单的建立一个可变对象的拷贝。
咱们老是可以在调用时对列表进行拷贝
L=[1,2]
changer(X,L[:])
若是不想改变传入的对象,不管函数是如何调用的,咱们能够在函数内部进行拷贝,避免可变参数的修改
>>> def changer(a,b):
...     a=2
...     b=b[:]
...     b[0]='diege'

特定参数匹配模型
参数在ptyhon中老是经过赋值进行传递,传入的对象赋值给了在def头部的变量名。尽管这样,在模型的上层,python提供了额外的工具,该工具改变了调用过中
赋值时参数对象匹配在头部的参数名的优先级。这些工具是可选的。

总结与特定模式有关的语法:
语法            位置    解释
func(value)        调用者    常规参数,经过位置进行匹配,从左到右进行匹配
func(name=value)    调用者    关键字参数,经过变量名匹配
func(*name)        调用者    以name传递全部的对象,并做为独立的基于位置的参数
func(**name)        调用者    以name成对的传递全部的关键字/值,并做为独立的关键字的参数

def func(name)        函数    常规参数:经过位置或变量名进行匹配
def func(name=value)    函数    默认参数值:若是没有在调用中传递的话,就是用默认值
def func(*name)        函数    匹配并收集(在元组中)全部包含位置的参数
def func(**name)    函数    匹配并收集(在字典中)全部包含位置的参数。

五、匿名函数:lamdba
lambad 建立了一个以后可以被调用的函数,它返回了一个函数而不是将这个函数赋值给一个变量名。
lambda表达式
lanbda arg1,arg2,...,argN:expression using arguments
lambda 是一个表达式,而不是一个语句
lambda的主体是一个单个的表达式,而不是代码块
func(x,y,z):return x+y+z
默认参数也可以再lambda参数中使用,就像在def中使用同样。
lambda a='free',b='file',c='feel':a+b+c

六、做参数来应用函数
内置函数apply
当须要变得更加动态的话,能够经过将一个函数做为一个参数传递给apply来调用一个生成的函数,而且
也将传给那个函数的参数做为一个元组传递给apply函数()
apply(函数,参数1(元组),参数2(元组))

七、在序列中映射函数:map
使用内置工具map,map函数会对一个序列对象中的每个元素应用被传入的函数,而且返回一个包含了全部函数调用结果的一个列表。
map(函数,传入函数的序列对象)
>>> def inc(x):return x+10
>>> L=[1,2,3,4,5]
>>> map(inc,L)
[11, 12, 13, 14, 15]
>>> L=[1,2,3,4,5]
map嵌套lambda     
>>> map((lambda x:x+3),L)
[4, 5, 6, 7, 8]
高级功能:提供了多个序列做为参数,它可以并行返回分别以每一个序列的元素做为【函数对应参数】获得的结果的列表
>>> pow(3,4)
81
>>> map(pow,[1,2,3],[2,3,4])    #1**2,2**3,3**4
[1, 8, 81]

八、函数式编程工具:filter和reduce
函数式编程的意思就是对序列应用一些函数的工具。
基于某一测试函数过滤出一些元素-filter
对每对元素都应用函数并运行到最后结果-reduce
>>> filter((lambda x:x>0),range(-5,5))
[1, 2, 3, 4]
这个等效于for range:if语句
reduce稍微复杂一点。这里两个reduce调用,计算在一个列表中全部元素加起来和以及乘起来的乘积
>>> reduce((lambda x,y:x+y),[1,2,3,4])
10
>>> reduce((lambda x,y:x*y),[1,2,3,4])
24
依次计算

九、重访列表解析:映射
1)、列表解析基础
>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> map((lambda x:x**2),range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
2)、增长测试和嵌套循环
在for以后编写if分支,用来增长逻辑选择,if分支至关filter
>>> [x for x in range(5) if x%2==0]
[0, 2, 4]
>>> filter((lambda x:x%2==0),range(5))
[0, 2, 4]
3)filter出来的列表能够做为map的第2个参数
>>> map((lambda x:x**2),filter((lambda x:x%2==0),range(5)))
[0, 4, 16]
列表解析和map的对比
二者相似,都会收集对序列或其余可迭代对象中每一个元素应用元算后的结果(一次一个项目),从而建立新列表。其主要差别在于。
map会对每一个元素应用函数,而列表解析则是应用任意的表达式。所以,列表解析更通用一些,能够像map那样应用函数调用表达式,
可是,map须要一个函数才能应用其余种类的表达式(函数是map的第一参数).列表解析也支持扩展语法,若是,嵌套for循环和if
分句从而能够包含内置函数filter的功能。

十、重返迭代器:生成器
编写的函数可以返回一个值,而且稍后还能够从它刚才离开的地方仍然返回值。这样的函数被认做是生成器,由于它们随时间生成一个序列的值。
大多数方面生成器函数就像通常函数,在Python它们被自动用做实现迭代协议,因它只可以再迭代的语境中出现。
生成器和通常的函数之间代码上最大的不一样就是一个生成器yield一个值,而不是return一个值。yield语句将会将函数关起,并向它的调用者返回一个值
可是保存足够的状态信息为了让其可以在函数从它挂起的地方恢复。
包含yield的语句的函数将会特意编译成为生成器。当调用它时,他们返回一个生成器对象,这个生成器对象支持迭代器对象接口。
>>> def Dtest(N):
...     for i in range(N):
...             yield i**2
使用
>>> for i in Dtest(5):
...     print i,':',
...
0 : 1 : 4 : 9 : 16 :
查看过程
>>> x=Dtest(4)
>>> x.next()
0
>>> x.next()
十一、函数设计概念
*耦合性:对于输入使用参数,而且对于输出使用return语句
*耦合性:只有在真正必要的状况下使用全局变量。
*耦合性:不要改变可变类型的参数,除非调用者但愿这样作。
*聚合性:每个函数都应该有一个单一的,同一的目标
*大小:每个函数应该相对较小。
*耦合:避免直接改变在另外一个模块文件中的变量。
函数是对象:简洁调用

2、模块
一、基本
每一个文件都是一个模块,而且模块导入其余模块以后就可使用导入模块定义的变量名。模块能够由两个语句和一个重要的内置函数进行处理。
import: 使客户端(导入者)以一个总体获取一个模块。
from:允许客户端从一个模块文件中获取特定的变量名。
reload:在不停止Python程序的状况下,提供了一个从新载入模块文件代码的方法。
import和from是赋值语句,是可执行的语句,能够嵌套到if ,def语句中和def同样import和from都是隐性赋值语句

在一个模块文件的顶层定义的全部变量名都成为了被导入的模块对象的属性。
模块至少有三个角色:
代码重用:模块仍是定义变量名的空间,被认做是属性。能够被多个外部的客户端应用。
系统命名空间的划分:
现实共享服务和数据:

二、python程序构架
import如何工做
执行三个步骤
1)、找到模块文件
2)、编译成位码(须要时)
3)、执行模块的代码来建立其所定义的对象。
在以后导入相同的模块时候,会跳过这三个步骤,而只提取内存中已加载模块对象。
express

搜索模块
导入模块时,不带模块的后缀名,好比.pyapache

Python搜索模块的路径:
1)、程序的主目录
2)、PTYHONPATH目录(若是已经进行了设置)
3)、标准链接库目录(通常在/usr/local/lib/python2.X/)
4)、任何的.pth文件的内容(若是存在的话).新功能,容许用户把有效果的目录添加到模块搜索路径中去
.pth后缀的文本文件中一行一行的地列出目录。
这四个组建组合起来就变成了sys.path了,
>>> import sys
>>> sys.path
导入时,Python会自动由左到右搜索这个列表中每一个目录。
第1,第3元素是自动定义的,第2,第4能够用于扩展路径,从而包括本身的源码目录。

三、模块的建立和使用。
建立模块
后缀.py文本文件,模块顶层指定的全部变量名都会变成其属性。
定义一个module.py模块
name='diege'
age=18
def printer(x):
        print x
使用模块
import所有导入,import将整个模块对象赋值给一个变量名,模块只导入一次,由于该操做开销大
>>> import module
属性
>>> module.name
'diege'
函数
>>> module.printer('hi')
hi
from语句:from 将获取(复制)模块特定变量名
from会把变量名赋值到另外一个做用域,因此它就可让咱们直接在脚本中使用复制后的变量名,而不是经过模块
from 模块名 import 须要复制的属性 将一个或多个变量名赋值给另外一个模块中同名的对象
from 模块名 import 须要复制的属性 as 新的属性名 将一个或者多个变量名赋值给另外一个模块中不一样名的对象
from * 语句 from 模块名 import * 取得模块顶层全部赋了值的变量名的拷贝
>>> from module import name
>>> name
'diege
>>> from module import name as myname
>>> myname
'diege'
>>> from module import printer as PR
>>> PR('hi python')
hi python
>>> from module import name,age 复制多个变量名时要用逗号隔开
>>> name,age
('diege', 18)
>>> from module import name as myname,age as myage  复制多个变量名并改变需时须要用逗号隔开多个as
>>> from module import *

四、模块命名空间
模块最好理解为变量名的封装,简而言之,模块就是命名空间(变量名创建所在的场所),而存在于模块以内的变量名就是模块对象的属性。
文件生成命名空间
*模块语句会在首次导入时执行。
*顶层的赋值语句会建立模块属性(文件顶层不在的def和class以内的,但def和class隐性建立的变量名也属于模块属性)。赋值的变量名会存储在模块的命名空间内。
*模块的命名空间能经过属性__dict__(module.__dict__)或dir(module)获取
因为导入而创建的模块的命名空间是字典,可经过模块对象相关联的内置__dict__属性读取。
dir函数查看,大至与对象的__dict__属性的键排序后的列表相等,可是它还包含了类继承的变量名。
*模块是一个独立的做用域。

五、重载模块
模块程序代码默认值对每一个过程执行一次,要强制使模块代码从新载入并从新运算须要使用reload内置函数。
reload是函数,import是语句。两个语法不同。
>>> import module
>>> reload(module)    
<module 'module' from 'module.pyc'>
>>> reload(test17)
<module 'test17' from '/root/test17.py'>
reload()以前需得import过一次

六、模块包
除模块名之外,导入也能够指定目录路径,Pytyh代码的目录就是称为包。所以这类导入就称为包导入
import dir1.dir2.mod
from dir1.dir2.mod import x
.号路径至关于机器上目录层次的路径。
dir1在容器目录dir0中,dir0这个目录能够在Python模块搜索路径中找到。
__init__.py包文件
若是选择使用包导入,那就必须遵循一条约束:包导入语句的路径的每一个目录内部都必须有__init__.py这个文件,
不然导入包会失败。
dir1和dir2中必须包含__init__.py,容器目录dir0不须要这类文件。由于自己没有列在import语句中
__init__.py文件能够包含程序代码,也能够是空的。 更一般状况下,__init__.py文件扮演了包初始化的挂钩
替目录产生模块命名空间以及用目录导入实现from *行为的角色。
*包初始化:
首次导入某个目录时,会自动执行该目录下__init__.py文件中全部程序代码。
因此这个文件就是放置包内文件所需初始化的代码的场所。可使用其初始化文件,建立所需的数据文件,
链接数据库等。
*模块命名空间的初始化:
*from * 语句的行为:
做为一个高级功能,能够在__init__.py文件中使用__all__列表来定义目录以form *语句形式导入时,须要
导出什么。__all__列表是指出当包(目录—)名称使用from *的时候,应该导入的子模块名称清单。
eg:
/usr/local/lib/python2.7/sqlite3/__init__.py
from dbapi2 import *
/usr/local/lib/python2.7/site-packages/mod_python/__init__.py
__all__ = ["apache", "cgihandler", "psp",
           "publisher", "util", "python22"]
version = "3.3.1"
常见的第三方扩展都是以包目录形式发布给用户,而不是单纯的模块列表。
这样就能够经过路径来导入

七、在模块中隐藏数据
最小化from *的破坏:_X和__all__达到隐藏变量名的目的
有种特定状况,能够把下划线放在变量名前(_X),能够防止客户端使用from * 语句导入模块名时,把其中的那些变量名赋值出去。这实际上是为了把命名空间的破坏最小化而已。下划线和__all__不是私有声明,还能够经过其余导入形式 修改这类变量名。例如import语句、from module import _X
之外,也能够在模块顶层把变量名的字符串列表赋值给变量__all__,以达到相似于_X命名惯例的隐藏效果【__all__是不隐藏的】
mod_python.__all__ 能够看到能够用from *语句复制那些变量名
_X和__all__ 对比
_X 隐藏了 没法from *
__all__  只显示,from *只能获取__all__中指定的,其余隐藏。
python中from *会先寻找模块内的__all__列表,有的话复制其中的变量名,若是没有定义的话,from *就会复制开头没有下划线的全部命令名。
怎么以为__all__列表里存放的是模块呢???

八、混合用法模式:__name__和__main__
这是一个特殊的与模块相关的技巧,能够把文件做为模块导入,并以独立式程序的形式运行。每一个模块都有个名为__name__的内置属性。Python会自动设置该属性:
*若是文件是以顶层程序文件执行,在启动时,__name__就会被设置为字符串__main__
*若是文件被导入,__name__就会改设成客户端所了解模块名。
结果就是模块能够检测本身的__name__,来肯定它是执行仍是在导入。
定义一个文件test15.py
def tester():
        print "It's python test!"
if __name__=='__main__':
        tester()

九、修改模块搜索路径
能够经过PYTHONPATH以及可能的.pth路径文件进行定制。
Python程序自己是修改sys.path的内置列表。sys.path在程序启动时就进行初始化,但那以后也能够随意对其元素进行删除,附加和重设
>>> import sys
>>> sys.path
# cd /tmp/
# python
>>> sys.path.append('/root')【增长新的路径】
>>> sys.path
['', '/usr/local/lib/python2.7/site-packages/MySQL_python-1.2.3-py2.7-freebsd-8.2-RELEASE-i386.egg', '/usr/local/lib/python2.7/site-packages/setuptools-0.6c12dev_r88846-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Babel-0.9.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Trac-0.12.3-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Genshi-0.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/IniAdmin-0.2_r10454-py2.7.egg', '/usr/local/lib/python2.7/site-packages/TracAccountManager-0.4dev_r11251-py2.7.egg', '/usr/local/lib/python2.7/site-packages/SvnAuthzAdminPlugin-0.2-py2.7.egg', '/usr/local/lib/python27.zip', '/usr/local/lib/python2.7', '/usr/local/lib/python2.7/plat-freebsd8', '/usr/local/lib/python2.7/lib-tk', '/usr/local/lib/python2.7/lib-old', '/usr/local/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/site-packages', '/root']
导入/root目录下test17.py,注意启动python时在/tmp目录,因此/root不是程序启动目录
>>> import test17 
>>> dir(test17)
>>> test17.lessthan(3,4)
True

十、相对导入语法
from语句如今可使用点号(.)更倾向于同一个包内的模块(称为包相对导入),而不是位于模块导入搜索路径上其余地方的模块(所谓的绝对导入)
*如今,可使用点号指出该导入应该与其所在包相关联:这类导入倾向于导入位于该包内的模块,而不是导入搜索路径sys.path上其余地方的同名模块
from .apache CallBack as CB
同一个包内导入apache模块CallBack为CB变量

十一、模块设计理念
*老是在Python的模块内编写代码
*模块耦合要降到最底:全局变量。模块应该尽量和其余模块的全局变量无关。
*最大化模块的沾合性:统一目标
*模块应该少去修改其余模块的的变量。
模块是对象:元程序
模块经过内置属性显示了他们大多数的特性。所以,可很容易的编写程序来管理其余程序。咱们一般称这类管理程序为元程序,由于他们是在其余系统上工做。这也 称为内省,由于程序能看见和处理对象的内部。内省是高级功能,可是它能够作建立程序工具,取得模块内名为name的属性,方法包括
(1)可使用结合点号运算,
(2)或者对模块的属性字典进行索引运算(在内置__dict__属性中显示)。
(3)Python也在sys.modules字典中导出全部已经加载的模块。
(4)并提供一个内置函数getattrr,让咱们以字符串名来取出属性。(就好像object.attr,而attr是运行时的字符串)
>>> test17.name
'diege'
>>> test17.__dict__.keys()
>>> test17.__dict__['name']      
'diege
>>> test17.__dict__['lessthan']   
<function lessthan at 0x28495844>
>>> sys.modules 显示全部加载的模块
>>> sys.modules['test17']
<module 'test17' from '/root/test17.py'>
>>> sys.modules['test17'].name
'diege
>>> getattr(test17,'lessthan')
<function lessthan at 0x28495bc4>

十二、模块陷阱
1)顶层代码的语句次序的重要性
*在导入时,模块文件顶层的程序代码(不在函数内)一旦python运行,就会马上执行。所以,该语句是没法引用文件后面位置赋值的变量名。
*位于函数主体内的代码知道函数被调用后才会运行。
做为一条原则,若是须要把当即执行的代码和def一块儿混用,就要把def放在文件前面,把顶层代码放在后面。这样的话,你的函数在使用的代码运行时,能够保证他们都已定义并赋值过了。
2)经过变量名字符串导入模块的方法
import或from语句内的模块名是”硬编码“的变量名。
>>> x='string'
>>> import x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named x
这里python会试着导入文件x.py
为了不发生这样的问题,一般的作法就是把import语句构形成python代码的字符串,再传给exec语句执行:
>>> modname='string'
>>> exec "import "+modname
exec语句以及和他相似eval会编译一段字符串代码,将其传给Python解析器来执行。
3)from复制变量名,而不是链接
from语句实际上是在导入者的做用域内对变量名的赋值语句,也就是变量名拷贝运算,而不是变量名的别名机制。它的实现和python全部赋值运算都同样, 微妙之处在于,共享对象的代码存在于不一样的文件中。而后,咱们使用import得到了整个模块,而后赋值某个点号运算的变量名,就会修改导入的模块中的变 量名。点号运算把python定向到了模块对象,而不是赋值模块中对象。
4)from*会让变量语义模糊
5)reload不会影响from导入
6)不要使用reload、from以及交互模式测试
reload中引用模块得经过import至少将其加载一次:
不要from导入以后reload
7) reload使用没有传递性
当重载一个模块时,Python只会重载那个模块的文件,不会自动重载该文件重载嘶碰巧还要导入的模块。
8)递归形式的from import没法工做
不要在递归导入中使用 from。编程

相关文章
相关标签/搜索