【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
posted @ 2018-07-13 11:01  爱寂寞撒的谎言  阅读(186)  评论(0)    收藏  举报