将前面变量的赋值、流程控制等语句组合起来一方面便于维护,另一方面实现了模块化。可以通过函数、类、文件、模块甚至库来达到不同规模的要求。
5. 函数
用def语句声明函数,把形参放到一对括号里。调用函数也只要给出函数名和实参就可以了。下面这个例子用于和指定web服务器连接,并对给定路径提交一个GET请求,获取HTTP头部内容。
参数与返回值
import http.client #在Python 2中是httplib def check_web_server(host, port, path): h = http.client.HTTPConnection(host, port) h.request('GET', path) resp = h.getresponse() print('HTTP Response:') #print在Python 2中是语句,在3中是函数 print(' status =', resp.status) print(' reason =', resp.reason) print('HTTP Headers:') for hdr in resp.getheaders(): print(' %s: %s' % hdr) check_web_server('www.python.org', 80, '/') #这是调用,返回结果不展示了
函数声明里存在默认参数,会以传值的方式给对象,谨慎使用可变对象作为默认值,可能导致意料之外的行为,特别是当他们持续穿过多个函数调用时,如:
>>> def func(arg=[]): arg.append(1) print(arg) >>> func() [1] >>> func() [1, 1]
函数调用中使用关键字参数,以“键=值”形式加以指定。
也即是,Python中传递不可变的值看起来像按值传递,传递可变对象会直接修改该并反映在原始对象中。往往这并非是期望的,有一定副作用,最好避免。
return语句从函数中返回一个值 ,如果没有指定则返回 None,返回多个值可以将它们放在一个元组中。并可以类似于(x, y) = func()获取。
函数中*和**的含义
在函数声明和调用中,前者都表示元组出现(位置参数),后者说明字典出现(关键字参数)。但在函数声明中表示可扩展的参数列表,调用中表示顺次解包作为参数 。例如我们在前面的例子当中,可以先把参数放在host_info = ('www.python.prg', 80, '/'),然后调用check_web_server(*host_info),其好处不言而喻。类似地,可以用字典来完成,host_info = ('host': 'www.python.org', 'port': 80, 'path': '/'),字典把形参和实参对应起来,用check_web_server(**host_info)调用。在Django中,QuerySets动态创建ORM查询时经常包含 了**kwargs。
还可以使用**kwargs语法把关键字参数传递给另一个函数:
在为其他函数编写包装器和代理时经常这样使用。def callfunc(*args, **kwargs): func(*args, **kwargs)
作用域规则
i = 1 def countdown(start): n = start def display(): print('T-minus %d' % n) def decrement(): nonlocal n #可以使用外层函数中定义的变量 global i #可以使用函数外的变量 n-=i while n > 0: display() decrement()
函数是第一类对象
像Python对象一样使用,函数也可以存放在容器里,赋值给其他变量,作为参数传递给其他函数等。函数一经定义就和其他变量一样了,可以任意次引用。
def tip(): print('from_tip') tik = tip func_list = [tip, tik] for func in func_list: func()
Django里面经常会用到函数可以像其他值一样传递的性质,例如URLconf里面设置view,以及model feild里的默认参数等等。函数的first-class是有用的性质。
匿名函数lambda
它通常只由一个表达式生成,代表了函数“返回值”。“表达式”是一个值,结果一个是一个Python对象,这和语句不同。语法是lambda args: 表达式。其首要用途是指定短小的回调函数。例如:
a = lambda x,y: x+y r = a(2, 3)
Django中在确认用户访问权限时比较简洁,一般用作认证装饰器,如@user_passes_test(lambda u: u.is_allowed_to_vote)
装饰器
装饰(上面用@表示的),从字面上讲就是对函数的一个包装,执行一些和原来设计不同或者额外的操作,如日志、计时、过滤等。装饰器也是一个函数,一般返回对象还是原来的名称,以保持兼容性。让我们来看一个具体例子,这个例子用来跟踪一个函数:
@trace def square(x): return x*x #@trace实际上就是square = trace(square) #trace函数定义如下 enable_tracing = True if enable_tracing: debug_log = open('debug.log', 'w') def trace(func): if enable_tracing: def callf(*args, **kwargs): debug_log.write('Calling %s: %s, %s\n' %(func.__name__, args, kwargs)) r = func(*args, **kwargs) debug_log.write('%s returned %s\n' % (func.__name__, r)) return r return callf else: return func
装饰器可以套用,并可以传递参数,但在Django中不常用 。
函数比较重要的内容还有生成器、yield、协程,这里不详细记述。
6. 文件
作为具有系统级开发能力的语言,Python的文件处理方面比较庞杂,但在Django中应用的并不复杂。
打开文件f = open('tip', 'r'),或者以写的方式'w',‘a'表示附加。通过提供(+),如r+,可以打开文件进行直接更新。file对象常用的方法:
f.read([n]) | 最多读取 n个字节 |
f.readline([n]) | 读取单行输入的最多 个字符,省略则读取整行 |
f.readlines([size] | 读取所有行并返回一个列表 |
f.write(s) | 写入字符串s |
f.writeline(lines) | 写入lines中所有字符串 |
f.close() | 关闭文件 |
f.flush() | 清除输出缓冲区 |
f.__next__() | 返回下一行或引发StopIteration |
文件是可迭代对象,读取文件所有行的便捷方式是for循环。
断行符在读入时事保留的,所以一般用字符串的rstrip去掉它们。在写入文件时也要添加断行符,不然就写成一行了。