[Python基础]函数
来源:互联网 发布:mac显示磁盘 编辑:程序博客网 时间:2024/06/03 21:48
2.x和3.x的print使用方式不一样,这里不做太多针对版本的修正,大家使用案例时注意打印方式就行了,3.x需要括号。
1.定义函数
函数通过 def 关键字定义。
def 关键字后跟一个函数的 标识符 名称,然后跟一对圆括号。圆括号之中可以包括一些变量名,该行以冒号结尾。
接下来是一块语句,它们是函数体。
例:
def sayHello(): print('Hello World!') # block belonging to the function sayHello() # call the function2.函数形参
函数中的参数名称为 形参 而你提供给函数调用的值称为 实参 。
3.局部变量
当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的。
这称为变量的 作用域 。所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。
x = 50def func(x): print('x is', x) x = 2 print('Changed local x to', x) # 打印: 2func(x)print('x is still', x) # 打印: 50, 值没有变4.global 语句
如果要为一个定义在函数外的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是 全局 的。使用 global 语句完成这一功能。
没有 global 语句,是不可能为定义在函数外的变量赋值的。
你可以使用定义在函数外的变量的值(假设在函数内没有同名的变量)。然而,应避免这样做,因为这降低程序的可读性,不清楚变量在哪里定义的。
使用global语句可以清楚地表明变量是在外面的块定义的。
注:可以使用同一个 global 语句指定多个全局变量。例如 global x, y, z。
def func(): global x print('x is', x) x = 2 print('Changed local x to', x) # 打印: 2x = 50func()print('Value of x is', x) # 打印: 2, 值被改变了# 错误示例# 局部函数里要改变全局变量,将会报错:UnboundLocalError: local variable 'CONSTANT' referenced before assignmentCONSTANT = 0def modifyConstant(): CONSTANT += 1 # 函数内部可以直接访问全局变量,但直接改变则会报错 print CONSTANTmodifyConstant()# 上面示例 的正确写法CONSTANT = 0def modifyConstant(): global CONSTANT # 使用 global, 则函数内部可以直接改变全局变量了 CONSTANT += 1 print CONSTANTmodifyConstant()5.默认参数值
如果希望一些参数是 可选 的,这些参数可使用默认值。
可以在函数定义的形参名后加上赋值运算符(=)和默认值,从而给形参指定默认参数值。
注意,默认参数值应该是一个参数。
def say(message, times = 2): print(message * times)say('Hello ') # 打印:Hello Hellosay('World ', 5) # 打印:World World World World World重要:
只有在形参表末尾的那些参数可以有默认参数值,即不能在声明函数形参的时候,先声明有默认值的形参而后声明没有默认值的形参。
这是因为赋给形参的值是根据位置而赋值的。例如,def func(a, b=5)是有效的,但是def func(a=5, b)是 无效 的。
6.关键参数
如果某个函数有许多参数,而你只想指定其中的一部分,那么可以通过命名来为这些参数赋值
——这被称作 关键参数 ——使用名字(关键字)而不是位置来给函数指定实参。
这样做有两个优势:
一、由于我们不必担心参数的顺序,使用函数变得更加简单了。
二、假设其他参数都有默认值,我们可以只给我们想要的那些参数赋值。
def func(a, b=5, c=10): print('a is', a, 'and b is', b, 'and c is', c)func(3, 7) # 参数a得到值3,参数b得到值7,而参数c使用默认值10。func(25, c=24) # 根据实参的位置,变量a得到值25。根据命名,即关键参数,参数c得到值24。变量b根据默认值,为5。func(c=50, a=100) # 使用关键参数来完全指定参数值。a得到值100,c得到值50。变量b根据默认值,为5。7.return 语句
return 语句用来从一个函数 返回 即跳出函数。我们也可选从函数 返回一个值 。
例:def maximum(x, y): if x > y: return x else: return yprint(maximum(2, 3)) # 打印 38.函数属性 func_*
在Python 2里,函数的里的代码可以访问到函数本身的特殊属性。在Python 3里,为了一致性,这些特殊属性被重新命名了。
Python 2 与 Python 3 的比较
Python 2 Python 3 说明 ① a_function.func_name a_function.__name__ # 包含了函数的名字。 ② a_function.func_doc a_function.__doc__ # 包含了在函数源代码里定义的文档字符串(docstring)。 ③ a_function.func_defaults a_function.__defaults__ # 是一个保存参数默认值的元组。 ④ a_function.func_dict a_function.__dict__ # 一个支持任意函数属性的名字空间。 ⑤ a_function.func_closure a_function.__closure__ # 一个由cell对象组成的元组,包含了函数对自由变量(free variable)的绑定(闭包列表)。 ⑥ a_function.func_globals a_function.__globals__ # 一个对模块全局名字空间的引用,函数本身在这个名字空间里被定义。 ⑦ a_function.func_code a_function.__code__ # 一个代码对象,表示编译后的函数体。 ⑧ a_function.func_module a_function.__module__ # 所在 Module 的名称(func_module 其实无法调用)10.
默认参数: 如果调用的时候没指定,那它会是函数定义时的引用;(只在加载时定义一次,以后都是调用同一个默认参数)
因此,默认参数建议使用基本类型;如果不是基本类型,建议写 None,然后在函数里面设默认值
##### 范例1,默认参数如果是 []、{} ,将会影响全局 ########def t1(a, b = []): b.append(a) print('%s %s' % (id(b), b))t1(1) # 打印: 12523400 [1]t1(2) # 打印: 12523400 [1, 2]t1(3, b=[]) # 打印: 12545000 [3]def t2(a, b = {}): b[len(b)] = a print('%s %s' % (id(b), b))t2(1) # 打印: 12540928 {0: 1}t2(2) # 打印: 12540928 {0: 1, 1: 2}t2(3, b={}) # 打印: 11547392 {0: 3}##### 范例2,如果默认的是其它的函数调用,同样原理,默认值只是函数定义时的引用,后面不再改变 ########import timedef cc(a,b = time.time()): print('%s %s' % (a,b))cc(1) # 打印: 1 1306501851.48cc(1,b=2) # 打印: 1 2cc(2) # 打印: 2 1306501851.48##### 范例3,只是为了更好的理解上述所讲 ########def aa(): print('aa...') return []# 只在函数定义时,执行被调用的 aa(), 后面不再执行def bb(a,b = aa()): b.append(1) print('%s %s' % (id(b), b))bb(1) # 打印: 12542840 [1]bb(2) # 打印: 12542840 [1, 1]################################################# 范例4, 为避免上面的出错,正确的写法是这样的:def t1(a, b = None): b = b or [] b.append(a) print('%s %s' % (id(b), b))def t2(a, b = None): b = b or {} b[len(b)] = a print('%s %s' % (id(b), b))import timedef cc(a, b = None): b = b or time.time() print('%s %s' % (a,b))10.条件参数列表
在实际开发中,我们会遇到如下一种需求:
1. 默认条件有 (a, b, c, d ...),总之很多。
2. 调用者可以传递 (b = False, c = False) 来提供 "非" 条件,其他默认为 True。
3. 或者传递 (b = True, c = True),其他默认为 False。
4. 还可以用 (all = True, ...) 来明确指定默认值。
def test(**on): # 全部条件列表 accept_args = ("a", "b", "c", "d", "e") # 默认条件 default = on.pop("all", None) # 如果没有显式指明默认条件,则检查参数列: # 1. 如果有任何一个 True 条件则默认值为 False。 # 2. 如果全部为 False,则默认值为 True。 if default is None: default = not(True in on.values()) # 使用 setdefault 补全参数字典 for k in accept_args: on.setdefault(k, default) return onprint test(b = False, e = False) # 显示:{'a': True, 'c': True, 'b': False, 'e': False, 'd': True}print test(c = True) # 显示:{'a': False, 'c': True, 'b': False, 'e': False, 'd': False}print test(a = True, e = False) # 显示:{'a': True, 'c': False, 'b': False, 'e': False, 'd': False}print test(all = True, c = False, e = True) # 显示:{'a': True, 'c': False, 'b': True, 'e': True, 'd': True}print test(all = True, c = False, e = False) # 显示:{'a': True, 'c': False, 'b': True, 'e': False, 'd': True}print test(all = False, c = True, e = True) # 显示:{'a': False, 'c': True, 'b': False, 'e': True, 'd': False}11.使用 * 和 ** 来传递参数
Python 2.x 提供了另个方法来做相同的事. 你只需要使用一个传统的函数调用 , 使用 * 来标记元组, ** 来标记字典.
下面两个语句是等价的:
result = function(*args, **kwargs)result = apply(function, args, kwargs)lambda 同样支持默认值和变参,使用方法完全一致。
test = lambda a, b = 0, *args, **kwargs: (a, b, args, kwargs,)print test(1, *[2, 3, 4], **{"x": 5, "y": 6}) # 打印: (1, 2, (3, 4), {'y': 6, 'x': 5})12.在函数中接收元组和列表(函数的参数数量可以变动,即可变长参数)
当要使函数接收元组或字典形式的参数的时候, 有一种特殊的方法, 它分别使用*和**前缀。
这种方法在函数需要获取可变数量的参数的时候特别有用。
而且, 使用*和**前缀的参数还可以传递给其它函数。
# 由于在args变量前有*前缀, 所有多余的函数参数都会作为一个元组存储在args中def sum(message, *args): '''Return the sum of each argument.''' total = 0 # 除了用循环,也可以用下标来读取参数,如: args[0] for i in args: total += i print (str(type(args)) + ' ' + message + ":" + str(total)) sum2(args) # 这样传过去的 args 是一个元组;打印如: ((3, 5.5),) sum2(*args) # 这样传过去的 *args 表示多个参数;打印如:(3, 5.5)def sum2(*args): print(args)sum('hight', 3, 5.5) # 打印: <type 'tuple'> hight:8.5sum('weight', 10) # 打印: <type 'tuple'> weight:10# 函数参数接收字典用法。使用的是**前缀, 多余的参数则会被认为是一个字典的键/值对。def printDict(message, **args): print(str(type(args)) + ' ' + message + ':' + str(args)) printDict2(args = args) # 可这样, 把 args 当做一个值(里面是字典), 传过去;打印如: {'args': {'a': 3, 'b': 'dd'}} printDict2(**args) # 也可这样, 把 **args 看做传过来的多个键/值对, 传过去;打印如:{'a': 3, 'b': 'dd'}def printDict2(**args): print(args)# 注意:参数为字典时,参数里面必须使用等号,否则运行出错printDict('hight', a=3, b='dd') # 打印: <type 'dict'> hight:{'a': 3, 'b': 'dd'}# 可以混合使用*和**前缀的参数, 但是必须 *args 在前, **args 在后,否则编译不通过def printMul(message, *args1, **args2): print(message + ' args1:' + str(args1) + ' args2:' + str(args2))printMul('hello', 5, 4, a=2, b=3) # 打印: hello args1:(5, 4) args2:{'a': 2, 'b': 3}13.闭包
闭包是指:当函数离开创建环境后,依然持有其上下文状态。如下面的 a 和 b, 在离开 test 函数后,依然持有 test.x 对象
def test(): x = [1, 2] print hex(id(x)) def a(): print hex(id(x)), x x.append(3) def b(): print hex(id(x)), x return a, ba, b = test() # 打印: 0x986260a() # 指向 test.x, 打印: 0x986260 [1, 2]b() # 打印: 0x986260 [1, 2, 3]print a.func_closure # 打印: (<cell at 0x00988210: list object at 0x00986260>,)print b.func_closure # 打印: (<cell at 0x00988210: list object at 0x00986260>,)#test 在创建 a 和 b 时,将它们所引用的外部对象 x 添加到 func_closure 列表中。因为 x 引用计数增加了,所以就算 test 堆栈帧没有了, x 对象也不会被回收。'''通过 func_code,可以获知闭包所引用的外部名字。•co_cellvars: 被内部函数引用的名字列表。•co_freevars: 当前函数引用外部的名字列表。'''print test.func_code.co_cellvars # 被内部函数 a 引用的名字。打印: ('x',)print a.func_code.co_freevars # a 引用外部函数 test 中的名字。打印: ('x',)14.堆栈帧
Python 堆栈帧基本上就是对 x86 的模拟,用指针对应 BP、SP、IP 寄存器。
堆栈帧成员包括函数执行所需的名字空间、调用堆栈链表、异常状态等。
typedef struct _frame { PyObject_VAR_HEAD struct _frame *f_back; # 调用堆栈 (Call Stack) 链表 PyCodeObject *f_code; # PyCodeObject PyObject *f_builtins; # builtins 名字空间 PyObject *f_globals; # globals 名字空间 PyObject *f_locals; # locals 名字空间 PyObject **f_valuestack; # 和 f_stacktop 共同维护运行帧空间,相当于 BP 寄存器。 PyObject **f_stacktop; # 运行栈顶,相当于 SP 寄存器的作用。 PyObject *f_trace; # Trace function PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; # 记录当前栈帧的异常信息 PyThreadState *f_tstate; # 所在线程状态 int f_lasti; # 上一条字节码指令在 f_code 中的偏移量,类似 IP 寄存器。 int f_lineno; # 与当前字节码指令对应的源码行号 ... ... PyObject *f_localsplus[1]; # 动态申请的一段内存,用来模拟 x86 堆栈帧所在内存段。} PyFrameObject;可使用 sys._getframe(0) 或 inspect.currentframe() 获取当前堆栈帧。
其中 _getframe() 深度参数为 0 表示当前函数, 1 表示调用堆栈的上个函数。
除用于调试外,还可利用堆栈帧做些有意思的事情。
# 范例1: 权限管理# 通过调用堆栈检查函数 Caller, 以实现权限管理。import sysdef save(): f = sys._getframe(1) if not f.f_code.co_name.endswith("_logic"): # 检查 Caller 名字,限制调用者身份。 raise Exception("Error!") # 还可以检查更多信息。 print "ok"def test(): save()def test_logic(): save()test() # Exception: Error!test_logic() # ok# 范例2: 上下文# 通过调用堆栈,我们可以隐式向整个执行流程传递上下文对象。# inspect.stack 比 frame.f_back 更方便一些。import inspectdef get_context(): for f in inspect.stack(): # 循环调用堆栈列表。 context = f[0].f_locals.get("context") # 查看该堆栈帧名字空间中是否有目标。 if context: return context # 找到了就返回,并终止查找循环。def controller(): context = "ContextObject" # 将 context 添加到 locals 名字空间。 model()def model(): print get_context() # 通过调用堆栈查找 context 。controller() # 测试通过, 打印:ContextObject15.包装
用 functools.partial() 可以将函数包装成更简洁的版本。
from functools import partialdef test(a, b, c): print a, b, cf = partial(test, b = 2, c = 3) # 为后续参数提供命名默认值。f(1) # 打印: 1 2 3f2 = partial(test, 1, c = 3) # 为前面的位置参数和后面的命名参数提供默认值。f2(5) # 打印: 1 5 3f2(2, c=4) # 打印: 1 2 4# partial 会按下面的规则合并参数。def partial(func, *d_args, **d_kwargs): def wrap(*args, **kwargs): new_args = d_args + args # 合并位置参数,partial 提供的默认值优先。 new_kwargs = d_kwargs.copy() # 合并命名参数,partial 提供的会被覆盖。 new_kwargs.update(kwargs) return func(*new_args, **new_kwargs) return wrap
- Python基础II---函数基础
- 【Python基础】函数
- Python基础07 函数
- python基础(3)-函数
- Python基础07 函数
- Python基础07 函数
- Python socket基础函数
- Python基础07 函数
- 【Python】读书笔记,基础函数
- Python<11>函数基础
- Python基础07 函数
- Python基础07 函数
- Python基础3--函数
- Python基础07 函数
- Python基础07 函数
- Python基础:常用函数
- 【Python.基础】main函数
- 7.Python基础 函数
- 使用Python爬虫查询12306列车信息
- 我的电路初学
- python 的进程、线程以及协程(2)
- bootstrap之popover
- [noip2014]联合权值
- [Python基础]函数
- 大数据之路-MAC中VMwareFusion安装CentOS6.8
- COM学习(三)——COM的跨语言
- JSP基础(十六)——使用JDBC进行批处理
- linux自动安装系统脚本
- webstorm使用日记_代码测试
- 多态/接口/抽象类
- 关于如何用线段树实现查找区间内第一个小于(大于)某一值x的方法
- js文件保存的相关学习