Python基础之函数(四)

一、函数定义

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段;比如常用的print(),就是内建函数;通俗来讲,函数就是把代码打包成不通形状的乐高积木,以便可以根据需求调用拼装;当然这种函数叫做自定义函数。
定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可.
特性: 减少重复代码;使程序变的可扩展;使程序变得易维护.

  • 语法
def 函数名(参数列表):
    函数体
  • 事例
def hello():
    print('Hello World')

#上面是函数主体,下面是调用hello这个函数;

hello()
  • 返回值
    要想获取函数的执行结果,就可以用return语句把结果返回
注意:
一旦函数经过调用并开始执行,那函数外部的程序,就无法再控制函数的执行过程,只能等待函数执行结果;所以return语句代表着函数的结束;
如果未在函数中指定return,那这个函数的返回值为None;

def count(numb1, numb2):
        numb3 = numb1 * numb2
        return numb3

print(count(3, 4))
二、函数参数

参数从调用的意义上来讲,分为形式参数实际参数,简称"形参"和"实参";形参指的是函数创建和定义过程中小括号内的参数;实参是指函数被调用的过程中传递进来的参数;

  • 形参:变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量;
  • 实参:可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值;


    形参和实参
  • 普通参数
    普通参数又位置参数,需要按照形参的顺序,进行实参的赋值
def data(name, age):
    '''
    :param name:
    :param age:
    :return:
    '''
    print('姓名:{0}\n年龄:{1}'.format(name, age))

data('lain', 23)
  • 关键字参数
    关键字参数在传入实际参数时指定形参的变量名,不用考虑具体位置
def data(name, age, pro):
    '''
    :param name:
    :param age:
    :param pro:
    :return:
    '''
    print('姓名:' + name + '\n年龄:' + age + '\n职业:' + pro)

data(pro='CA', name='LAIN', age='28')  #不用考虑先后顺序
  • 默认参数
    使用默认参数时,可以不带实参去调用函数;默认参数是在参数定义的过程中,为形参赋值,当函数调用的时候不传递实参,则默认使用形参的赋值参数代替
def data(pro='CA', name='LAIN', age='28'):
    '''
    :param name:
    :param age:
    :param pro:
    :return:
    '''
    print('姓名:' + name + '\n年龄:' + age + '\n职业:' + pro)

data()  #实参不传参则按照形参赋值的参数进行打印;
data(name='LIMING', age='30', pro='DBA')  #实参传参则按照实参传递的参数进行打印;
  • 收集参数
    一个函数能处理比当初声明时更多的参数是收集参数又叫可变参数或者不定长参数;声明时不会命名;
    命名规范:*args **kwargs
  1. 元组形式:加一个星号(*)的变量名会存放所有未命名的变量参数。如果在函数调用时没有指定参数,它就是一个空元组;
def data(name, age, pro, *hobby):
    '''
    :param name:
    :param age:
    :param pro:
    :return:
    '''
    print('姓名:' + name + '\n年龄:' + age + '\n职业:' + pro)
    print('兴趣爱好:', *hobby)

data('LIMING', '30', 'DBA', '足球', '跑步', '爬山')
#上面的事例可以理解为打包,既然可以打包那么肯定可以解包
def data(name, age, pro, *hobby):
    '''
    :param name:
    :param age:
    :param pro:
    :return:
    '''
    print('姓名:' + name + '\n年龄:' + age + '\n职业:' + pro)
    print('兴趣爱好:', *hobby)

a = ['足球', '跑步', '爬山']
data('LIMING', '30', 'DBA', *a)
  1. 字典形式:加两个星号(**)的变量名会存放所有未命名的变量参数。
def data(name, age, pro, *hobby, **mes):
    '''
    :param name:
    :param age:
    :param pro:
    :return:
    '''
    print('姓名:' + name + '\n年龄:' + age + '\n职业:' + pro)
    print('兴趣爱好:', *hobby)
    print('其他信息:', mes)

data('LIMING', '30', 'DBA', '足球', '跑步', '爬山', phone='13452123453', studID='20183511')

如果在函数调用时没有指定参数,它就是一个空字典;

def data(name, age, pro, *hobby, **mes):
    '''
    :param name:
    :param age:
    :param pro:
    :return:
    '''
    print('姓名:' + name + '\n年龄:' + age + '\n职业:' + pro)
    print('兴趣爱好:', *hobby)
    print('其他信息:', mes)

data('LIMING', '30', 'DBA', '足球', '跑步', '爬山')
三、函数的变量作用域

python中的作用域:局部作用域(L-Local)、闭包函数外的函数作用域(E-Enclosing)、全局作用域(G-Global)、内建函数作用域(B-Built-in);

  • 变量作用域由内到外查找:L —>E—>G—>B
  1. 函数外声明的是全局变量,函数内声明的是局部变量;
  2. 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序;
  3. 当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。
  4. 正常情况下,局部环境变量无法修改全局变量;
a = 0
def number(arg1, arg2):
    '''
    :param arg1: 
    :param arg2: 
    :return: 
    '''
    a = arg1 + arg2
    print('函数内a的值: ',a)
    print('函数内a的内存地址: ',id(a))

number(1, 2)
print('函数外a的值: ', a)
print('函数外a的内存地址: ', id(a))

执行结果:
函数内a的值:  3
函数内a的内存地址:  492495664
函数外a的值:  0
函数外a的内存地址:  492495616
  • 通过global修改全局变量
a = 0
def number(arg1, arg2):
    '''
    :param arg1:
    :param arg2:
    :return:
    '''
    global a
    a = arg1 + arg2
    print('函数内a的值: ',a)
    print('函数内a的内存地址: ',id(a))

number(1, 2)
print('函数外a的值: ', a)
print('函数外a的内存地址: ', id(a))

执行结果:
函数内a的值:  3
函数内a的内存地址:  492495664
函数外a的值:  3
函数外a的内存地址:  492495664
  • 通过nonlocal修改闭包函数外的函数变量
a = 0
def number():
    '''
    :param arg1:
    :param arg2:
    :return:
    '''
    a = 1
    print('number()函数内a的内存地址: ', id(a))

    def soure():
        nonlocal a  #修改的是外层函数a=1的值
        a = 2
        print('soure()函数内a的内存地址: ', id(a))
    soure()
    print('soure()函数外a的内存地址: ', id(a))

number()
print('函数外a的内存地址: ', id(a))
  • lambda表达式
    lambda表达式语法:冒号左边放原函数的参数,可以有多个参数,用逗号隔开即可,冒号右边是返回值。
def calc(x,y):
    runturn x*y
print(calc(3,4))

用lambda表达式如下

calc = lambda x,y:x*y
print(calc(3,4))
四、递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
递归特性:

  1. 自己调用自己;
  2. 必须有一个明确的结束条件;
  3. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少;
  4. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出);
data = range(0,1000000)

def  digui(data,find):
    if len(data) > 0:
        middle_pos = int(len(data)/2)
        if data[middle_pos] == find:
            print('find_name:',find)
        elif data[middle_pos] < find:
            print('\033[31;1m 右边的值:%s\033[0m',data[middle_pos+1:])
            digui(data[middle_pos+1:],find)
        else:
            print('\033[32;1m 左边的值:%s\033[0m',data[0:middle_pos])
            digui(data[0:middle_pos],find)
    else:
        print('没有找到!')

digui(data,451232)
def sum_number(n):
    if n <= 0:
        return 0
    return n+sum_number(n-1)

print(sum_number(100))
五、推导式
  • 列表推导式
list = [i*i for i in range(10)]  #列表推导式,用中括号表示
print(list)

list1 = []
for i in range(10):
    list1.append(i*i)
print(list1)

其他事例:
list = [i*i for i in range(10) if i%2 == 0]  
print(list)

list = [x*y for x in range(1, 10) for y in range(1, 10) if x >= y]
print(list)
  • 字典推导式
dic = {i:i*i for i in range(5)}  #大括号内冒号左右分别是key、value组合
print(dic)
  • 集合推导式
s = {i for i in 'hello world' if i not in 'w'}   #集合与字典推导式的区别是key、value组合
print(s)
  • 元组推导式
tup = tuple(i for i in range(5))  #tuple
print(tup)
****
result = [lambda x:x + i for i in range(5)]
print(result[0](10))    #调用函数时循环结束i的值为4,所以结果为10+4=14
14

result = [lambda x,y=i:x + y for i in range(5)]
print(result[0](10))    #每次循环将i赋值给y,结果为0+10=10
print(result[1](10))    #每次循环将i赋值给y,结果为1+10=11
六、迭代器

迭代:通过for循环遍历对象每个元素的过程
我们已经知道,可以直接作用于for循环的数据类型有以下几种:

  • 一类是集合数据类型,如list、tuple、dict、set、str等;
  • 一类是generator,包括生成器和带yield的generator function;
    以上这些统称为可迭代对象(Iterable)
可以通过python内置的方法Iterable来测试数据类型是否为可迭代对象
from collections import Iterable

print(isinstance('Hello', Iterable))    #字符串是可迭代对象
print(isinstance([1, 2, 3], Iterable))  #列表是可迭代对象
print(isinstance({'a':2, 'b':3}, Iterable)) #字典是可迭代对象
print(isinstance((1, 2, 3), Iterable))  #元组是可迭代对象
print(isinstance({1, 2, 3}, Iterable))  #集合是可迭代对象
print(isinstance(1, Iterable))  #整数是不可迭代对象

运行结果:
True
True
True
True
True
False

可以迭代并且可以被next()函数和iter()调用,并不断返回下一个值的对象就称为迭代器(Iterator);迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往后遍历不能回溯,不像列表,你随时可以取后面的数据,也可以返回头取前面的数据。

通过Iterator可以测试上面的可迭代对象是否为迭代器
from collections import Iterator

print(isinstance('Hello', Iterator))    #字符串不是迭代器
print(isinstance([1, 2, 3], Iterator))  #列表不是迭代器
print(isinstance({'a':2, 'b':3}, Iterator)) #字典不是迭代器
print(isinstance((1, 2, 3), Iterator))  #元组不是迭代器
print(isinstance({1, 2, 3}, Iterator))  #集合不是迭代器
print(isinstance(1, Iterator))  #整数是不是迭代器

运行结果:
False
False
False
False
False
False
通过iter()函数创建迭代器
numb = [1, 2, 3, 4, 5]  #创建的是列表,通过iter函数创建就是列表迭代器,如果是集合、字典、字符串那就是集合迭代器、字典迭代器、字符串迭代器;
it = iter(numb)
print(type(it))

运行结果:
<class 'list_iterator'>  #列表迭代器
通过next()函数取值
numb = 'hello'
it = iter(numb)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))

运行结果:  #从第一个元素开始,到遍历完成结束;
h
e
l
l
o
  • 为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
七、生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。生成器的作用是一边获取一边生成,用多少生成多少;

#推导式生成器
g = (i* i for i in range(4))
print(type(g))

运行结果:
<class 'generator'>  #类型为生成器
#通过yield,函数返回值会变成生成器
- 斐波那契数列
def fibonacci(n):
    a, b, counter = 0, 1, 0
    while True:
        if counter > n:
            return
        yield a
        a, b = b, a+b
        counter = counter + 1
fib = fibonacci(10)
# print(type(fib))

for i in fib:
    print(i, end=',')

运行结果:
<class 'generator'>  #print数据类型为生成器
0,1,1,2,3,5,8,13,21,34,55,  #直接遍历取出
八、装饰器

装饰器定义:装饰器是函数,只不过该函数可以具有特殊的含义,装饰器用来装饰函数或类,使用装饰器可以在函数执行前和执行后添加相应操作完全符合程序开发中,开放-封闭原则;不改变原有代码功能,不改变原有调用方式实现新功能的扩张。

# -*- coding: UTF-8 -*-
# Author: LAIN
# Time: 2018-08-15

#装饰器
user_status = False
def login(fund):
    def inner():
        user = 'LAIN'
        pwd = '12345'
        global user_status

        if user_status == False:
            username = input('USER:')
            passwd = input('PASSWORD:')

            if username == user and passwd == pwd:
                print('登录成功')
                user_status = True
            else:
                print('账号密码错误!')

        if user_status == True:
            fund()
    return inner

def home():
    print('-----商城首页-----')

@login    #语法糖,表明这是个装饰器
def numerical():
    print('-----电子数码-----')

@login
def food():
    print('-----食品生鲜-----')

@login
def department():
    print('-----百货商品-----')

home()
numerical() #运行login函数后调用
food()  #运行login函数后调用
department()    #运行login函数后调用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 〇、前言 本文共108张图,流量党请慎重! 历时1个半月,我把自己学习Python基础知识的框架详细梳理了一遍。 ...
    Raxxie阅读 18,934评论 17 410
  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,598评论 1 19
  • 函数只定义一次,但可能被执行或调用任意次。JS函数是参数化的,函数的定义会包括一个称为形参的标识符列表,这些参数在...
    PySong阅读 311评论 0 0
  • 每一年,我们都在长大。 正在给儿子做晚饭的我,接到小静的电话,“恁家老板又...
    恪臻阅读 441评论 0 0
  • 血浓于水从古至今都是亘古不变的,然而在古代的皇权之争中,兄弟隙墙、反目成仇的悲剧早已见怪不怪;手足相残、勾心斗...
    黎明的晨露阅读 470评论 0 1