Python——追加学习笔记(四)

函数

1、python里的函数可以返回一个值或者对象,知识在返回一个容器对象的时候有点不同,看起来像是能返回多个对象。

>>> def bar():
...     return 'abc', '123'
...
>>> bar()
('abc', '123')
>>> type(bar())   #返回的其实是元组
<type 'tuple'>

简而言之,如果没有显式的返回于元素或者若果返回None时,python会返回一个None。如果函数返回多个对象,python把他们聚集起来并以一个元组返回。

调用函数

1、以一对圆括号调用函数.

>>> bar()
('abc', '123')

创建函数

1、def 语句

语法:

def function_name(arguments):
    "function_documentation_string"  #函数注释
    function_body_suite

示例:

>>> def bar():
...     "return a tuple"    #函数注释
...     return 'abc', '123'
...
>>> bar()       #函数调用
('abc', '123')

2、前向引用

语言已经无法表达了。

>>> def foo():
...     print 'In foo()'
...     bar()
...
>>> def bar():
...     print 'In bar()'
...
>>> foo()
In foo()
In bar()

3、函数属性

>>> def foo():
...     print 'In foo()'
... 
>>> foo.__doc__     #函数注释
>>> foo.__name__    #函数名字
'foo'
>>> foo.version = 0.1   #函数版本
>>> foo.__doc__ = 'Make a test function'
>>> help(foo)   #可以输出函数的注释文本

4、内部/内嵌函数

eg.
python解释器输出:

>>> def foo():
...     def sar():
...             print 'sar() called'
...     print 'foo() called'
...     sar()
...
>>> foo()
foo() called
sar() called
>>> sar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sar' is not defined

将上述函数整合为一个模块:#inner.py

#!/usr/bin/env python 
# -*- coding:utf-8 -*- 
# Script_Name: inner.py
def foo():
    def sar():
        print 'sar() called'
    print 'foo() called'
    sar()
    
if __name__ == '__main__':
    foo()
    sar()

带有内嵌函数的外部函数,其执行顺序依然是顺序执行。
执行inner.py结果:

foo() called
sar() called
Traceback (most recent call last):
  File ".\inner.py", line 18, in <module>
    bar()
NameError: name 'sar' is not defined

内嵌函数整个函数体都是在外部函数的作用域之内的,如果没有对内部函数的外部引用,那么除了函数体内,任何地方都不会对其起作用。

5、内嵌函数体补充

  • func1():
>>> def func1():
...     def fo():
...         print 'fo() called'
...     return fo
...
>>> func1()
<function fo at 0x7fa699baab90>
>>> result = fun1c()
>>> type(result)
<type 'function'>

这里return fo返回的是一个函数对象的引用。

  • func2():
>>> def func2():
...     def fo():
...         print 'fo() called'
...     return fo()
...
>>> func2()
fo() called

这里的返回[return fo()]的是一个函数(对象)的调用。

6、传递函数(函数对象的引用和函数对象的调用)

当对一个对象赋值时,世纪时将相同对象的引用复制给这个变量,如果这个对象是一个函数时,这个对象所有的别名都是可调用的。
eg:

>>> def foo():
...     print 'in foo()'
...
>>> bar = foo #实际上是bar和foo引用了同一个函数对象,foo是函数对象的引用
>>> foo()   #foo()是函数对象的调用
in foo()
>>> bar()
in foo()
>>>
>>> def bar(argfunc):   #传入的参数:argfunc就是函数对象foo
...     argfunc()   #()双圆括号是函数调用的操作符,函数调用
...
>>> bar(foo)
in foo()

7、默认参数

实际上就是定义函数时允许设定默认的参数,如下例中的rate=0.0825.

>>> def taxMe(cost, rate=0.0825):
...     return cost + (cost * rate)
...
>>> taxMe(100)
108.25
>>> taxMe(100, 0.05)
105.0

切记:所有必须的参数都要在默认参数之前,也就是说默认参数在最后。这是强制规定。

>>> def taxMe(cost = 100, rate):
...     return cost + (cost * rate)
...
  File "<stdin>", line 1
SyntaxError: non-default argument follows default argument

8、可变长度的参数

  • 非关键字可变长参数参数(元组)
    函数调用时可以接受一个非固定数目的参数,其使用元组的方式。
    可变长参数元组必须在位置和默认参数之后,且使用星号操作符,*(星号操作符)之后的形参座位元组传递给函数,元组保存了所有的传递给函数的额外的参数,如果没有给出额外的参数,那么元组为空。
>>> def test(arg1, arg2 = 'default', *args):
...     print 'arg1 ——>> ', arg1
...     print 'arg2 ——>> ', arg2
...     for each in args:
...             print 'another arg ——>> ', each
...
>>> test('abc')
arg1 ———>>  abc
arg1 2— —  default
>>> test('abc', '123')
arg1 ———>>  abc
arg1 2— —  123
>>> test('abc', '123', 'def', 456)
arg1 ———>>  abc
arg1 2— —  123
another arg ——>>  def
another arg ——>>  456
  • 关键字变量参数(字典)
    与非关键字可变长参数参数(元组)类似,只不过换成了**表示了。依然放在最后面,在存在元组的情况下依然在最后。
>>> def test(arg1, arg2 = 'default', **args):
...     print 'arg1 -->> %s' % arg1
...     print 'arg2 -->> %s' % arg2
...     for each in args:
...             print '%s -->> %s' % (each, args[each])
...
>>> test(123, 456, a=789)
arg1 -->> 123
arg2 -->> 456
a -->> 789
>>> test(123, 456, a=789, b=101112)
arg1 -->> 123
arg2 -->> 456
a -->> 789
b -->> 101112
>>> test(123, 456, a=789, b=101112, c='abcdefg')
arg1 -->> 123
arg2 -->> 456
a -->> 789
c -->> abcdefg
b -->> 101112

元组和字典参数共同存在的情况下:

>>> def test(arg1, arg2 = 'default', *argt, **args):
...     print 'arg1 -->> %s' % arg1
...     print 'arg2 -->> %s' % arg2
...     for each in argt:
...             print 'Tuple -->> %s' % each
...     for each in args:
...             print '%s -->> %s' % (each, args[each])
...
>>> test(123, 456, 789, dict='abc')
arg1 -->> 123
arg2 -->> 456
Tuple -->> 789
dict -->> abc
>>> test(123, 456, 789, 'abc', dict1='abc', dict2=135)
arg1 -->> 123
arg2 -->> 456
Tuple -->> 789
Tuple -->> abc
dict1 -->> abc
dict2 -->> 135
  • 调用带有可变长参数对象函数
    我们将非关键字参数放在元组中,将关键字参数放在字典中。
>>> def test(arg1, arg2 = 'default', *argt, **args):
...     print 'arg1 -->> %s' % arg1
...     print 'arg2 -->> %s' % arg2
...     for each in argt:
...             print 'Tuple -->> %s' % each
...     for each in args:
...             print '%s -->> %s' % (each, args[each])
...
>>>
>>>
>>> test(10, 20, 30)
arg1 -->> 10
arg2 -->> 20
Tuple -->> 30
>>> test(10, 20, 30, 40)
arg1 -->> 10
arg2 -->> 20
Tuple -->> 30
Tuple -->> 40
>>> test(10, 20, 30, 40, foo=50)
arg1 -->> 10
arg2 -->> 20
Tuple -->> 30
Tuple -->> 40
foo -->> 50
>>> test(10, 20, 30, 40, foo=50, bar=60)
arg1 -->> 10
arg2 -->> 20
Tuple -->> 30
Tuple -->> 40
foo -->> 50
bar -->> 60
>>> test(10, 20, *(30, 40), **('foo':50, 'bar':60))     #字典使用{}花括号,元组使用()圆括号
  File "<stdin>", line 1
    test(10, 20, *(30, 40), **('foo':50, 'bar':60))
                                    ^
SyntaxError: invalid syntax
>>> test(10, 20, *(30, 40), **{'foo':50, 'bar':60})
arg1 -->> 10
arg2 -->> 20
Tuple -->> 30
Tuple -->> 40
foo -->> 50
bar -->> 60

此外,我们还可以在函数调用之外创建元组和字典:

>>> t = (30, 40)
>>> d = {'foo':50, 'bar':60}
>>> test(10, 20, *t, **d)
arg1 -->> 10
arg2 -->> 20
Tuple -->> 30
Tuple -->> 40
foo -->> 50
bar -->> 60

8、函数式编程

  • 匿名函数和lambda
    lamdba 表达式运行起来就像一个函数,当被调用时,创建一个框架对象。
    语法格式:
lambda [arg1[, arg2, arg3,....argN]] : expression

示例:

>>> lambda x, y : x + y
<function <lambda> at 0x6ffffe276e0>

lambda 表达式可以用来赋值给一个如列表和元组的数据结构。

>>> n = lambda *z : z
>>> n('a', 'b')
('a', 'b')
>>> m = lambda **y : y.keys()
>>> m(a=123)
['a']
>>> m(a=123, b=456)
['a', 'b']
>>> p=lambda x : x + x
>>> p(1)
2
>>> p('a')
'aa'
  • 内建函数filter()、map()、reduce()

1、filter()

filter()是一个过滤函数。

filter(func, seq) #func判断的布尔函数, seq需要过滤的序列

调用一个布尔函数func来迭代遍历每个seq中的元素,返回一个使func返回值为true的元素的序列。
eg:

>>> def tell(x):
...     return x % 2
...
>>> filter(tell, range(10))
[1, 3, 5, 7, 9]
#   可以与lambda表达式结合
>>> filter(lambda x:x%2, range(10))     
[1, 3, 5, 7, 9]
# 更简单的过滤方法
>>> [x for x in range(10) if x % 2]
[1, 3, 5, 7, 9]

2、map()

实际上就是一个针对所有的数据进行遍历的函数。

map(func, seq)  

eg:

>>> map((lambda x: x+3), range(10))
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> def foo(x, y):
...     return x+y
...
>>> map(foo, range(10), range(10))      #这里的func是foo而不是foo(),这点要特别注意,在之前的笔记中有提到,foo是函数的引用,foo()是函数对象的调用,这里只能是引用,函数对象的调用需要有参数,如果你执行调用会报错
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> map((foo(x, y)), range(10), range(10))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
# 使用函数对象的调用会报错

3、reduce()

语法格式:

reduce(func, seq[, init])

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

reduce()实际上就是一个迭代器,对seq进行逐个迭代,其中前两个作为初始值。从seq[]中依此选择两个值传入func函数,得出新的结果,并将seq中被选择的两个数值替换为新的结果,然后重复上述过程,直至结束。典型的例子就是数列求和:

#求[0, 1, ..., 9]的和:
>>> reduce((lambda x, y : x + y), range(10))
45

9、偏函数

好吧,实际上我也不是很懂。。。。。。。。。。。。。。。

>>> from operator import add, mul
>>> from functools import partial
# 泛化偏函数(PFA)使用partial,可以简化代码
>>> add1 = partial(add, 1)    # add1(x) == add(1, x)
>>> mul100 = partial(mul, 100)    #mul100(x) == mul(100, x)
>>>
>>> add1(10)
11
>>> mul100(10)
1000
  • 简单GUI
from functools import partial
import Tkinter

root = Tkinter.Tk()
MyButton = partial(Tkinter.Button, root, fg='white', bg='blue')
# MyButton(x) == TKinter.Button(root, fg='white', bg='blue', x)
b1 = MyButton(text='Button1')
b2 = MyButton(text='Button2')
qb = MyButton(text='QUIT', bg='red', command=root.quit)

b1.pack()
b2.pack()
qb.pack(fill=Tkinter.X, expand=True)
root.title('PFAs!')
root.mainloop()

运行截图:

10、变量作用域

  • 全局变量和局部变量
    定义在函数内的变量有局部作用域,在一个模块中的最高级的变量有全局作用域。
    全局变量的一个特征就是除非被剔除掉,否则它们会存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的。而局部变量,仅仅是暂时存在的,只依赖于定义他们的函数现阶段是否处于活动。当一个函数调用出现时,其局部变量就进入声明他们的作用域。在那一刻,一个新的局部变量名为那个对象创建了,一旦函数完成,框架被释放,变量就会离开作用域。

  • global 语句
    如果将全局变量的名字声明在一个函数体内的时候,全局变量的名字能被局部变量给覆盖掉。

>>> is_this_global = 'xyz'
>>> def  foo():
...         this_is_local = 'abc'
...         is_this_global = 'def'  #局部变量覆盖了全局变量
...         print this_is_local + is_this_global
...
>>> foo()
abcdef

为了明确地引用一个已经命名的全局变量,必须使用global语句。

global var1[, var2, ..., varN]
>>> is_this_global = 'xyz'
>>> def  foo():
...         global is_this_global
...         this_is_local = 'abc'
...         is_this_global = 'def'
...         print this_is_local + is_this_global
...
>>> foo()
abcdef
  • 闭包
    在一个内部函数内,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。
    定义在外部函数内但由内部函数引用或者使用的变量称为自由变量。
    故闭包所满足的条件是:
1. 必须有一个内嵌函数
2. 内嵌函数必须引用外部函数中的变量
3. 外部函数的返回值必须是内嵌函数
>>> def counter(start_at=0):
...     count = [start_at]
...     def incr():
...             count[0] += 1
...             return count[0]
...     return incr
...
>>> count = counter(5)
>>> count()
6
>>> count()
7
>>> count2 = counter(25)
>>> count2()
26
>>> count2()
27
>>> count3 = counter(5) # 每次调用counter()都会返回一个新的函数,即使传入相同的参数也是不同。
>>> count == count3
False

11、生成器

  • 1、yield 是一个关键字。带有yield的函数不再是一个函数,而是一个生成器,可用于迭代。其迭代的关键是next()方法,工作原理就是通过重复调用next()方法,直至捕捉一个异常。
  • 2、yield是一个类似return的关键字,迭代一次返回yield后面的值(yield后面也可以是一个函数的调用或者表达式),然后脚本就会在这里停止,当再次迭代后,从下一行开始。
  • 3、yield是类似return一样返回一个值,如果yield和它后面的值被赋值给其他变量,那么这个新的变量就是None,除非是用send()方法才能将yield后面的值赋给新的变量。
  • 4、第一次调用时必须先next()或者send(None),否则会报错。
>>> def g():
...     print '1'
...     x = yield 'hello'
...     print '2', 'x = ', x
...     y = 5 + (yield x)
...     print '3', 'y = ', y
...
>>> f = g()
>>> f.send(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>> f.send(None)
1
'hello'

示例代码过程解读:

# next()方法
>>> def g():
...     print '1'
...     x = yield 'hello'
...     print '2', 'x = ', x
...     y = 5 + (yield x)
...     print '3', 'y = ', y
...
>>> f = g()
>>> f.next()  #第一次迭代时[yield 'hello']返回['hello'],并赋值x为None,程序执行进程停止
1
'hello'
>>> f.next()    #第二次迭代,从第一次停止的下一行开始,从输出可以看出x的值为None
2 x =  None

# send()方法
>>> def g():
...     print '1'
...     x = yield 'hello'
...     print '2', 'x = ', x
...     y = 5 + (yield x)
...     print '3', 'y = ', y
...
>>> f = g()
>>> f.next()
1
'hello'
>>> f.send(5)   #send()方法会重置第一次迭代的yield,并重新赋值为send传递的新值,而且此时x会被赋值为新的值,且程序将向下执行,直到遇见新的yield。
2 x =  5
5
>>> f.send(2)   # 上次使用send()方法后yield x返回5,并在此处停止,在send(2)后,重置(yield x)为新值2.
3 y =  7
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
posted @ 2017-12-22 16:00  水木梵华  阅读(327)  评论(0编辑  收藏  举报