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

第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()

原文地址:https://www.cnblogs.com/hansonwang99/p/4966706.html