假设有以下函数:程序员
def add(x,y): return x+y
好比说有个包含由两个相加的数字组成的元组:shell
params = (1,2)
使用*
运算符对参数进行“分配”,不过是在调用而不是在定义时使用:编程
>>> add(*params) 3
======闭包
一样,可使用 双星号 运算符来处理字典。app
假设以前定义了hello_3
,那么能够这样使用:函数式编程
>>> params = {'name':Sir Robin','greeting':'Well met'} >>> hello_3(**params) Well met.Sir Robin
星号只在 定义函数(容许使用不定数目的参数)或者 调用(“分割”字典或者序列)时才有用。函数
在执行x=1
赋值语句后,名称x
引用到值1
。这就像是使用字典同样,键引用值。固然,变量和所对应的值用的是个“不可见”的字典。ui
內建的vars
函数能够返回这个字典:spa
>>> x = 1 >>> scope = vars() >>> scope['x'] 1 >>> scope['x'] += 1 >>> x 2
这类“不可见字典”叫作 命名空间 或者 做用域 。除了全局做用域外,每一个函数调用都会建立一个新的做用域:翻译
>>> def foo(): x = 42 ... >>> x = 1 >>> foo() >>> x 1
这里的foo函数改变(重绑定)了变量x,可是在最后的时候,x并无变。这是由于当调用foo
的时候,新的命名空间就被建立了,它做用于foo
内的代码块。赋值语句x=42
只在内部做用域(局部命名空间)起做用,因此它并不影响外部(全局)做用域中的x
。
函数内的变量被称为局部变量(local variable),这是与全局变量相反的概念。参数的工做原理相似于局部变量,因此用全局变量的名字做为参数名并无问题。
>>> def output(x): print x ... >>> x = 1 >>> y = 2 >>> output(y) 2
======
重绑定全局变量:
若是在函数内部将值赋予一个变量,它将会自动成为局部变量——除非告知Python将其声明为全局变量:
>>> x = 1 >>> def change_global(): global x x = x + 1 >>> change_global() >>> x 2
======
Python的函数是能够嵌套的:
def foo(): def bar(): print "Hello,World!" bar()
函数嵌套有一个很突出的应用,例如须要一个函数“建立”另外一个。也就意味着能够像下面这样(在其余函数内)书写函数:
def multiplier(factor): def multiplier(number): return number*factor returnmultiplyByFactor
一个函数位于另一个里面,外层函数返回里层函数。也就是说函数自己被返回了,但并无被调用。重要的是返回的函数还能够访问它的定义所在的做用域。换句话说,它“带着”它的环境(和相关的局部变量)。
每次调用外层函数,它内部的函数都被从新绑定。factor
变量每次都有一个新的值。因为Python的嵌套做用域,来自(`multiplier
的)外部做用域的这个变量,稍后会被内层函数访问:
>>> double = multiplier(2) >>> double(5) 10 >>> triple = multiplier(3) >>> triple(3) 9 >>> multiplier(5)(4) 20
相似multiplayByFactor
函数存储子封闭做用域的行为叫作闭包(closure)。
递归的定义(包括递归函数定义)包括它们自身定义内容的引用。
关于递归,一个相似的函数定义以下:
def recursion(): return recursion()
理论上讲,上述程序应该永远地运行下去,然而每次调用函数都会用掉一点内存,在足够的函数调用发生后(在以前的调用返回后),空间就不够了,程序会以一个“超过最大递归深度”的错误信息结束。
这类递归就作无穷递归(infinite recursion),相似于以while True
开始的无穷循环,中间没有break
或者return
语句。由于(理论上讲)它永远不会结束。
有用的递归函数包含如下几个部分:
当函数直接返回值时有基本实例(最小可能性问题)
递归实例,包括一个或者多个问题较小部分的递归调用。
这里的关键就是将问题分解成小部分,递归不可能永远继续下去,由于它老是以最小可能性问题结束,而这些问题又存储在基本实例中的。
当每次函数被调用时,针对这个调用的新命名空间会被建立,意味着当函数调用“自身”时,实际上运行的是两个不一样的函数(或者说是同一个函数具备两个不一样的命名空间)。实际上,能够将它想象成和同种类的一个生物进行对话的另外一个生物对话。
计算数n
的的阶乘:
def factorial(n): result = n for i in range(1,n): result *= 1 return result
递归实现:
1的阶乘是1;
大于1的数n的阶乘是n
乘n-1
的阶乘。
def factorial(n): if n == 1: return 1 else: return n * factorial(n-1)
======
计算幂
例子:power(x,n)
(x
为n
的幂次)是x
自乘n-1
次的结果(因此x
用做乘数n
次。
def power(x,n): result = 1 for i in range(n): result *= x return result
递归实现:
对于任意数字x
来讲,`power(x,0)是1;
对于任何大于0的书来讲,power(x,n)
是x
乘以(x,n-1)
的结果。
def power(x,n): if n == 0: return 1 else: return x * power(x,n-1)
递归实现:
若是上下限相同,那么就是数字所在位置,返回;
不然找到二者的中点(上下限的平均值),查找数字是在左侧仍是在右侧,继续查找数字所在的那半部分。
def search(sequence,number,lower,upper): 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)
提示:标准库中的bisect
模块能够很是有效地实现二分查找。
Python在应对“函数式编程”方面有一些有用的函数:map
、filter
和reduce
函数(Python3.0中都被移至fuctools
模块中)。
map
和filter
在目前版本的Python并不是特别有用,而且可使用列表推导式代替。不过可使用map
函数将序列中的元素所有传递给一个函数:
>>> map(str,range(10)) #Equivalent to [str(i) for i in range(10)] ['0','1','2','3','4','5','6','7','8','9']
filter
函数能够基于一个返回布尔值的函数对元素进行过滤。
#island 判断字符变量是否为字母或数字, #如果则返回非零,不然返回零 >>> def fun(x): return x.isalnum() >>> seq = ["foo","x41","?!","***"] >>> filter(func,seq) ['foo','x41']
本例中,使用列表推导式能够不用专门定义一个函数:
>>> [x for x in seq if x.isalnum()] ['foo','x41']
事实上,还有个叫作lambda
表达式的特性,能够建立短小的函数。
>>> filter(lambda x: x.isalnum().seq) ['foo','x41']
=======
reduce
函数通常来讲不能轻松被列表推导式替代,可是一般用不到这个功能。它会将序列的前两个元素与给定的函数联合使用,而且将它们的返回值和第3个元素继续联合使用,直到整个序列都处理完毕,而且获得一个最终结果。
可使用reduce
函数加上lambda x,y:x+y
(继续使用相同的数字):
>>> numbers = [72,101,108,108,111,44,32,119,111,114,108,100,33] >>> reduce(lambda x,y:x+y,numbers) 1161
固然,这里也可使用内建函数sum
。
抽象。抽象是隐藏多余细节的艺术。定义处理细节的函数可让程序更抽象。
函数定义。函数使用def
语句定义。它们是由语句组成的块,能够从“外部世界”获取值(参数),也能够返回一个或者多个值做为运算的结果。
参数。函数从参数中获得须要的信息,也就是函数调用时设定的变量。Python中有两类参数:位置参数 和 关键数参数。参数在给定默认值时是可选的。
做用域。变量存储在做用域(也叫做命名空间)中。Python有两类主要的做用域——全局做用域 和 局部做用域。做用域能够嵌套。
递归。 函数能够调用自身即递归。一切用递归实现的功能都能用循环实现,可是有些时候递归函数更易读。
函数式编程。Python有一些进行函数式编程的机制。包括lambda
表达式以及map
、filter
和reduce
函数。
| 函数 | 描述 |
| ------------- |:-------------|
| map(func,seq[,seq,...])
| 对序列中的每一个元素应用函数 |
| filter(fuc,seq)
| 返回其函数为真的元素的列表 |
| reduce(func,seq[,initial])
| 等同于func(func(func(seq[0],seq[1],se1[2]...
|
| sum(seq)
| 返回seq
全部元素的和 |
| apply(func,args[,kwargs]]
| 调用函数,能够提供参数 |
在面对对象程序设计中,术语对象(object)基本上能够看作数据(特性)以及由一系列能够存取、操做这些数据的方法所组成的集合。使用对象替代全局变量和函数的缘由可能有不少,其中对象最重要的优势包括如下几方面:
多态(Polymorphism):意味着能够对不一样类的对象使用一样的操做,它们会像“被施了魔法通常”工做。
封装(Encapsulation):对外部世界隐藏对象的工做细节。
继承(Inheritance):以通用的类为基础创建专门的类对象。
术语多态的意思是“有多种形式”。多态意味着就算不知道变量所引用的对象类型是什么,仍是能它进行操做,而它也会根据对象(或类)类型的不一样而表现出不一样的行为。
repr
函数是多态特性的表明之一,能够对任何东西使用:
def length_message(x): print "The length of",repr(x),"is",len(x)
>>> length_message('Fnord') The length of 'Fnord' is 5 >>> length_message([1,2,3]) The length of [1,2,3] is 3
不少函数和运算符都是多态的——你写的绝大多数程序可能都是,只要使用多态函数和运算符,就会与“多态”发生关联。事实上,惟一能毁掉多态的就是使用函数显式地检查类型,好比type
、isinstance
以及issubclass
函数等等。若是可能的话,应该尽力避免使用这些毁掉多态的方式。真正重要的是如何让对象按照你所但愿的方式工做,无论它是否是真正的类型(或者类)。
封装是指向程序中的其余部分隐藏对象的具体实现细节的原则。
可是封装并不等同于多态,多态可让用户对于不知道什么是类(对象类型)的对象进行方法调用,而封装是能够不用关心对象是如何构建的而直接进行使用。
基本上,须要将对象进行抽象,调用方法的时候不用关心其余的东西,好比它是否干扰了全局变量。
能够将其做为 特性(attribute) 存储。特性是做为变量构成对象的一部分,事实上方法更像是绑定到函数上的属性。
对象有着本身的状态(state)。对象的状态由它的特性(好比名称)来描述。对象的方法能够改变它的特性。因此就像是将一大堆函数(方法)捆在一块儿,而且给予他们访问变量(特性)的权力。它们能够在函数调用之间保持保存的值。
略
类是一种对象,全部的对象都属于某一个类,称为类的实例(instance)。
当一个对象所属的类是另一个对象所属类的子集时,前者就被称为后者的 子类(subclass),因此“百灵鸟类”是“鸟类”的子类。相反,“鸟类”是“百灵鸟类”的“超类”(superclass)。可是,在面向程序设计中,子类的关系是隐式的,由于一个类的定义取决于它所支持的方法。类的全部实例都会包含这些方法,因此全部子类的全部实例都有这些方法。定义子类只是个定义更多(也有多是重载已经存在的)方法的过程。
事实上,self
参数正是方法和参数的区别。方法(更专业一点能够称为绑定方法)将它们的第一个参数绑定到所属的实例上,所以无需显式提供该参数。固然也能够将特性绑定到一个普通函数上,这样就不会有特殊的self
参数了:
>>> class Class: def method(self): print 'I hava a self' >>> def function(): print "I don't..." >>> instance = Class() >>> instance.method() I hava a self! >>> instance.method =function >>> instance.method() I don't...
注意,self
参数并不依赖于调用方法的方式,前面使用的是instance.method
(实例.方法
)的形式,能够随意使用其余变量引用同一个方法:
>>> class Bird: song = 'Squaawk!' def sing(self): print self.song >>> bird = Bird() >>> bird.sing() Squaawk! >>> birdsong = bird.sing >>> birdsong() Squaawk!
尽管最后一个方法调用看起来与函数调用十分类似,可是变量birdsongs
引用绑定方法bird.sing
上,也就意味着这仍是会对self
参数进行访问(也就是说,它仍旧绑定到类的相同实例上)。
默认状况下,程序能够从外部访问一个对象的特性:
>>> c.name 'Sir Lancelot' >>> c.name = 'Sir Gumby' >>> c.getName() 'Sir Gumby'
为了不这类事情的发生,应该使用私有(private)特性,这是外部对象没法访问到,但getName
和setName
等访问器(accessor)可以访问的特性。
Python并不直接支持私有防暑,为了让方法或者特性变为私有(从外部没法访问),只要在它的名字前面加上双下划线便可。
class Secretive: def __inacessible(self): print "Bet you can't see me.." def accessible(self): print "The secret message is:" self.__inaccessible
如今,__inaccessible
从外界是没法访问的,而在类内部还能使用(好比从accessible
)访问:
>>> s = Secretive() >>> s.__inaccessible() Traceback (most recent call last): File "<pyshell#112>",;ine 1, in ? s.__inaccessible() AttributeError: Secretive instance has no attribute '__inaccessible' >>> s.accessible() The secret message is: Bet you can't see me...
尽管双下划线有些奇怪,可是看起来像是其余鱼鱼中的标准的私有方法。而在类的内部定义中,全部以双下划线开始的名字都被“翻译”成前面加上单下划线类名的形式。
>>> Secretive._Secret__inaccsible <unboud method Secretive.__inaccessible>
但实际上仍是可以在类外访问这些私有方法,尽管不该该这么作:
>>> s._Secretive.__inaccessible Bet you can't see me..
简而言之,确保他人不会访问对象的方法和特性是不可能的,可是这类“名称变化”是提醒他们不该该访问这些函数或者特性的强有力信号。
若是不须要使用这种方法可是又想让其余对象不要访问内部数据,那么可使用单下划线,这不过是个习惯,但的确有实际效果。例如,前面有下划线的名字都不会被带星号的import
语句(from module import *
)导入。
下面的两个语句几乎等价:
def foo(x):return x*x foo = lambda X:x*x
二者都建立了返回参数平方的函数,并且都将变量foo
绑定到函数上。变量foo
能够在全局(模块)范围内进行定义,也可处在局部的函数或方法内。定义类时,太阳的事情也会发生,全部位于class
语句中的代码块都在特殊的命名空间中执行——类命名空间(class namespace)。这个命名空间可由类内全部成员访问。但并非全部Python程序员都知道类的定义其实就是执行代码块。
子类能够拓展超类的定义。将其余类名写在class
语句后的圆括号内能够指定超类。
若是想要查看一个类是不是另外一个的子类,可使用内建的issubclass
函数。
若是想要知道已知类的基类(们),能够直接使用它的特殊特性__base__
:
一样,还能使用isinstance
方法检查一个对象是不是一个类的实例: