Python核心编程读笔 10:函数和函数式编程

时间:2023-03-08 16:11:03

第11章 函数和函数式编程

一 调用函数

 1 关键字参数

  def foo(x):
    foo_suite # presumably does some processing with 'x'

  标准调用 foo():  foo(42)  foo('bar')  foo(y)

  关键字调用 foo():  foo(x=42)  foo(x='bar')  foo(x=y) 即明确给出相应的参数名

2 参数组

  Python允许程序员执行一个没有显式定义参数的函数,相应的方法是通过一个把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数。

  func( *tuple_grp_nonkw_args, **dict_grp_kw_args )

3 小结

  python 中允许的函数调用的完整语法为:
    func( positional_args, keyword_args, *tuple_grp_nonkw_args, **dict_grp_kw_args )

二 创建函数

1 def语句

  def function_name(arguments):
    "function_documentation_string"
    function_body_suite

  特别注意:python不区分函数的声明与定义!

2 函数属性

  函数也可以有用.号表示的属性

3 内部函数

  在函数体内创建另外一个函数:

  def foo():

    def bar():

      print 'bar() called'

    bar()   #bar()属于函数foo的内部函数,因此只能在foo函数体内部进行调用!!!

4 传递函数

  python的函数和普通对象一样,可以被传递

  举例:

    >>> def foo():
    ... print 'in foo()'
    ...
    >>> bar = foo  函数传递!
    >>> bar()
    in foo()

  

    >>> def bar(argfunc):
    ... argfunc()
    ...
    >>> bar(foo)  函数传递!
    in foo()

三 函数参数

1 形式参数

  (1)位置参数:  必须以在被调用函数中定义的准确顺序来传递

  (2)默认参数

2 可变长度参数

  (1)非关键字可变参数(元组)

      def function_name([formal_args,] *vargs_tuple):  #所有形式参数必须先于非正式的参数之前出现!!!
        "function_documentation_string"
        function_body_suite

      举例:

      def tupleVarArgs(arg1, arg2='defaultB', *theRest):  

        print 'formal arg 1:', arg1

        print 'formal arg 2:', arg2

        for eachXtrArg in theRest:

          print  'another arg:', eachXtrArg

      

      >>> tupleVarArgs('abc', 123, 'xyz', 456.789)
      formal arg 1: abc
      formal arg 2: 123
      another arg: xyz
      another arg: 456.789

  (2)关键字变量参数(字典)

     def function_name([formal_args,][*vargst,] **vargsd):
       function_documentation_string function_body_suite

     举例:

      def dictVarArgs(arg1, arg2='defaultB', **theRest):

        print 'formal arg1:', arg1
        print 'formal arg2:', arg2
        for eachXtrArg in theRest.keys():
          print 'Xtra arg %s: %s' % \
          (eachXtrArg, str(theRest[eachXtrArg]))

      

      >>> dictVarArgs('one', d=10, e='zoo', men=('freud', 'gaudi'))

      formal arg1: one

      formal arg2: defaultB

      Xtra arg men: ('freud', 'gaudi')

      Xtra arg d: 10

      Xtra arg e: zoo

   (3)关键字和非关键字可变长参数同时出现的情形

      要求:关键字字典是最后一个参数并 且非关键字元组先于它之前出现

      def newfoo(arg1, arg2, *nkw, **kw):
        print 'arg1 is:', arg1

        print 'arg2 is:', arg2

        for eachNKW in nkw:
          print 'additional non-keyword arg:', eachNKW
        for eachKW in kw.keys():
          print "additional keyword arg '%s': %s" % \
          (eachKW, kw[eachKW])

      >>> newfoo('wolf', 3, 'projects', freud=90, gamble=96)

      arg1 is: wolf arg2 is: 3
      additional non-keyword arg: projects
      additional keyword arg 'freud': 90
      additional keyword arg 'gamble': 96

   (4)更多举例

      >>> newfoo(2, 4, *(6, 8), **{'foo': 10, 'bar': 12})
      arg1 is: 2
      arg2 is: 4
      additional non-keyword arg: 6
      additional non-keyword arg: 8
      additional keyword arg 'foo': 10
      additional keyword arg 'bar': 12

      >>> aTuple = (6, 7, 8)
      >>> aDict = {'z': 9}
      >>> newfoo(1, 2, 3, x=4, y=5, *aTuple, **aDict)
      arg1 is: 1
      arg2 is: 2
      additional non-keyword arg: 3
      additional non-keyword arg: 6
      additional non-keyword arg: 7
      additional non-keyword arg: 8
      additional keyword arg 'z': 9
      additional keyword arg 'x': 4
      additional keyword arg 'y': 5

四 函数式编程

1 匿名函数与lambda

  python 允许用 lambda 关键字创造匿名函数。匿名是因为不需要以标准的def方式来声明。

  lambda [arg1[, arg2, ... argN]]: expression

  举例:

    def add(x, y): return x + y          等价于   lambda x, y: x + y

    def usuallyAdd2(x, y=2): return x+y    等价于   lambda x, y=2: x+y

    def showAllAsTuple(*z): return z     等价于   lambda *z: z

    >>>a = lambda x, y=2: x + y

    >>>a(3)
    5

    >>>b = lambda *z: z
    >>> b(23, 'zyx')
    (23, 'zyx')
    >>> b(42)
    (42,)

2 内建函数apply()、filter()、map()、reduce()

  lambda 函数可以很好的和这使用了这四个函数的应用程序结合起来,因为它们都带了一个可执行的函数对象,lambda 表达式提供了迅速创造这些函数的机制

  apply( func[, nkw][, kw] )  用可选的参数来调用 func,nkw 为非关键字参数,kw 关键字参数;返回值是函数调用的返回值。

  filter( func, seq) 调用一个布尔函数 func 来迭代遍历每个 seq 中的元素; 返回一个 使 func 返回值为 ture 的元素的序列。

  map( func, seq1[,seq2...] ) 将函数 func 作用于给定序列(s)的每个元素,并用一个列表来提供返回值;如果 func为None,func 表现为一个身份函数,返回一个含有每个序列中元素集合的 n 个元组的列表。

  reduce( func, seq[, init] ) 将二元函数作用于 seq 序列的元素,每次携带一对(先前的结果 以及下一个序列元素),连续的将现有的结果和下雨给值作用在获 得的随后的结果上,最后减少我们的序列为一个单一的返回值;如 果初始值 init 给定,第一个比较会是 init 和第一个序列元素而不 是序列的头两个元素。

  

  举例:

    filter()

      from random import randint

      def odd(n):
        return n % 2
      allNums = []
      for eachNum in range(9):
        allNums.append(randint(1, 99))
      print filter( odd, allNums )

      改变1:用一个 lambda 表达式替换

      from random import randint

      allNums = []
      for eachNum in range(9):
        allNums.append(randint(1, 99))
      print filter( lambda n: n%2, allNums )  #使用了lambda表达式!!!

      

      改变2:list 综合使用如何能成为 filter()合适的替代者

      from random import randint
      allNums = []
      for eachNum in range(9):
        allNums.append(randint(1, 99))
      print [n for n in allNums if n%2]

      改变3:利用列表解析!

      from random import randint as ri
      print [n for n in [ri(1,99) for i in range(9)] if n%2]

    map():

      map( (lambda x: x+2), [0, 1, 2, 3, 4, 5] )

      

      带有多个序列的map()的例子:

      >>> map( lambda x, y: x + y, [1,3,5], [2,4,6])
      [3, 7, 11]
      >>>
      >>> map( lambda x, y: (x+y, x-y), [1,3,5], [2,4,6] )
      [(3, -1), (7, -1), (11, -1)]
      >>>
      >>> map( None, [1,3,5], [2,4,6] )
      [(1, 2), (3, 4), (5, 6)]

    reduce():

      >>> print 'the total is:', reduce( (lambda x,y: x+y), range(5) )
      the total is: 10  #0+1+2+3+4=10

3 偏函数应用(PFA)

  这种函数将任意数量(顺序)的参数的函数转化成另一个带剩余参数的函数对象

  例子:

    >>> from operator import add, mul
    >>> from functools import partial
    >>> add1 = partial(add, 1)         # add1(x) == add(1, x)
    >>> mul100 = partial(mul, 100)     # mul100(x) == mul(100, x)
    >>>
    >>> add1(1)
    2
    >>> mul100(10)
    1000

    >>> baseTwo = partial(int, base=2)
    >>> baseTwo.__doc__ = 'Convert base 2 string to an int.'
    >>> baseTwo('10010')
    18
    这个例子使用了 int()内建函数并将 base 固定为2来指定二进制字符串转化。现在没有多次用相同的第二参数2来调用int (), 比如('10010', 2),而可以只用带一个参数的新 baseTwo()函数。

    注意警惕关键字:

    这个例子如果你创建了不带 base 关键字的偏函数,比如, baseTwoBAD = partial(int, 2),这可能 会让参数以错误的顺序传入 int(),因为固定参数的总是放在运行时刻参数的左边, 比如 baseTwoBAD(x) == int(2, x)。如果你调用它,它会将2作为需要转化的数字,而base 作为'10010' 来传入,然后就产生一个异常:

    >>> baseTwoBAD = partial(int, 2)  #词句和上面的区别就在于没有写base=2
    >>> baseTwoBAD('10010')
    Traceback (most recent call last): File "<stdin>", line 1, in <module>
    TypeError: an integer is required

五 变量作用域

1 全局变量和局部变量

2 globa语句

  为了明确地引用一个已命名的全局变量,须使用global修饰:

   global var1

3 闭包

    若在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closure。定义在外部函数内的但由内部函数引用或者使用的变量被称为*变量。

  简单闭包的例子:

    def counter(start_at=0):

      count = [start_at]

      def incr():
        count[0] += 1
        return count[0]
      return incr         #注意返回的是一个可调用的函数对象!!!

    >>> count = counter(5)
    >>> print count()
    6
    >>> print count()
    7
    >>> count2 = counter(100)
    >>> print count2()
    101
    >>> print count()
    8

4 作用域和lambda

  python的lambda匿名函数遵循和标准函数一样的作用域规则。一个 lambda 表达式定义了新的作用域。

六 生成器

生成器是一个带 yield语句的函数。一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果----那就是 yield 语句的功能,返回一个值给调用者并暂停执行。当生成器的 next()方法被调用的时候,它会准确地从离开地方继续

简单的生成器特性:

  def simpleGen():
    yield 1
    yield '2 --> punch!'

  >>> myG = simpleGen()
  >>> myG.next()
  1
  >>> myG.next()
  '2 --> punch!'
  >>> myG.next()
  Traceback ( most recent call last ):
  File "", line 1, in ?
  myG.next() StopIteration

  

  from random import randint
  def randGen(aList):
    while len(aList) > 0:
      yield aList.pop( randint(0, len(aList) ) )
  >>> for item in randGen(['rock', 'paper', 'scissors']):
  ...     print item
  ...
  scissors
  rock
  paper

加强的生成器特性:

  在 python2.5 中,一些加强特性加入到生成器中,所以除了next()来获得下个生成的值,用户可以将值回送给生成器,在生成器中抛出异常,以及要求生成器退出:

  def counter(start_at=0):
    count = start_at
    while True:
      val = (yield count)

      if val is not None:
        count = val
      else:
        count += 1

  >>> count = counter(5)
  >>> count.next()
  5
  >>> count.next()
  6
  >>> count.send(9)
  9
  >>> count.next()
  10
  >>> count.close()