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