函数

函数

引子

求1-15的和;

求23-36的和;

求55-68的和;

def sum_of_num(x, y):
    sum1 = 0
    for i in range(x, y + 1):
        sum1 += i
        print(i, end=' ')
    return sum1


print(sum_of_num(1, 15))
print(sum_of_num(23, 36))
print(sum_of_num(55, 68))
#1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 120
#23 24 25 26 27 28 29 30 31 32 33 34 35 36 413
#55 56 57 58 59 60 61 62 63 64 65 66 67 68 861

程序而言:函数就是对程序逻辑进行结构化或者过程化的一种编程方法。

built-in function 内置函数—>BIF

函数的定义

声明函数的一般形式:

def funtion_name(arg1, arg2, ..., argn):
    '''statements'''
    func_statements
    return Value

说明如下:

1.函数代码块以 def 关键词开头,后接函数标识符名称和小括号 ()。

2.任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。

3.函数的第一行语句可以选择性地使用文档字符串----用于存放函数说明。

4.函数内容以冒号起始,并且缩进。

5.return [表达式] 结束函数,选择性的返回一个值给调用方。不带表达式的return相当于返回 None。

其中参数列表和返回值不是必须的,return后也可以不跟返回值,甚至连 return也没有。

对于return后没有返回值的和没有return语句的函数都会返回None值。

有些函数可能既不需要传递参数,也没有返回值。

没有参数时,包含参数的圆括号也必须写上,圆括号后也必须有“:”。

函数调用

使用函数名加参数的方式调用函数

def add_sum(x, y):
    return x + y
print(add_sum(1, 2))
#3

函数文档说明

函数的第一行语句可以选择性地使用文档字符串,使用__doc__方法可以返回该字符串

def sum_add():
    '''add'''
    pass
print(sum_add.__doc__)
#add

函数参数

  • 不传参函数
  • 参数函数
    • 顺序(关键字参数需在固定参数之后)

函数返回值

return [表达式] 结束函数,选择性的返回一个值给调用方。不带表达式的return相当于返回 None。

函数参数种类

  • 形参

    • 只有在调用时才分配内存单元。调用结束后,立即释放所分配的内存单元。因此,形参只在函数内部有效,函数调用结束返回主调用函数后则不能再使用该形参变量。
  • 实参

    • 实参是一个确定的值,能够传递给形参。
      • 作为位置参数或者关键字参数传递。

位置参数

def usr_manage(name, age, job, hobby):
    print("用户管理系统".center(16, '-'))
    print("\tName:\t", name)
    print("\tAge:\t", age)
    print("\tJob:\t", job)
    print("\tHobbt:\t", hobby)


usr_manage("tom", 20, "IT", "Coding")
usr_manage("Jim", 21, "Student", "Reading")
-----用户管理系统-----
	Name:	 tom
	Age:	 20
	Job:	 IT
	Hobbt:	 Coding
-----用户管理系统-----
	Name:	 Jim
	Age:	 21
	Job:	 Student
	Hobbt:	 Reading

只传递实参,位置一一对应—>位置参数

使用位置参数时和函数头定义的形参在顺序,个数以及类型上匹配。

默认值参数

def usr_manage(name, age, job, hobby="game"):
    print("用户管理系统".center(16, '-'))
    print("\tName:\t", name)
    print("\tAge:\t", age)
    print("\tJob:\t", job)
    print("\tHobbt:\t", hobby)


usr_manage("tom", 20, "IT")
usr_manage("Jim", 21, "Student", "Reading")
-----用户管理系统-----
	Name:	 tom
	Age:	 20
	Job:	 IT
	Hobbt:	 game
-----用户管理系统-----
	Name:	 Jim
	Age:	 21
	Job:	 Student
	Hobbt:	 Reading

关键字参数

def usr_manage(name, age, job, hobby="game"):
    print("用户管理系统".center(16, '-'))
    print("\tName:\t", name)
    print("\tAge:\t", age)
    print("\tJob:\t", job)
    print("\tHobt:\t", hobby)


usr_manage("tom", job="IT", 20)
usr_manage("Jim", 21, "Student", "Reading")

默认值参数,关键字参数需放在位置参数之后。

不定参数

在Python中不定参数主要是指*args**kwargs两个魔法变量。

他们俩主要是用于函数定义,我们可以将不定数量的参数传递给一个函数。

  • *args用来接收任意非键值对的任意数量的参数列表给函数。传递一个元组。

  • **kwargs用来接收任意不定长度的键值对列表给函数。传递一个字典。

def ave(*args):
    sum1 = 0
    for i in args:
        sum1 += i
    return sum1/len(args)
print(ave(1, 5, 11, 21, 23))
print(ave(2, 2, 2, 2, 2, 2))
#12.2
#2.0
def un_para_key(**kwargs):
    print(kwargs)
    print(type(kwargs))
un_para_key(a=1, b=2)
#{'a': 1, 'b': 2}
<class 'dict'>

函数引用

函数被调用前只要被加到到缓存中,就无所谓顺序

def foo():
    print("in foo()")
    bar()
def bar():
    print("in bar()")
foo()

函数属性

函数属性是Python中另外一个使用了句点属性标识并拥有命名空间的领域。

你可以获得每个 pyhon 模块,类,和函数中任意的名字空间。你可以在模块 foo 和 bar 里都有名为 x 的一个变量,但是在将这两个模块导入你的程序后,仍然可以使用这两个变量。所以,即使在两个模块中使用了相同的变量名字,这也是安全的,因为句点属性标识对于两个模块意味了不同的命名空间,比如说,在这段代码中没有名字冲突:print(foo.x + bar.x)

内嵌函数

作用域

bar()整个函数都处于外部foo()函数的作用域里(foo()是我们可以从外部访问的一个对象区域)。除了在foo()内部,其他地方无法对bar()进行调用的。

def foo():
    def bar():
        print("bar() called.")
    print("foo() called.")
foo()
bar()
#foo() called.
#bar() called.
Traceback (most recent call last):
  File "D:/python/test1/day06.py", line 152, in <module>
    bar()
NameError: name 'bar' is not defined

变量作用域

作用域的产生

就作用域而言,python和C有很大差别,只有当变量在module,class,函数中定义的时候才会有作用域的概念。

def foo():
    a = "foo"
    print(a)
foo()
print(a)
foo
Traceback (most recent call last):
  File "D:/python/test1/day06.py", line 157, in <module>
    print(a)
NameError: name 'a' is not defined

在作用域中定义的变量,一般只在作用域内有效。需要注意的是,在if-elif-else,for-else,while-else,try-except(else-finally)等关键字的语句块中不会产生作用域。

if True:
    a = 100
    print(a)
print(a)
#100
#100

作用域的类型

Python中,使用一个变量时并不要求预先声明。但在真正使用的时候,他必须绑定到某个内存对象。(被定义,赋值)。这种变量名的绑定,将在当前作用域引入我们新的变量,同时屏蔽外层作用域中的同名变量。

  • 局部作用域(locale — L)

    局部变量:包含在def关键字定义的语句块中,即在函数中定义的变量。每当函数被调用时都会创建一个新的局部作用域。Python中也有递归,即自己调用自己,每次调用都会创建一个新的局部命名空间。在函数内部的变量声明,除非特别的声明为全局变量,否则均默认为局部变量。有些情况需要在函数内部定义全局变量,这时可以使用global关键字来声明变量的作用域为全局。局部变量域就像一个栈,仅仅是暂时的存在,依赖创建该局部作用域的函数是否处于活动的状态。所以,一般建议尽量少定义全局变量,因为全局变量在模块文件运行的过程中会一直存在,占用内存空间。
    注意:如果需要在函数内部对全局变量赋值,需要在函数内部通过global语句声明该变量为全局变量。

  • 嵌套作用域(enclosing — E)

    • E也包含在def关键字中,E和L是相对的,E相对于跟上层的函数而言也是L。与L的区别在于,对于一个函数而言,L是定义在此函数内部的局部作用域。
    • 主要为了实现Python的闭包,而增加的实现。
  • 全局作用域(global — G)

    即在模块层次中定义的变量。模块顶层声明的变量具有全局作用域。从外部来看,模块的全局变量就是一个模块对象的属性。

  • 内置作用域(bulit-in — B)

    • 系统固定模块中定义的变量。

搜索变量名的优先级:局部作用域>嵌套作用域>全局作用域>内置作用域

全局变量和局部变量

gbl_str = "foo"
def foo():
    loc_str = "bar"
    return gbl_str + loc_str
print(foo())
#foobar
print(gbl_str)
#foo
print(loc_str)  # 仅能访问全局变量,访问局部变量报错
Traceback (most recent call last):
  File "D:/python/test1/day06.py", line 168, in <module>
    print(loc_str)
NameError: name 'loc_str' is not defined
a = 6688
def foo():
    # a = 666
    print('foo,a:\t', a)  # 局部变量与全局变量重名,全局变量被屏蔽,局部变量还未被定义,报错
    a = 888
    print(a)
def bar():
    print('bar,a:\t', a)
foo()
bar()
Traceback (most recent call last):
  File "D:/python/test1/day06.py", line 182, in <module>
    foo()
  File "D:/python/test1/day06.py", line 177, in foo
    print('foo,a:\t', a)
UnboundLocalError: local variable 'a' referenced before assignment

global

可变类型的全局变量

a = 6688
def foo():
    global a  # 声明为全局变量
    print("foo(),a:\t", a)
    a = 666
    print("foo(),a:\t", a)
def bar():
    print("bar(),a:\t", a)
foo()
bar()
#foo(),a:	 6688
#foo(),a:	 666
#bar(),a:	 666
a = 6688
def foo():
    a = 666  # 仍为局部变量
    def inner_foo():
        global a  # 声明inner_foo()中的a为全局变量
        print(a)
        a = 888
        print(a)
    inner_foo()
    print(a)
print(a)
foo()
print(a)
#6688
#6688
#888
#666
#888

递归

函数不断调用自身

def fib(n):
    if n == 1 or n ==2:
        return 1
    else:
        return fib(n-2) + fib(n-1)
print(fib(10))
#55

匿名函数(lambda)

python 使用 lambda 来创建匿名函数。

所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

  • lambda 只是一个表达式,函数体比 def 简单很多。

  • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。

  • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。

  • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

  • 格式:

    lambda para1, para2, para3, ..., paraN : expression using paras
    
def add(x, y, z):
    return x + y + z
f = lambda x, y, z: x + y + z
print(add(1, 2, 3))
print(f(1, 2, 3))
#6
#6

高阶函数

高阶函数:把一个函数名,以实参的形式,传递给这个函数的形参,这个函数就成为高阶函数。

def pow_2(x):
    return x**2
def add(a, b, c):
    return c(a) + c(b)
a_value = add(-9, 1, pow_2)
print(a_value)
#82

filter函数

通过函数实现过滤:

li = ['Zhejiang', 'University', 'City', 'College']
s = lambda sr:sr.startswith('C')
e = lambda sr:sr.endswith('ty')
def filter_test_2(para, n):
    ret = []
    for i in para:
        if not n(i):
            ret.append(i)
    return ret
print(filter_test_2(li, s))
print(filter_test_2(li, e))
#['Zhejiang', 'University']
#['Zhejiang', 'College']

filter:

功能:

  • 过滤掉序列中不符合函数条件的元素。当序列中需要保留的元素可以用某些函数描述时,就应该想到filter函数。

调用格式:

  • filter(function, sequence)
    • function —> 可以是自定义的函数,也可以是匿名函数
    • sequence —> 列表、元组、字符串
li = ['Zhejiang', 'University', 'City', 'College']
fl = filter(lambda sr: sr.endswith('ty'), li)
print(list(fl))
#['University', 'City']

map 映射

功能

  • 求一个序列或者多个序列进行函数映射后的值。(用list()强转)

格式

  • map(function, iterable1, iterable2)
    • function的参数可以不止一个
    • iterable1,iterable2就是传入的参数
li = [1, 2, 3, 4, 5]
li2 = [3, 4, 5, 6, 7, 8]
res = map(lambda x, y: x*y+2, li, li2)
print(list(res))
#[5, 10, 17, 26, 37]

使用其他方法实现类似功能:

def func_test(oj):
    return oj+1
def func_test2(oj):
    return oj-1
def add_1(a, b):
    for i in range(0, len(b)):
        b[i] = a(b[i])
    return b
print(add_1(func_test, [1, 2, 3, 4]))
print(add_1(func_test2, [1, 2, 3, 4]))

reduce 函数

  • 功能:
    • 对一个序列进行压缩运算,得到一个值value。
    • python2中,reduce()是内置函数,而现在,python3中,他被移植到一个模块中,functools。
    • from functools import reduce
  • 格式
    • reduce(function, iterable, [initial])
      • function必须要传入两个参数
      • iterable —> 列表/元组
from functools import reduce
k = [2, 3, 4, 5, 6]
z = reduce(lambda x, y: x+y, k,1)
print(z)
#21

apply 函数

功能

  • pandas中,应用对象是pandas中的DataFrame或者Series
  • 直接对DataFrame或者Series应用函数
  • 对pandas中的groupby之后的聚合对象应用apply
import numpy as np
import pandas as pd

a = np.random.randint(low=0, high=4, size=(2, 4))
data = pd.DataFrame(a)
print(data)
print(data.apply(lambda x: x*10))
#   0  1  2  3
#0  3  3  3  1
#1  1  1  2  3
#    0   1   2   3
#0  30  30  30  10
#1  10  10  20  30

zip

功能

  • 将可迭代对象作为参数,将对象中对应的元素打包成一个个元组,返回由这些元组组成的对象。
  • 长度不一样的时候,以长度短的为准

注:

利用*号操作符,与zip相反,进行解压。

格式:

zip(iterable1, iterable2, …, iterableN)

  • iterable —> 两个或者多个可迭代序列(字符串,列表,元组,字典)
    • py2,由元组组成的列表
    • py3,返回的是一个对象,如果要得到一个列表,需要list()强转,也可以通过迭代访问
a = [1, 2, 3]
b = [4, 5, 6]
c = [4, 5, 6, 7, 8]
ziptest1 = zip(a, b)
print(list(ziptest1))

练习

#写一个函数,计算传入字符中的数字,字母,空格以及其他字符的个数
import string
def count_str(*args):
    digit, alpha, block, other = 0, 0, 0, 0
    for i in args:
        if i in string.ascii_letter:
            alpha += 1
        elif str(i) in string.digits:
            digit += 1
        elif i == ' ':
            block += 1
        else:
            other += 1
    print("字母的数量为:\t", alpha, "\n数字的数量为:\t", digit, "\n空格的数量为:\t", block, "\n其他字符的数量为:\t", other)
count_str(1, '2', '3', 4, 'a', 'b', ' ', '@', "#")
#字母的数量为:	 2 
#数字的数量为:	 4 
#空格的数量为:	 1 
#其他字符的数量为:	 2

#利用filter函数筛选1-1000的回文
#方法一:
def palindrome(num1):
    a = str(num1)
    for i in range(0, len(a)):
        if a[i] != a[-i-1]:
            return False
    return True
#方法二:
def palindrome_2(num1):
    return str(num1) == str(num1)[::-1]
#方法三:
palindrome_3 = lambda x:str(x) == str(x)[::-1]
a = filter(palindrome_3, range(1, 1000))
print(list(a))
#[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999] 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章