python函数

变量

命令a = 1,解释器会创建整形1和变量a,并把a指向1,
整形1也可以是各种类型的数据.

函数

函数是指将一堆代码通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可.

def 函数名(若有参数):
    代码
    return None\任意对象

作用:减少重复代码,使程序变的可扩展,使程序变得易维护.
函数调用:func(), 函数名加()
内置函数:python解释器自带的, 存储在builtins.py模块文件中:

函数名命名:
命名要有意义
常规是小写
单词间可以使用下划线隔开
避免与内置函数重名

函数执行:

  1. 函数先定义后使用(相当于变量一样先定义后使用);
  2. 函数只有调用才会执行;
  3. 函数定义载入内存过程中, 只检测语法,不执行代码;

按照参数分类:

  1. 有参数的函数: 接受外部传进来的值,进行一系列的运算,最后返回处理结果;
  2. 无参数的函数: 通常只是做一下操作而已;
  3. 空函数: 什么事情都不做就是一个空函数(即便是一个空函数,也会有基本的属性)

函数返回值:

  1. 可返回任意类型的值,例如:return func();
  2. return代表函数的结束, 后面的代码不会执行;
  3. 未指定return, 则默认返回None;

局部和全局变量:

  1. 在函数内定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量;
  2. 全局变量作用域是整个程序,局部变量作用域是定义该变量的函数;
  3. 当全局变量与局部变量同名时,在定义局部变量的函数内,局部变量起作用;在其它地方全局变量起作用。
  4. 声明global后,可以在函数内部修改全局变量;

函数参数

  1. 形参
    定义函数时括号内写的参数,形参在定义阶段是不占内存空间的,在调用的时候接收实参才占用内存空间.

  2. 实参
    可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参.

def f1(a, b):  # a, b为形参
    pass

f1(12, 10)  # 12, 10为实参
默认参数

定义函数def stu_register(name,age,course,country="CN"):时, country就是默认参数,这个参数在调用时不指定,那默认就是CN,指定了的话,就用你指定的值。

  1. 默认参数必须在位置形参后面;
  2. 默认参数的值只在函数定义阶段生效一次,定义之后的修改不会影响它的值;
  3. 默认参数不要设置为可变类型的, 就是一个坑!
位置参数
  1. 按位置定义的形参必须要传值;
  2. 按位置定义的实参按照位置与形参一一对应的传
关键字参数

按照key=value的形式定义的实参,传的时候与位置没有关系.
混用位置实参和 关键字实参不能给同一个形参重复赋值,且位置实参必须在关键字实参的前面.

def foo(x,y):
    pass

foo(1,2) # 与形参一一对应
foo(y=2, x=1) # 关键字参数,指名道姓的传
foo(1, y=2)
可变参数

若你的函数在定义时不确定用户想传入多少个参数,就可以使用可变长参数,参数*args接收位置参数,多个时变为元祖形式,**kwargs接收键值对形式的参数.

def f1(x, y, *args, **kwargs):
    print(x, y)     # 1 2
    print(args)     # (3, 4, 5)
    print(*args)    # 3 4 5
    print(kwargs)   # {'i': 1, 'j': 3}
    print(*kwargs)  # a b c

f1(1, 2, 3, 4, 5, a=6, b=7, c=8)

*会把多出来的值传给args,并且以元组的形式返回;
**会把多出来的值传给kwargs,并且以字典的形式返回;

匿名函数

当我们在传入函数时,有些时候不需要显式地定义函数,直接传入匿名函数更方便.

lambda x: x * x     # lambda 参数:返回值;
等价于
def f(x):
    return x * x

map_obj = map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(map_obj))  # [1, 4, 9, 16, 25, 36, 49, 64, 81]

命名空间namespace

存放名字的地方,若声明变量 x = 1, 值1存放在内存中, 那变量名x 就存放在命名空间里.
名称空间共3种,分别如下:

  1. locals: 是函数内的名称空间,包括局部变量 和 形参;
  2. globals: 全局变量,函数定义所在模块的名字空间;
  3. builtins: 内置模块的名字空间;

不同变量的作用域不同就是由这个变量所在的命名空间决定的.
全局范围:全局存活,全局有效;
局部范围:临时存活,局部有效;

查看作用域方法 globals(),locals()

level = 'L0'
n = 22

def func():
    level = 'L1'
    n = 33
    print(locals())  # {'n': 33, 'level': 'L1'}
    
    def outer():
        n = 44
        level = 'L2'
        print(locals(),n)  # {'level': 'L2', 'n': 44} 44

        def inner():
            level = 'L3'
            print(locals(),n)  # {'level': 'L3', 'n': 44} 44
        inner()
    print(locals()) # {'n': 33, 'level': 'L1', 'outer': <function func.<locals>.outer at 0x00000000021EF730>}
    outer()

func()
LEGB

代表名字查找顺序: locals -> enclosing function闭包 -> globals -> __builtins__.

locals: 是函数内的名字空间,包括局部变量和形参
enclosing: 外部嵌套函数的名字空间
globals:全局变量,函数定义所在模块的名字空间
builtins: 内置模块的名字空间

global声明:强制转换成全局变量,例如:

x=1
def foo():
    global  x  #强制转换x为全局变量
    x=10000000000
    
foo()
print(x)  # 10000000000
# 这个方法尽量能少用就少用

Nonlocal声明:让内部函数中的变量在上一层函数中生效,外部必须有,但是不会作用于全局,例如:

x=1
def f1():
    x=2
    def f2():
        # x=3
        def f3():
            nonlocal x
            x=10000000000

        f3()
        print('f2内的打印',x)

    f2()

    print('f1内的打印', x)

f1()
print(x)
# 修改x=3 为x=100000000,当x=3不存在时,修改x=2为100000000,# 必须保证x在函数内部存在

闭包(closure)

判断闭包函数: 函数内部print(函数名.__closure__)结果有cell元素就是是闭包函数.

def outer():
    name = 'kate'
    def inner():
        print("在inner里打印外层函数的变量", name)
        
    print(inner.__closure__)
    return inner

f = outer()  # f = inner
f()  # 在inner里打印外层函数的变量 kate

闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域;

闭包形成的必要条件:

  1. 闭包函数必须有内嵌函数;
  2. 闭包函数必须返回内嵌函数;
  3. 内嵌函数需要引用该嵌套函数上一级namespace中的变量;

装饰器decorator

作用:在不修改原函数 及 其调用方式的情况下对原函数功能进行扩展.
本质:就是一个闭包函数,函数名作为参数传入上级函数的命名空间,供内部函数返回掉调用.

无参数装饰器
import time
def wrapper(func):
    print('wrapper')

    def inner():
        print('inner')
        start=time.time()
        func()
        end=time.time()
        print(end-start)
    return inner
    
@wrapper
def f():
    print('f')
    time.sleep(1.2)
f()


# 调用前执行:(f可以理解为中间函数)
1. login = outer(login), 先执行等号右侧
2. login = inner
# 调用后执行:
3. login() >> inner()
4. 返回return login()

带参数的装饰器
import time

def timer(func):
    def inner(*args,**kwargs):
        print('inner')
        start = time.time()
        func(*args, **kwargs)
        end=time.time()
        print(end- start)
        
    return inner

@timer
def f1(a,b):
    print('in f1')
    print(a,b)
    
f1(1, 2)

# 定义期间
1. f1 = timer(f1)
2. f1 = inner

# 开始调用
3. f1(1, 2) = inner(*args)  # *args接收参数(1, 2)
4. func(*args) = f1(1, 2)

带返回值的装饰器
import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        end=time.time()
        print(end - start)
        return re
    return inner

@timer
def f1(a):
    print('in f1 and get a:%s'%(a))
    return 'fun2 over'

print(f1('EFG'))

# f1= timer(f1) = inner
# f1('efg') = inner('efg')
多个装饰器装饰一个函数
def f1(fun):
    def inner():
        print('in f1: before')
        ret = fun()
        print('in f1: after')
    return inner

def f2(fun):
    def inner():
        print('in f2: before')
        fun()
        print('in f2: after')
    return inner

@f1
@f2
def f3():
    print('饿了吗')

f3()

# 分析=================
@f1和@f2的执行顺序:
1. 先执行f1里面的 print('in f2: before');
2. 然后跳到了f2里面的 print('in f2: before'),执行函数f3;
3. f2内部接着执行 print('in f2: after');
4. 回到了f1里面的 print('in f2: after')。
装饰器练习
  1. 编写3个函数,每个函数执行的时间是不一样的.提示:可以使用time.sleep(2),让程序sleep 2s或更多;
  2. 编写装饰器,为每个函数加上统计运行时间的功能. 提示:在函数开始执行时加上start=time.time()就可纪录当前执行的时间戳,函数执行结束后在time.time() - start就可以拿到执行所用时间;
  3. 编写装饰器,为函数加上认证的功能,即要求认证成功后才能执行函数;
  4. 编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    注意: 以下作业必须独立完成才算掌握装饰器!
    参考答案:
# 功能2
from random import randrange
import time

def counter(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        ret = "函数<%s>运行时间<%s>" % (f.__name__,end_time-start_time)
        print(ret)
    return inner

@counter
def f1():
    time.sleep(randrange(1, 4))

@counter
def f2():
    time.sleep(randrange(1, 4))

@counter
def f3():
    time.sleep(randrange(1, 4))

f1()
f2()
f3()

# 功能3和4
LOGIN_STATUS = False
def login(f):
    def inner():
        global LOGIN_STATUS
        if LOGIN_STATUS:
            f()
            return
        username = input('username>> ')
        password = input('password>> ')
        if username == 'alex' and password == 'abc123':
            LOGIN_STATUS = True
            f()
            return
        else:
            print('函数<%s>执行失败, 用户名或密码有误!'% f.__name__)
            return
    return inner

@login
def f1():
    time.sleep(randrange(1, 4))
    print('执行f1完成')

@login
def f2():
    time.sleep(randrange(1, 4))
    print('执行f2完成')

@login
def f3():
    time.sleep(randrange(1, 4))
    print('执行f3完成')

f1()
f2()
f3()

生成器generator

  1. 生成器不会把结果保存在一个系列中,保存的是算法,每次调用next(g)就计算出 g 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误;

  2. 在处理大的序列或文件时使用生成器非常解决内存空间,因为不必像列表一样需要将所有的数据读进内存;

  3. 调用next()方法取值太low, 一般最常用的方法是使用for循环迭代出值, 而且不用考虑异常;

  4. 生成器是一次性的,迭代完成会出现StopIteration错误;

  5. range()与生成器的关系: range()在底层就是用生成器实现的,在Python2里, range(10)生产的就是列表,所以要用xrange(10),比较节约空间, python3优化了range(),结果是类生成器, range(121212121212212)在python shell下可以马上生成,但是list(range(121212121212121212))可能会很慢甚至直接出现内存错误,因为列表需要将所有的值都读进内存,但是内存分配给列表的标准内存是有限的空间,所以会出现内存错误,此时生成器的有点就显现出来了

  6. 可以将列表等可迭代对象转换成生成器

    l = [1, 2, 3]
    print(type(l))      # <class 'list'>
    
    l1 = iter(l)
    print(type(l1))     # <class 'list_iterator'>
    
生成器表达式

通列表解析语法: g = (i for i in range(1000), g就是生成器, next(g)或者for循环就可以迭代出内部的值,直到清空整个生成器;

g = (i for i in range(1, 3))
print(type(g))  # <class 'generator'>

print(g.__next__())  # 1
print(g.__next__())  # 2

try:
    print(g.__next__())
except StopIteration as e:
    print('StopIteration错误') 
# StopIteration错误
生成器函数

常规函数定义,但是它是使用yield语句返回值而不是return语句返回

# fabonacci生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

f = fib(10)  # 必须先生成生成器!
print(type(f))  #  <class 'generator'>
for i in f:
    print(i)
# 1, 1, 2, 3, 5, 8, 13, 21, 34, 55


# 生成器实现range( )函数
def range2(end):
    count = 0
    while count < end:
        n = yield count
        count += 1

g = range2(10)
print(next(g))
print(next(g))
print(next(g))
g.send('stop')

yield与协程

生成器调用send方法传输的参数可以被生成器函数内部的yield所接收.

  1. 通过列表解析或者定义生成器函数 制造生成器g;
  2. 第一次执行可以先调用g.next(), 使得g准备接受数据, num = yield;
  3. 当下一次执行g.send(i),那么生成器内部就可以接收参数num = i.
import time
def consumer(name):
    print("%s 准备消费!" %name)
    while True:
       product = yield
       print("[%s]来了,被[%s]吃了!" %(product,name))


def produce():
    """1. 生成 生成器; 2. 执行生成器,挂在yield处; 3. 生成器send值给yield接收, 逐个生产"""
    a = consumer('A')
    b = consumer('B')
    a.__next__()
    b.__next__()
    print("开始生产!")
    for i in range(1, 3):
        time.sleep(1)
        print("做了2个包子!")
        a.send(i)
        b.send(i)

produce()

迭代器Iterator

生成器都是迭代器;
迭代器不仅包含生成器, 还包含list, tuple, set, dict, str等数据集合;

可迭代对象Iterable:

  1. 数据集合类型: 凡是可以用for循环的对象都是可迭代对象, 常见的数据集合list, tuple, set, dict, str都是可迭代对象:

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    
  2. generator: 包括生成器和带yieldgenerator function

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

推荐阅读更多精彩内容

  • 〇、前言 本文共108张图,流量党请慎重! 历时1个半月,我把自己学习Python基础知识的框架详细梳理了一遍。 ...
    Raxxie阅读 18,910评论 17 410
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,727评论 2 9
  • 不要轻易相信别人所说,因为骗人是很容易就做得到的 不要轻易相信自己亲身的见闻,因为想要骗你的人完全有能力让你被骗 ...
    方形拘阅读 117评论 0 0
  • 女生宿舍604阅读 218评论 0 1
  • 八月间,乘火车由安徽返深圳,天色尚早,旅客大有谈兴。闻自谓江苏客者言:“我旧如尔等,亦不信鬼,尝经一事而笃信之有也...
    费蜡斋阅读 159评论 0 0