代码改变世界

Python基本语法_函数属性 & 参数类型 & 偏函数的应用

2015-10-20 21:00  云物互联  阅读(226)  评论(0编辑  收藏  举报

目录

前言

Python除了有丰富的标准库之外,还可以自己定义满足自身需求的类和函数。本篇主要介绍如何创建Python自定义函数。

软件环境

  • 系统
    • UbuntuKylin 14.04
  • 软件
    • Python 2.7.4
    • IPython 4.0.0

Python Module的程序入口

因为一个Python的程序文件同时也是一个模块文件,而且我们希望可以更好的实现类和函数的代码重用性、节省内存资源、提高执行效率。即,我希望主程序仅是在被期望执行的时候才会被执行而不会自动执行。所以我们会在每一个Python module文件中定义一个程序的入口,当我们直接执行Python module的时候会自动执行主程序。当我们一个Python module被别的程序导入时,则仅仅会导入在Python module文件中定义的类和函数。
我们一般会在Python module文件中的主程序前使用下面的一条语句:

if __name__ == '__main__':

__name__是模块的一个属性,其作用是调用当前模块的名称,若此模块是直接执行时,__name__ == ‘__main__’ 。当此模块是被其他程序import时,__name__的值为此模块的名称。
具体的Python module文件结构,请参考:http://blog.csdn.net/jmilk/article/details/48573995
Update: 2016-10-09
Python module程序入口最大的好处在于我们可以利用上述的这个特性来为一个 Module 编写测试代码, 当我们直接运行这个 module 文件的时候, 我们可以通过测试代码的实现来检验我们所定义的函数的输出和输出是否正确, 为每一个由必要测设的 module 文件写上测试代码是一个优秀的程序员所具备的编程习惯.

函数的属性

函数是对程序逻辑进行结构化或过程化的一种编程方法, 函数有助于节省代码空间, 也可以利于保持程序一致性(修改一处, 所有代用都会跟着改变).

函数的属性可以在定义函数时同时定义函数属性, 也可以在函数声明外定义函数属性.
EXAMPLE: 可以通过句点标识符和直接赋值的方法为一个函数添加属性.

In [2]: foo()
in foo()

In [3]: foo.__dict__
Out[3]: {}

In [4]: foo.name = 'jmilkfan'

In [5]: foo()
in foo()

In [6]: foo.__dict__
Out[6]: {'name': 'jmilkfan'}

In [7]: foo.name
Out[7]: 'jmilkfan'

还可以通过下面这种方式来定义函数属性

In [15]: foo.__dict__['age'] = 24

In [16]: foo.__dict__
Out[16]: {'age': 24, 'name': 'jmilkfan'}

In [17]: foo.age
Out[17]: 24

NOTE: 这里可以看出函数对象的 __dict__ 特殊属性包含了函数对象的属性和属性值.

Python函数的创建

一般格式:

def functionName(parameters):
    """Document""" 
    函数体
    return [expression] 

创建一个自定义函数需要遵循下面几个规则:
1.函数代码块以def关键词开始,接着函数标识符、圆括号()和代码块起始标识 : 号。
2.任何传入参数和自变量都必须放在()。()内可以用于定义参数。当有多个参数时,使用逗号隔开。
3.函数体的第一行语句可以使用文档字符串"""Document"""一般用作存放函数的使用说明。
在Python中,函数也是一个对象,( )中表示对可调用函数对象的一种调度,可调用的函数对象一定会继承了可调用的方法call( )。这也是用来检查一个函数可否能够被调用的依据。

callable(functionName)

若输出为True,表示对象functionName为可调用函数对象,其继承了call()方法,是一个函数。

In [5]: callable(sys.stdout.write)
Out[5]: True

函数的参数

Python有下面几种函数参数类型:
1.必备参数
2.命名参数
3.缺省参数
4.不定长参数
5.匿名参数

必备参数

必备参数是在自定义函数时,最常见的参数类型。必备参数的使用有下面几点要求:
1.在调用函数时,以形参与实参对应的顺序来确定形参的值并传入函数。
2.调用此函数时,传递的实参的数量必须与声明时定义的形参的数量一致。

In [19]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py

def DoubleCheckInput(input1,input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter1,enter2)

In [20]: run func1Test.py
Ple enter your name:Jmilk
ple enter your name again:Jmilk
Your enter the Jmilk

注意:当传递的参数数目不一致时,会触发下面的异常:

In [23]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py

def DoubleCheckInput(input1,input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter1)

In [24]: run func1Test.py
Ple enter your name:Jmilk
ple enter your name again:Jmilk
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/src/pyScript/func1Test.py in <module>()
     11     enter1 = raw_input("Ple enter your name:")
     12     enter2 = raw_input("ple enter your name again:")
---> 13     DoubleCheckInput(enter1)

TypeError: DoubleCheckInput() takes exactly 2 arguments (1 given)

缺省参数

缺省参数既默认参数。顾名思义,缺省参数可以实现在调用函数却没有指定实参时,可以为形参数提供一个默认值。相对的,当缺省参数对应的实参有传递值时,缺省参数的默认值会被覆盖。从而实参的数目未必一定会与形参的数目一致,因为使用了缺省参数。格式:

def functionName([para1,...,]paras=defaultValues)
In [46]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def DoubleCheckInput(input1,input2='Jmilk'):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
#    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter1)

In [47]: run func1Test.py
Ple enter your name:Jmilk
Your enter the Jmilk

注意:即便缺省参数会为形参提供一个默认的值,但是在Python的编译过程的语法检查时,参数的匹配依旧是按照顺序传递。所以一般而言,当你希望使用缺省参数的值的时候,需要在定义函数的参数列表时,缺省参数一定要置于必备参数和命名参数之后。避免在参数传递的时候,实参在将参数列表前端的缺省参数覆盖后,却没有实参传递给必备参数。下面是一个例子:

In [56]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def DoubleCheckInput(input1='Jmilk',input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
#    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter2)

In [57]: run func1Test.py
  File "/usr/local/src/pyScript/func1Test.py", line 4
    def DoubleCheckInput(input1='Jmilk',input2):
SyntaxError: non-default argument follows default argument

语法错误:非缺省参数不能在缺省参数之后

命名参数

命名参数也称之为关键字参数, 其仅针对函数的调用, 让调用者通过函数调用中的参数名字赋值来将实参映射到形参, 有下面几个要点:
1.调用时,用赋值的方式来为函数传入参数,调用格式:

functionName(formPara1=actualPara1[,...])

2.可以以任意顺序来指定参数

In [40]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def DoubleCheckInput(input1,input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(input2=enter2,input1=enter1)

In [41]: run func1Test.py
Ple enter your name:Jmilk
ple enter your name again:Jmilk
Your enter the Jmilk

下面举一个命名参数和缺省参数一结合使用的例子:

In [7]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def printYourEnter(input1,input2='Jmilk',input3='23'):
    print input1
    print input2
    print input3

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = raw_input("Ple enter your age:")
    printYourEnter(enter1)

In [8]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:
Ple enter your age:
BJ
Jmilk
23

在上面的例子中使用了缺省参数,当没有实参传递到函数的时候,形参会使用默认值。但在有些情况中,我们希望可以重新指定形参input3的值,而且希望继续使用input2的默认值。在大多数编程语言中,想要实现上面的要求,必须要使用3个参数来调用函数,即必须重新指定input2的值。但是在Python中因为命名参数类型,所以我们可以突破参数顺序的限制。将上面的例子修改为:

In [13]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def printYourEnter(input1,input2='Jmilk',input3='23'):
    print input1
    print input2
    print input3

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = raw_input("Ple enter your age:")
    printYourEnter(input1=enter1,input3=enter3)

In [14]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:
Ple enter your age:24
BJ
Jmilk
24

可以看见,当我没有传递input2,仅仅是传递了input1、input3,而input2还是使用了默认值,而不会因为没有指定命名参数导致input2被覆盖而input3使用默认值的情况。但是要注意的一点是,即便结合使用了命名参数和缺省参数,仍然无法改变non-default parameter not follows the default parameter的语法规则。

不定长参数

不定长参数也称之为参数组或冗余参数, 当你希望一个函数可以接受处理比当初声明定义函数时更多的参数,可以使用不定长参数。即,实现了函数的参数冗余。当传递的实参数目比函数的形参更多时,一般会报错。但函数中的不定长参数可以用来吸收多余的参数。注意:不定长参数仅允许放到函数形参列表的最后。格式:

def functionName([para1,para2,...,]*indefinitePara):

def functionName([para1,para2,...,]**indefinitePara):

def functionName([para1,para2,...,]*indefinitePara1, **indefinitePara2):

其中的 “*” 标识了不定长参数为 (非关键字参数) 会存储所有非关键字的冗余参数(没有在实参列表内指定关键字,EG. name='Jmilkfan'),并将冗余的参数存储为Tuple类型对象, 也可以向该形参传递一个 Tuple 类型的参数组, 两者的效果是一样的. 只是前者在函数内处理成 Tuple 的形式, 后者在调用之前就已经整理成 Tuple 了
Example1:

In [23]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def printYourEnter(input1,*indePara):
    print input1
    print indePara
    for var in indePara:
        print var

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = raw_input("Ple enter your age:")
    printYourEnter(enter1,enter2,enter3)

In [24]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:Jmilk
Ple enter your age:23
BJ
('Jmilk', '23')
Jmilk
23

上面例子的函数中使用了不定长参数*indePara,来存放enter2多余的enter3两个实参。并且是以元组的数据类型来存储。
Example2:
而 “**” 表示为一个 (关键字参数),以字典的数据类型来存储冗余实参, 也可以向该形参传递一个 Dict 类型的实参。

In [4]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
def printYourEnter(input1,**indePara):
    print input1
    print indePara

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = int(raw_input("Ple enter your age:"))
    printYourEnter(enter1,name=enter2,age=enter3)

In [5]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:Jmilk
Ple enter your age:23
BJ
{'age': 23, 'name': 'Jmilk'}

在调用函数时,使用key=value的映射关系元素,传递实参。本质上是传递了key的引用和value的数据两个值,并且传递到函数中的**indePara字典类型。要注意这与命名参数的区别,命名参数是为了可以乱序传递实参,而不定长参数是为了将多余的实参保存在函数中的Tuple或Dictionary类型对象中。
注意:以Tuple或Dic作为形参吸收实按冗余时,若实参中均没有多余的元素或没有映射关系元素时,会自动的为函数中的Tuple或Dic类型对象填入空值,避免报错。

In [9]: %pycat test.py
#!/usr/bin/env python
def test(x,*args,**kwargs):
    print x
    print args
    print kwargs
test(1)

In [10]: run test.py
1
()
{}

还需要注意的是:实参的映射关系元素中的key,不可以与已经存在的普通形参同名。否则会报错

In [13]: %pycat test.py
#!/usr/bin/env python
def test(x,**kwargs):
    print x
    print kwargs

test(1,x=1,y=2)

In [14]: run test.py
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/src/pyScript/test.py in <module>()
      4     print kwargs
      5 
----> 6 test(1,x=1,y=2)

TypeError: test() got multiple values for keyword argument 'x'

主要是避免命名参数和不定长参数的冲突。

匿名参数

匿名函数,即lambda函数。此函数能够快速的创建出单行最小行函数并用于任何需要函数的地方,使用匿名参数有下面几个要点:
1.无须使用def关键字来进行声明。
2.lambda函数可以接受任何数量的实参,但只会且仅返回一个表达式的值。
3.lambda函数不能包含有命令和多个表达式。
4.不能直接调用print语句,因为lambda函数是单行函数且此行只能为表达式。
5.lambda函数拥有自己的命名空间,不能访问除自身参数列表之外的全局命名空间内的参数。
6.lambda最大的优势在于,可以不占用栈内存从而增加执行效率。
lambda函数的定义格式:

functionName = lambda [arg1[,arg2,...]]:expression

调用格式:

functionName(arg1[,arg2,..])

Example:

In [76]: %pycat lamTest.py
#!/usr/bin/env python
#Filename:lamTest.py
n = lambda x,y:x*y
print type(n)
print type(n(2,3))
print n(2,3)

In [77]: run lamTest.py
<type 'function'>
<type 'int'>
6

lambda在定义时会返回一个需要接收的函数对象,在调用后会返回一个类型对象。

偏函数的应用

偏函数的概念:就是将函数式编程、缺省参数和冗余参数结合在一起,将任意数量(顺序)的参数转化为另一个带有剩余参数的函数对象,从而实现截取函数功能(偏向)的效果。
NOTE:偏函数需要 functools Module 的支持。
EXAMPLE 1:将二进制数转化为十进制

In [1]: int('10101010', base=2)
Out[1]: 170

一般而言我们会使用上述的方式来执行,但有些时候会觉得能不能将 int() 函数的 base 参数临时的设置成缺省参数呢?这样的话就可以获得一个专门为了实现 *将二进制数转化为十进制
* 功能的函数了。下面就是偏函数其中一个小的应用实例:

In [2]: from functools import partial

In [3]: baseTwo = partial(int, base=2)

In [5]: baseTwo('10101010')
Out[5]: 170

将 int() 函数的 base 参数定义为缺省参数,并返回给一个新的变量,这样就像是生成了一个新的函数,EG.
baseTwo(x) == int(x, base=2)

除此之外,我们还可以利用函数属性的特性来为这个 “新定义” 的函数编写文档。

In [6]: baseTwo.__doc__ = 'Convert base 2 string to an int.'

In [8]: baseTwo.__doc__
Out[8]: 'Convert base 2 string to an int.'