Python 学习笔记04(函数)

Author Avatar
EmptinessBoy 7月 06, 2020
  • 在其它设备中阅读本文章

函数是重用的程序段。它们允许你给一段语句命名一个名字。函数的使用必须遵循先定义后调用的原则

定义函数的语法结构:

def 函数名(参数1,参数2,参数3...):

  """函数注释"""
  # 函数体代码(函数的功能)

  return 返回值

1.def 定义函数的关键字

2.函数名 函数名指向函数内存地址 是对函数体代码的引用

3.括号 括号内定义参数 参数可有可无 且无需指定参数类型

4.冒号 括号后必须加冒号 然后在下一行开始需要缩进书写函数体代码

5.函数注释 描述函数的功能 参数介绍 非必要建议加上 增加代码的可读性

6.函数体代码 整个函数功能的核心代码

7.return 函数的返回值 可有可无

定义

# 1.无参函数
def index():
  print(123)

# 2.有参函数
def index(x,y):
  print(123)

# 3.空函数
def index():
    pass

lambda 表达式

函数定义的另一种方法是用 lambda 表达式,它定义了一个 匿名函数。lambda 的一般形式是关键字 lambda 后面跟一个或多个参数,紧跟一个冒号,后面是一个表达式。作为表达式,lambda 返回一个值。lambda 用来编写简单的函数,而def 用来处理更强大任务的函数。

#把lambda定义的匿名函数赋给函数g
g = lambda x,y,z : x+y+z
print(g(1,4,5))

>>> 10

函数的调用与返回值

  1. 函数没有return关键字的时候 默认返回的都是None
  2. return 后面什么都不写 返回的还是None
  3. return 后面写内容 写什么就返回什么

  4. return 返回多个元素 会自动组成元组的形式返回
  5. 函数体代码一旦遇到 return 会立刻结束
  6. return 可以返回多个任意类型的元素
def index():
    a = 123
    b = "456"
    c = [7,8,9]
    d = (10,11,12)
    return a,b,c,d
print(index())

>>> (123, '456', [7, 8, 9], (10, 11, 11))
  1. return 返回值的解压赋值
aa,bb,cc,dd = index()
print(aa,bb,cc,dd)

>>> 123 456 [7, 8, 9] (10, 11, 12)

函数的参数

  • 参数在函数定义的圆括号内指定,用逗号分割
  • 函数定义时的参数称为形参,这些参数就像变量一样
  • 当我们调用函数的时候,函数中的参数值称为实参
  • 函数形参取得的值是你调用函数时提供的实参

位置参数

位置参数是按照顺序定义的参数。

定义阶段按照从左到右的顺序依次定义形参,叫位置形参,但凡是按照这种形式定义的形参都必须在调用的时候被传值

def index(x,y): 
  print(x,y)

index(111,222)

"""
会按照位置顺序依次给形参赋值
x = 111
y = 222
"""

>>> 111 222

关键字参数

为了避免位置参数严格的位置要求,调用参数时可以指定对应形式参数的名字,这是关键字参数,它甚至可以采用与函数定义时不同的顺序。

def index(x,y): 
  print(x,y)

index(y=1,x=2)  # 指名道姓的传

>>> 2 1

位置参数和关键字参数混合:

如果同时出现两种参数形式, 首先应该写的是位置参数,然后是关键字参数

def index(x1,y1,x2,y2): 
  print(x1,y1,x2,y2)

index(111,222,y2=333,x2=444)
# 这一步 x2 = 44 ,y2 = 333 覆盖了原先的顺序赋值

>>> 111 222 444 333

下面程序出错:

def index(x1,y1,x2,y2): 
  print(x1,y1,x2,y2)

index(111,222,y2=333,444)
# 这一步 x2 = 44 ,y2 = 333 覆盖了原先的顺序赋值

>>> File "<input>", line 1 SyntaxError: positional argument follows keyword argument

默认值参数

当调用方没有提供对应形式参数的值时,你可以指定默认形式参数值。如果你供 实参, 在调用时会代替默认值

def register(name, sex='male'):
    print(name, sex)

register('EmptinessBoy')  # 用默认的参数
register('XiaoHong', 'female')  # 传递参数覆盖默认参数


>>> EmptinessBoy male
XiaoHong female

默认参数值在函数对象被创建时计算:

def init(arg, result=[]):
    result.append(arg)
    print(result)


init("a")
init("b")
init("c")
init(1, [2])

# 因为函数对象只创建一次,所以后两次用的形参 result 值没有被改变

>>> ['a']
['a', 'b']
['a', 'b', 'c']
[2, 1]

数量可变参数

参数值的元组 *

当函数参数数目不确定时 星号将一组可变数量的位置参数集合成参数值的元组 * 号会接受多余的位置参数 组织成元组的形式赋值给 * 号后面的变量名

收集参数到字典 ``````**

** 号会接受多余的关键字参数 组织成字典的形式赋值给 ** 号后面的变量名

def index(x, y, *a, **k):
    print(a, end=" ")
    print(k)


index(111, 222)
index(111, 222, 333, 444)  # a = (333, 444)
index(111, 222, n=1, m=2, o=3)  # k = {'n': 1, 'm': 2, 'o': 3}
index(111, 222, 333, 444, n=1, m=2, o=3)  # a = (333, 444)  # k = {'n': 1, 'm': 2, 'o': 3}


>>> () {}
(333, 444) {}
() {'n': 1, 'm': 2, 'o': 3}
(333, 444) {'n': 1, 'm': 2, 'o': 3}

函数在定义阶段 针对可变长的形参 推荐按照以下命名方式:

def index(*args,**kwargs):
    pass

实参拆包

在实参中的作用:

* 号会将列表打散成位置参数的形式 ** 号会将字典打散成关键字参数的形式

l = [1,2,3,4,5,6,7,8]
index(*l)
# 或者
index(*[1,2,3,4,5,6,7,8])

# 等价于 index(1,2,3,4,5,6,7,8)



k = {'username':'jason','password':'123'}
index(**k)
# 或者
index(**{'username':'jason','password':'123'})

# 等价于 index(username='jason',password='123')

注意

可变对象和不可变对象当参数:(可变对象和不可变对象当参数,效果可能是不同的)

当实参是不可变对象时,形参值改变 会影响实参!

当实参是可变对象时,形参值改变可能会影响实参!

名称空间与作用域

名称空间

  1. 变量可被访问范围称为变量的作用域,也称为变量命名空间或变量名字空间。 Python 程序用命名空间区分不同空间的相同名字。

  2. Python 解释器启动时建立一个全局命名空间,全局变量就放在这个空间,还建立内置命名空间 (built-in namespace),记录所有标准常量名、标准函数名等。在全局命名空间中定义的变量是全局变量。

  3. 每一个函数定义自己的命名空间,函数内部定义的变量是局部变量。如果在一个函数中定义一个变量 x,在另外一个函数中也定义 x 变量,因为是在不同的命名空间,所以两者指代的是不同的变量。可以通过多种方式获取其他命名空间的变量。

内置名称空间:

伴随着 python 解释器的启动(创建),结束(销毁),因为是第一个被加载的名称空间,该名称空间中存放了很多提前给你写好的名字,比如内置函数/方法 max min len

全局名称空间:

伴随着 py 文件的执行(创建),结束(销毁),是第二个被加载的名称空间,文件执行过程中产生的名字会存放于该名称空间内

import datetime  # 模块名

name = 'jason'  # 变量名

if name == 'jason':
    x = 1  # 变量名

def index():  # 函数名
    pass

class MyClass:  # 类名
    pass

作用域

对象的查找顺序

局部名称空间 >>> 全局名称空间 >>> 内置名称空间

  • 在局部名称空间

    1. 先在局部名称空间中找
    2. 再去全局名称空间中找
    3. 再去内置名称空间中找
  • 在全局名称空间

    1. 现在全局名称空间中找
    2. 内置名称空间中找
x = 100
def index():
    x = 300
    print(x)  # 300
    print(locals())  # 查看局部作用域中的名字

index()
print(x)  # 100
print(globals())  # 查看全局作用域中的名字


>>> 300
{'x': 300}
100
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001F7E6F40940>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'G:/NextCloud/Documents/代码操练/Python/01/eg6.py', '__cached__': None, 'x': 100, 'index': <function index at 0x000001F7E6F251F0>}

通过关键字互相访问

global 关键字

如希望在函数中使用全局变量,而不是创建局部变量,需要用 global 关键字声明

x = 100
def index():
    # 修改全局的x
    global x
    x = 300
    print(x)  # 300

>>> 300

nonlocal 关键字

python 支持函数的嵌套,在嵌套的函数内查找名字时, 会优先查找自己局部作用域的名字然后由内而外一层层查找外部嵌套函数定义的作用域,没有找到则去找全局

nonlocal 声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量。

def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        # 在inner的局部修改outer的局部
        print('inner',x)  # inner 2
    inner()
    print('outer',x)  # outer 2
outer()

>>> inner 2
outer 2

内置函数

sorted 函数

sorted 函数对字符串,列表,元组,字典等对象进行排序操作。

.sort 是应用在 list 上的方法,sorted 可以对更多的数据类
型进行排序操作。

即便都是对列表操作,list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 返回的是一个新的 list,而不是在原来的基础上进行的操作。

sorted (iterable, key, reverse)

参数内容
iterable– 序列,如字符串,列表,元组等。
key– 函数,缺省为空
reverse– 排序规则
reverse= True 降序 , reverse = False 升序(默认)。

例子(多元列表排序):

python-sorted.jpg

students = [('江幸', 89, 15), (' 方鹏', 80, 14), ('陈可', 85, 14)]
# 第二个分量是成绩,第三个分量是年龄

print(sorted(students, key=lambda s: s[2]))  # 按年龄从小到大排序
# [(' 方鹏', 80, 14), ('陈可', 85, 14), ('江幸',89, 15)]

print(sorted(students, key=lambda s: s[1], reverse=True))  # 按成绩从大到小降序
# [('江幸', 89, 15), ('陈可', 85, 14), (' 方鹏',80, 14)]


>>> [(' 方鹏', 80, 14), ('陈可', 85, 14), ('江幸', 89, 15)]
[('江幸', 89, 15), ('陈可', 85, 14), (' 方鹏', 80, 14)]

map函数

  • map 会根据提供的函数对指定序列做映射。
  • map 函数语法:map(function, iterable, …)
  • 第一个参数 function 是对参数序列中的每一个元素调用 function 函数,iterable 是序列,
  • 返回值的新列表或迭代器,每个元素是调用 function 函数的返回值

zip函数

  • zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表或迭代器 。
  • 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同。
  • zip 语法:zip([iterable, …])

参数说明:iterable,… –两个或多个序列返回值:返回元组列表

例子:

a = [1, 2, 3]
b = [4, 5, 6]
c = [4, 5, 6, 7, 8]
print(list(zip(a, b)))
# [(1, 4), (2, 5), (3, 6)]
print(list(zip(a, c)))  # 元素个数与最短的列表一致
# [(1, 4), (2, 5), (3, 6)]


>>>[(1, 4), (2, 5), (3, 6)]
[(1, 4), (2, 5), (3, 6)]

eval 和 exec函数

Python 是一种动态语言,它包含很多含义。Python 变量类型,操作的合法性检查都在动态运行中检查;运算的代码需要到运行时才能动态确定;程序结构也可以动态变化,容许动态加载新模块等。这两个函数就体现了这个特点。

  • eval是计算表达式,返回表达式的值。
  • exec可运行Python的程序,返回程序运行结果。
x, y = 3, 7
eval('x+3*y-4')
# 20

exec('print("hello world")')
# Hello world


>>> hello world

all 和 any 函数

all() 和 any() 函数将可迭代的对象作为参数 。

all 函数参数都是 True 时 ,才返回 True 否则返回 False
any 函数参数只要有一个为 True ,就返回 True, 参数全部是 False 时,返回 False.

函数对象

函数名可以被当作“数据”来处理,具体的可以分为以下四个应用:(函数名加括号执行优先级最高)

四种函数对象特性

函数名被引用

让一个变量等于函数名后,这个变量对应的地址和函数对应的地址一致,可以直接调用新定义的变量加括号来执行函数体内容

def index():
    print('from index')

print(index)  # <function index at 0x000001B260AE51F0>

x = index
print(x)  # <function index at 0x000001B260AE51F0>

# 运行 x
x()  # from index


>>> <function index at 0x00000289BDDA51F0>
<function index at 0x00000289BDDA51F0>
from index

函数名作为容器类型的元素

可以将函数名存放到列表,元组,字典,集合等数据容器中并进行调用

def index():
    print('from index')

d = {'username':'apple','func':index}
print(d)  # {'username': 'jason', 'func': <function index at 0x0000025C266251F0>}
d.get('func')()  # from index
d['func']()


>>> {'username': 'apple', 'func': <function index at 0x0000017B4EFD51F0>}
from index
from index

函数名还当作函数的参数

def index():
    print('from index')

def foo(a,b):
    print(a)
    b()

foo(1,index)


>>> 1
from index

函数名当作函数的返回值

def index():
    print('from index')

def foo():
    print('from foo')
    return index

res = foo()
res()


>>> from foo
from index

闭包函数

根据上述函数对象的特性,我们可以用来组装闭包函数。那么闭包函数的定义如下:

  1. 必须是定义在函数内部的函数
  2. 内部函数必须使用了外部函数名称空间/作用域的名字
def index():
    name = 'jason'
    def inner():  # 闭包函数
        print(name)

闭包函数的出现给我们提供了另外一种给函数体传参的方式

1.通过函数的形参传入
def index(username):
    print(username)

2.利用闭包函数将参数包给函数
def func(username):
    print(username)

def outer(username):
    # username = 'jason'
    def index():
        print(username)
    return index
    # 将局部名称空间中的名字返回 打破层级限制
    # 使得在全局也能够访问到局部名称空间中的名字
# 重复打印三次用户名
# func('jason')
# func('jason')
# func('jason')
res = outer('jason')  # 闭包函数可以帮我们保存内部函数所需要的数据
res()
res()
res()
res1 = outer('egon')
res1()
res1()
res1()

装饰器

"""
在不改变被装饰对象(函数...)原来的调用方式和内部原代码的情况下给该对象(函数...)添加新的功能
"""
# 必备知识点
import time  # 别人写好的用来操作时间的模块
# 1.获取1970 到执行代码的那一刻所经历的秒数  时间戳
print(time.time())  # 1593501513.9230678
# 2.让程序原地阻塞
time.sleep(2)  # 让程序阻塞2秒之后再运行
print(123)

# 装饰器推导流程
# 封装成函数
# 1 如果形参的形式给函数传参不符合装饰器定义
# def get_time(func):
#     start_time = time.time()
#     func()
#     end_time = time.time()
#     print('耗时:%s'%(end_time-start_time))
# get_time(home)
# get_time(index)
# 2 利用闭包函数给函数传参  装饰器雏形
# def outer(func):  # func = home
#     def get_time():
#         start_time = time.time()
#         func()  # home()
#         end_time = time.time()
#         print('耗时:%s'%(end_time-start_time))
#     return get_time
# home = outer(home)  # 普通的变量名home = outer(真正的函数名home)
# # print(home)  # <function outer.<locals>.get_time at 0x11d69f620>
# home()
# index = outer(index)
# index()
# 3 如果被装饰的函数有参数     可变长参数
def outer(func):  # func = home
    def get_time(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)  # home()
        end_time = time.time()
        print('耗时:%s'%(end_time-start_time))
    return get_time
home = outer(home)  # 普通的变量名home = outer(真正的函数名home)
# print(home)  # <function outer.<locals>.get_time at 0x11d69f620>
home(123)
index = outer(index)
index()

装饰器语法糖

# 装饰器语法糖
def outer(func):  # func = home
    def get_time(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)  # home()
        end_time = time.time()
        print('耗时:%s'%(end_time-start_time))
    return get_time
# @outer  # home = outer(home)  会将紧挨着语法糖下面的函数名自动当作第一个参数传递给@后面的函数调用执行
def home(a):
    time.sleep(3)
    print('from home',a)

@outer
def index():
    time.sleep(3)
    print('from index')
# print(home)  # <function outer.<locals>.get_time at 0x10ade9d08>
home(123)
index()
# print(index)  # <function outer.<locals>.get_time at 0x10ade9d08>
"""使用语法糖的好处就在于 只需要给需要加功能的函数上面加一句代码就完事了 其他代码都不用看也不用该"""

多层装饰器

def wrapper1(func):
    def inner1():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner1
def wrapper2(func):
    def inner2():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner2
def wrapper3(func):
    def inner3():
        print('wrapper3 ,before func')
        func()
        print('wrapper3 ,after func')
    return inner3
@wrapper3  # f = wrapper3(inner2)
@wrapper2  # inner2 = wrapper2(inner1)
@wrapper1  # inner1 = wrapper1(f)
def f():
    print('in f')
# print(f)  # <function wrapper3.<locals>.inner3 at 0x119502730>
f()
"""
语法糖在装饰的时候 如果有多个 那么顺序是从下网上
wrapper3 ,before func
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
wrapper3 ,after func
"""

补充

# 带参数的装饰器
def login_auth(db):
    def outer(func):
        def inner(*args,**kwargs):
            if db == 'MySQL':
                res = func(*args,**kwargs)
            elif db == 'Oracle':
                res = func(*args,**kwargs)
            elif db == 'postgresql':
                res = func(*args,**kwargs)
            else:
                res = func(*args,**kwargs)
            return res
        return inner
    return outer

@login_auth('MySQL')  # 1.先执行login_auth('MySQL')  # 2.再执行语法糖句式 @outer
def index():
    pass

装饰器模版

# 无参装饰器
def outer(func):
  def inner(*args,**kwargs):
    print('执行被装饰对象之前你可以做的操作')
    res = func(*args,**kwargs)
    print('执行被装饰对象之后你可以做的操作')
    return res
     return inner

# 有参装饰器
def login_auth(*args,**kwargs):
    def outer(func):
      def inner(*args,**kwargs):
        print('执行被装饰对象之前你可以做的操作')
        res = func(*args,**kwargs)
        print('执行被装饰对象之后你可以做的操作')
        return res
         return inner
     return outer

模块

import 模块名

模块名是另外一个 Python 文件的文件名,不包含扩展名,模块是可以运行的程序,“import 模块名” 就是执行文件名为模块名的程序

from … import

from 模块名 import *

这种方法引入模块中的所有函数,调用的时候不需要再加模块名

from 模块名 import 函数名

这种方法引入模块中的单个函数,调用的时候也不需要再加模块名

This blog is under a CC BY-NC-ND 4.0 Unported License
本文链接:https://coding.emptinessboy.com/2020/07/Python-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B004%EF%BC%88%E5%87%BD%E6%95%B0%EF%BC%89/