【Python3_基础系列_013】Python3-函数
一、函数
python中函数的定义很简单.函数的目的就是为了代码的复用。
def function_name(params): 函数体 return expression(表达式)/value #return 是可选的,无return的时候返回类型为None。
def:关键字表示定义一个函数
function_name: 函数名,和变量名的命名要求相同,以字母和_开头,可以包含字母、数字和_
params:表示参数,可以是零个,一个 或者多个参数,函数参数不用指定参数类型,因为python中的变量都是弱类型参数,python会根据传入的值自动来维护
return:语句是可选的,可以出现在函数中的任何地方,没有的时候会返回一个None值 ,return后面没有接表达式的话也会返回一个None 值,并且返回值可以是多个
二、函数的参数
| 必须参数 | 参数定义了但是没有默认值 |
| 默认参数 | 参数有默认值,调用函数是可以传参也可以不传参,不传参时,参数是默认值 |
| 关键字参数 | 传参时传入参数名和参数值,可以改变传参的顺序 |
| 不定长参数 | *args 代表元组;**kwargs代表字典 |
1-必需参数
必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
调用printme()函数,你必须传入一个参数,不然会出现语法错误:
#!/usr/bin/python3 #可写函数说明 def printme( str ): "打印任何传入的字符串" print (str) return #调用printme函数 printme() 以上实例输出结果: Traceback (most recent call last): File "test.py", line 10, in <module> printme() TypeError: printme() missing 1 required positional argument: 'str'
2-关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
以下实例在函数 printme() 调用时使用参数名:
#!/usr/bin/python3 #可写函数说明 def printme( str ): "打印任何传入的字符串" print (str) return #调用printme函数 printme( str = "菜鸟教程") 以上实例输出结果:菜鸟教程
以下实例中演示了函数参数的使用不需要使用指定顺序:
实例(Python 3.0+) #!/usr/bin/python3 #可写函数说明 def printinfo( name, age ): "打印任何传入的字符串" print ("名字: ", name) print ("年龄: ", age) return #调用printinfo函数 printinfo( age=50, name="runoob" ) 以上实例输出结果: 名字: runoob 年龄: 50
3-默认参数
调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入 age 参数,则使用默认值:
实例(Python 3.0+) #!/usr/bin/python3 #可写函数说明 def printinfo( name, age = 35 ): "打印任何传入的字符串" print ("名字: ", name) print ("年龄: ", age) return #调用printinfo函数 printinfo( age=50, name="runoob" ) print ("------------------------") printinfo( name="runoob" ) 以上实例输出结果: 名字: runoob 年龄: 50 ------------------------ 名字: runoob 年龄: 35
4-不定长参数
你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述 2 种参数不同,声明时不会命名。基本语法如下:
def functionname([formal_args,] *var_args_tuple ):
"函数_文档字符串"
function_suite
return [expression]
加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
实例(Python 3.0+) #!/usr/bin/python3 # 可写函数说明 def printinfo( arg1, *vartuple ): "打印任何传入的参数" print ("输出: ") print (arg1) print (vartuple) # 调用printinfo 函数 printinfo( 70, 60, 50 ) 以上实例输出结果: 输出: 70 (60, 50)
如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。如下实例:
实例(Python 3.0+) #!/usr/bin/python3 # 可写函数说明 def printinfo( arg1, *vartuple ): "打印任何传入的参数" print ("输出: ") print (arg1) for var in vartuple: print (var) return # 调用printinfo 函数 printinfo( 10 ) printinfo( 70, 60, 50 ) 以上实例输出结果: 输出: 10 输出: 70 60 50
还有一种就是参数带两个星号 **基本语法如下:
def functionname([formal_args,] **var_args_dict ):
"函数_文档字符串"
function_suite
return [expression]
加了两个星号 ** 的参数会以字典的形式导入。
实例(Python 3.0+) #!/usr/bin/python3 # 可写函数说明 def printinfo( arg1, **vardict ): "打印任何传入的参数" print ("输出: ") print (arg1) print (vardict) # 调用printinfo 函数 printinfo(1, a=2,b=3) 以上实例输出结果: 输出: 1 {'a': 2, 'b': 3}
声明函数时,参数中星号 * 可以单独出现,例如:
def f(a,b,*,c): return a+b+c 如果单独出现星号 * 后的参数必须用关键字传入。 >>> def f(a,b,*,c): ... return a+b+c ... >>> f(1,2,3) # 报错 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f() takes 2 positional arguments but 3 were given >>> f(1,2,c=3) # 正常 6 >>>
三、函数变量的作用域
变量的作用域可以用以下几句话概括:
1-#函数外面不能访问函数里面的变量 2-#函数里面能够访问函数外面的变量 3-#函数里面不能修改函数外面的变量 4-#函数里面的变量和外面的变量即使重名也不是同一个对象 5-#如果在函数里面要修改外面的变量,需要用global修饰 6-#nonlocal b #python3才有 在嵌套函数里面可以修改外层的变量
下面通过例子解释这几句话:
1-#函数外面不能访问函数里面的变量
这个很好理解不做解释,函数内部的变量不能被外面访问,是函数封装的基本特性
2-#函数里面能够访问函数外面的变量
a = 12 def fun2(): #函数里面能够访问函数外面的变量 print(a) return a + 1 fun2() 输出:12
函数内部调用了变量a=12的值
3-函数里面不能修改函数外面的变量
a = 12 def fun3(): #函数里面不能修改函数外面的变量 a = a + 1 return a
解释:这个函数在调用的时候会提示异常,因为无法修改a的值
4-函数里面的变量和外面的变量即使重名也不是同一个对象
a = 257 print(id(a)) def fun4(): #函数里面的变量和外面的变量即使重名也不是同一个对象 a = 257 print(a,id(a)) a = a + 1 print(a,id(a)) return a fun4() 输出: 4996512 257 4996464 258 4996528
可以看出在函数外部定义了a=257,内部也定义a=257,并且内部重新定义a=a+1.这三个a的id不一样,代表不同的对象。
这里有一个引申的问题就是python中的常量池:
在python里,有一个神奇的机制:常量池
Python 内部做了一些优化,Python把常用的整数对象都预先缓存起来
特点:
1.整数范围: -5 -- 257
2.它永远不会被GC机制回收, 只要定义的整数变量在 范围: -5 -- 256内,会被全局解释器重复使用, 257除外
3.只要在这个 -5 -- 256 范围内,创建同一区域代码块的变量的值如果是相等的,那么不会创建新对象(python万物皆对象,数值也是对象),直接引用。
a = 256 print(id(a)) def fun4(): #函数里面的变量和外面的变量即使重名也不是同一个对象 a = 256 print(a,id(a)) a = a + 1 print(a,id(a)) return a fun4() 输出: 495281888 256 495281888 257 1850784
可以看出当a=256的时候,即使整型a在函数内外部实际是同一个对象。
但是整型常量池中的257是一个特例:
def test_a(): a = 257 b = 257 print(id(a)) # 1872078405424 print(id(b)) # 1872078405424 num1 = 257 num2 = 257 print(id(num1)) # 1872077827792 print(id(num2)) # 1872077827792 test_a() 257 这个整数对象是区分作用域的,它只有在相同的作用域, 内存地址才会相同
5-如果在函数里面要修改外面的变量,需要用global修饰
a = 12 def fun5(): global a #如果在函数里面要修改外面的变量,需要用global修饰 a = a + 1 print(a) fun5() 输出:13
函数内部修改了外部变量的值
6-nonlocal b #python3才有 在嵌套函数里面可以修改外层的变量
a=10 def fun7(): print(a) print('fun7()在被调用') b = 10 def fun8(): nonlocal b #python3才有 在嵌套函数里面可以修改外层的变量 b = b + 1 print(b) print('fun8()在被调用') return fun8() fun7() 输出: 10 fun7()在被调用 11 fun8()在被调用
可以看出fun8嵌套函数中修改了外层函数fun7的变量b
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:
L (Local) 局部作用域
E (Enclosing) 闭包函数外的函数中
G (Global) 全局作用域
B (Built-in) 内建作用域
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。
x = int(2.9) # 内建作用域
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 闭包函数外的函数中
def inner():
i_count = 2 # 局部作用域
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,如下代码:
>>> if True:
... msg = 'I am from Runoob'
...
>>> msg
'I am from Runoob'
>>>
实例中 msg 变量定义在 if 语句块中,但外部还是可以访问的。
如果将 msg 定义在函数中,则它就是局部变量,外部不能访问:
>>> def test():
... msg_inner = 'I am from Runoob'
...
>>> msg_inner
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'msg_inner' is not defined
>>>
从报错的信息上看,说明了 msg_inner 未定义,无法使用,因为它是局部变量,只有在函数内可以使用。
四、面试题
关于函数的面试题应该是笔试中最喜欢的问题,例如:排序,递归算法都是函数实现的。
1.编写word_count(file)函数,实现对指定文件的word实现count功能。
def word_count(filename): """ :param filename: 需要统计的文件名 :return: 返回一个字典 """ file = open(filename) lines = file.readlines() file.close() result = {} for line in lines: line = line.strip().replace('.', '').replace(',', '').replace('*', '').replace('-', '').replace('!', '') words = line.split(' ') for word in words: data = word.lower() if data not in result: result.setdefault(data, 1) else: result[data] += 1 return result print(word_count("word.txt")) 输出: {'the': 6, 'zen': 1, 'of': 3, 'python': 1, 'by': 1, 'tim': 1, 'peters': 1, '': 2, 'beautiful': 1, 'is': 10, 'better': 8, 'than': 8, 'ugly': 1, 'explicit': 1, 'implicit': 1, 'simple': 1, 'complex': 2, 'complicated': 1, 'flat': 1, 'nested': 1, 'sparse': 1, 'dense': 1, 'readability': 1, 'counts': 1, 'special': 2, 'cases': 1, "aren't": 1, 'enough': 1, 'to': 5, 'break': 1, 'rules': 1, 'although': 3, 'practicality': 1, 'beats': 1, 'purity': 1, 'errors': 1, 'should': 2, 'never': 3, 'pass': 1, 'silently': 1, 'unless': 2, 'explicitly': 1, 'silenced': 1, 'in': 1, 'face': 1, 'ambiguity': 1, 'refuse': 1, 'temptation': 1, 'guess': 1, 'there': 1, 'be': 3, 'one': 3, 'and': 1, 'preferably': 1, 'only': 1, 'obvious': 2, 'way': 2, 'do': 2, 'it': 2, 'that': 1, 'may': 2, 'not': 1, 'at': 1, 'first': 1, "you're": 1, 'dutch': 1, 'now': 2, 'often': 1, 'right': 1, 'if': 2, 'implementation': 2, 'hard': 1, 'explain': 2, "it's": 1, 'a': 2, 'bad': 1, 'idea': 3, 'easy': 1, 'good': 1, 'namespaces': 1, 'are': 1, 'honking': 1, 'great': 1, "let's": 1, 'more': 1, 'those': 1}
2.冒泡排序
def bubbo(a): len1 = len(a) for i in range(len1): for j in range(i+1,len1): if a[j]<=a[i]: a[j],a[i]=a[i],a[j] print(a) bubbo([1,2,3,4,5,6,2,1,2,4,5,67,8]) 输出: [1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 6, 8, 67]
3.递归阶乘 6!
def digui(n): if n<=0: return 1 else: return n*digui(n-1) print(digui(5)) 输出:120
4.斐波那契数列,兔子数列,青蛙跳台,小矩形覆盖大矩形
题目1:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。 题目2:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 题目3:一只青蛙一次可以跳上1级台阶,也可以跳上2级。。。。。它也可以跳上n级,此时该青蛙跳上一个n级的台阶总共有多少种跳法? 题目4:小矩形覆盖大矩形,用2*1的小矩形横着或竖着去覆盖各大矩形。 题目5:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
这几个问题都可以通过归纳的方法求出f(n)的公式,然后通过递归去实现。详情可以参考这篇文章:https://blog.csdn.net/u010177286/article/details/47129019
下面给出一般代码:
def fibbnaci(n): if n<=1: return 1 elif n==2: return 1 else: return fibbnaci(n-1)+fibbnaci(n-2) for i in range(5): print(fibbnaci(i+1),end="\t")
输出:
1 1 2 3 5

浙公网安备 33010602011771号