Python 函数

一、初识函数定义和调用
def my_first_func():
    """这里是函数说明文档"""
    print("my first function...")


my_first_func()

定义: def关键字开头,空格之后接函数名称和圆括号(),最后还有一个“:” …
函数文档: 每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。通过函数特殊属性__doc__或者 help(函数名) 可以查看该函数的说明文档
调用: 就是函数名(),要记得加上括号。

二、函数的返回值

在python中使用关键字return来返回值,要研究返回值,分以下几个情况:没有返回值,返回一个值,返回多个值

  • 没有返回值
    不写return:默默返回一个None
    只写return:也是返回None, 另外,程序一旦遇到return,会结束整个函数。
    写 return None:和以上两种情况一样,但一般不这样写

  • 返回一个值
    return 后面写上要返回的内容即可。

# 函数定义
def mylen():
    """计算s1的长度"""
    s1 = "hello world"
    length = 0
    for i in s1:
        length += 1
    return length


# 函数调用
str_len = mylen()
print('str_len:%s' % str_len)

注意:return和返回值之间要有空格,可以返回任意数据类型的值

- 返回多个值

def demo1():
    '''返回多个值'''
    return 1, 2, 3, 4


def demo2():
    '''返回多个数据类型'''
    return 1, ['jack', 520], 'hey'


ret1 = demo1()
print(ret1)   # (1, 2, 3, 4)
ret2 = demo2()
print(ret2)   # (1, ['jack', 520], 'hey')

返回的多个值会被组织成元祖被返回,也可用多个值来接受,但是注意返回几个数据,就用几个变量来接受,不然会报错。

def demo3():
    '''返回多个数据类型'''
    return 1, ['jack', 520], 'hey'


a, b, c = demo3()
print(a, b, c)  # 1 ['jack', 520] hey
三、函数的参数
# 函数定义
# 函数定义
def mylen(s1):
    """计算s1的长度"""
    length = 0
    for i in s1:
        length += 1
    return length


# 函数调用
str_len = mylen("hello world")
print('str_len:%s' % str_len)

函数可以传递多个参数,用逗号隔开即可
- 实参与形参
1、 调用函数时传递的这个“hello world”被称为实际参数,应为这个是实际要交给函数的内容,简称实参
2、定义函数时的s1,只是一个名字,被称为形式参数,因为定义函数时它只是一个形式,表示这里有一个参数,简称形参

- 位置参数
  站在实参角度
1.按照位置传值

def mymax(x,y):
    #此时x=10,y=20
    the_max = x if x > y else y
    return the_max

ma = mymax(10,20)
print(ma)

2.按照关键字传值

def mymax(x,y):
    #此时x = 20,y = 10
    print(x,y)
    the_max = x if x > y else y
    return the_max

ma = mymax(y = 10,x = 20)
print(ma)

按照关键字传参

3.位置、关键字形式混着用

def mymax(x,y):
    #此时x = 10,y = 20
    print(x,y)
    the_max = x if x > y else y
    return the_max

ma = mymax(10,y = 20)
print(ma)

正确用法:
        1.位置参数必须在关键字参数的前面
        2.对于一个形参只能赋值一次
  站在形参角度
      位置参数必须传值

def mymax(x,y):
    #此时x = 10,y = 20
    print(x,y)
    the_max = x if x > y else y
    return the_max

#调用mymax不传递参数
ma = mymax()
print(ma)

#结果
TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'

- 默认参数
    1.正常使用
       使用方法:将变化比较小的值设置为默认参数
    2.默认参数的定义

def stu_info(name,sex = "male"):
    """打印学生信息函数,由于班中大部分学生都是男生,
        所以设置默认参数sex的默认值为'male'
    """
    print(name,sex)


stu_info('jacky')
stu_info('xiaohong','female')

3.参数陷阱:默认数据类型是一个可变数据类型

def jack(a, lis=[]):
    lis.append(a)
    return lis


print(jack(1))
print(jack(2))
print(jack(3))

运行结果:
[1]
[1, 2]
[1, 2, 3]

# 改进
def jack(a, lis=None):
    if lis is None:
        lis = []
    lis.append(a)
    return lis
# 陷进有时候用来简化代码,恰到好处
def jack(k,l = {}):
    l[k] = 'v'
    print(l)


jack(1)
jack(2)
jack(3)

运行结果:
{1: 'v'}
{1: 'v', 2: 'v'}
{1: 'v', 2: 'v', 3: 'v'}

summary:默认参数值只被赋值一次,如果默认参数的值是一个可变数据类型,那么每一次调用函数的时候如果不传参数,就共用这个数据类型的资源

- 动态参数
动态参数有两种:可以接受任意个参数
  *args : 接收的是按照位置传参的值,组织成一个元组
   **kwargs: 接受的是按照关键字传参的值,组织成一个字典
   args必须在kwargs之前
  
按位置传值多余的参数都由args统一接收,保存成一个元祖的形式

def sum_num(*args):  #站在形参的角度上,给变量加上*,就是组合所有传来的值。
    sum = 0
    for each_num in args:
        sum += each_num
    return sum


sum1 = sum_num(1, 2, 3, 4)
print(sum1)   # 10
l = [4, 3, 2, 1]
sum2 = sum_num(*l)  #站在实参的角度上,给一个序列加上*,就是将这个序列按照顺序打散
print(sum2)  #  10

将多余的关键字参数都由kwargs统一接收,保存成一个字典的形式

def good_info(**kwargs):  #形参角度,把键值对打包成一个字典
    print(kwargs)
    print(kwargs['name'], kwargs['price'])


good_info(name="computer", price=4000)
d1 = {'name': 'computer', 'price': 4000}
good_info(**d1)  # 把一个字典解包传进去

# 运行结果
>>>{'name': 'computer', 'price': 4000}
>>>computer 4000
>>>{'name': 'computer', 'price': 4000}
>>>computer 4000

在函数内部使用 * 可以将传入的位置参数(被打包成一个元祖)解包成多个参数(实参传值时的模样)以方便使用
使用 ** 可以将传入的关键字参数(被打包成一个字典)解包成多个参数(多个键值对的感觉)
在装饰器时,需要把外部函数可变参数传入给内部时,把参数打散再传入

def good_info(*args, **kwargs):  
    print(args, type(args))
    print(*args)   # 把元祖拆开
    print(kwargs, type(kwargs))
    # print(**kwargs)  # 打印这句将会报错


good_info("computer", "mouse", "phone", name="jack", sex="man")
# ('computer', 'mouse', 'phone') <class 'tuple'>
# computer mouse phone  
# {'name': 'jack', 'sex': 'man'} <class 'dict'>

参数总结
只有调用函数的时候
    按照位置传:直接写参数的值
    按照关键字传:关键字 = 值
    
定义函数的时候:
    位置参数 : 直接定义参数
    默认参数,关键字参数 :参数名 = ‘默认的值’
    动态参数 : 可以接受任意多个参数
        参数名之前加*,习惯参数名args,
        参数名之前加**,习惯参数名kwargs
顺序:位置参数,*args,默认参数,**kwargs

形参:
  位置参数 : 必须传
  *args :可以接收任意多个位置参数
  默认参数 : 可以不传
  **kwargs : 可以接收多个关键字参数
实参:按照位置传参,按照关键字传参

四、命名空间和作用域

命名空间一共分三种:
  全局命名空间
  局部命名空间
  内置命名空间
 *内置命名空间存放了python解释器为我们提供的名字,例如:print,input,list,len,…
 
加载顺序:内置命名空间(程序运行前加载)-> 全局命名空间(程序运行中,从上到下加载)-> 局部命名空间(函数被调用的时候加载)
取值:
  在局部调用时:局部 -> 全局 -> 内置
  
  在全局调用:全局->内置
   
作用域: 作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域
全局作用域:包含内置名称空间全局名称空间,在整个文件的位置都能被引用,全局有效。
局部作用域:局部名称空间,只能在局部范围内生效。
找变量
globals() 和locals()的用法:

  • globals() 函数会以字典类型返回当前位置的全部全局变量
  • locals() 函数会以字典类型返回当前位置的全部局部变量。 对于函数, 方法, lambda 函式, 类, 以及实现了 call 方法的类实例, 它都返回 True。
def jack():
    name = "jack_hh"
    print(locals())  # 如果该代码放在全局范围内,那么会返回全局变量
    print(globals())  # 在哪里调用结果都一样,返回全局变量


jack()
print(globals())
运行结果:
{'name': 'jack_hh'}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001BD1B7073C8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/python文件都在这里/python 全栈/day9-20/day10/day10.py', '__cached__': None, 'a': 1, 'jack': <function jack at 0x000001BD1B6BC268>}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001BD1B7073C8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/python文件都在这里/python 全栈/day9-20/day10/day10.py', '__cached__': None, 'a': 1, 'jack': <function jack at 0x000001BD1B6BC268>}
  • global关键字:
    在函数内不能修改全局的变量,如果这样做python会在函数内部生成一个和全局变量名字一样的全局变量,全局变量不受影响。
    可以在局部内使用global关键字,来修改全局变量的值
a = 1
b = 1

def func():
    global b
    a = 2
    b = 2
    print("in func: a:%d b:%d" % (a, b))


func()
print("in outside: a:%d b:%d" % (a, b))

运行结果:
in func: a:2 b:2
in outside: a:1 b:2  # 只有global声明的变量b在函数中得到了修改
五、函数的嵌套和作用域链
  • 函数的嵌套调用
def my_max2(x, y):
    return x if x > y else y


def my_max4(a, b, c, d):
    res1 = my_max2(a, b)
    res2 = my_max2(res1, c)
    res3 = my_max2(res2, d)
    return res3
    
# my_max4(52,-2,123,0)
  • 函数的嵌套的定义
    函数嵌套定义
  • 函数的作用域链
# 内层函数可以使用外层函数变量
def f1():
    a = 1
    def f2():
        print(a)
    f2()

f1()
# 内层函数修改外层函数变量,将在内层函数中生成一个名字相同的变量,外层函数变量不受影响
def f1():
    a = 1
    def f2():
        a = 2
    f2()
    print('a in f1 : ', a)

f1()
#  a in f1 :  1

nonlocal 关键字用于在内层函数修改外层函数变量

1.nonlocal 只能用于局部变量,找上层中离当前函数最近一层的局部变量
2. 在内部函数声明nonlocal之前不能再出现同名变量
3. 声明了nonlocal的内部函数的变量修改会影响到 离当前函数最近一层的局部变量,对全局无效,对局部也只是对最近的一层有效

a = 0
def outer():
    a = 1
    def inner1():
        a = 2
        def inner2():
            nonlocal a
            a *= 10
            print("in inner2 a:%d"%a)
        inner2()
        print("in inner1:a:%d"%a)  # 外层函数inner1变量a被内层函数inner2函数修改
    inner1()

print("outside a:%d"%a)
outer()
# 运行结果
outside a:0
in inner2 a:20
in inner1:a:20
六、函数名的本质

函数名的本质就是函数的内存地址,内存地址加()就是函数的调用

  • 可以被引用
def func():
    print("in func")

f = func
f()
  • 可以被当做容器类型的元素
def func1():
    print("in func1")


def func2():
    print("in func2")


def func3():
    print("in func3")


lis = [func1, func2, func3, ]
dict1 = {"f1": func1, "f2": func2, "f3": func3}
for each in lis:
    each()
dict1["f2"]()

# in func1
# in func2
# in func3
# in func2
  • 可以当做函数的参数和返回值
def wahaha(f):  
    f()
    return f  # 函数名可以作为返回值

def qqxing():
    print("in qqxing")

func = wahaha(qqxing)  # 函数名可以作为参数传入
func()
# result:
# in qqxing
# in qqxing

不懂? 那就把函数名当普通变量来用
第一类对象的概念:
第一类对象(first-class object)指

  1. 可在运行期创建
  2. 可用作函数参数或返回值
  3. 可存入变量的实体。
七、闭包
def func():
    name = 'jacky'
    def inner():
        print(name)

闭包函数:内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数
闭包会一直存在内存当中,不会因为函数执行结束而被释放

由于作用域的关系,我们不能拿到函数内部的变量和内部定义的函数,这时,如果需要在函数外部使用函数内部变量,我们可以直接将变量返回, 那如果在需要用到函数内部定义的函数时,,我们就可以可以将函数的名字返回即可。

  • 闭包函数的最常用的用法
def func():
    name = 'jacky'
    def inner():
        print(name)
    return inner

f = func()
f()
  • 判断闭包函数的方法__closure__
# #输出的__closure__有cell元素 :是闭包函数
def func():
    name = 'jacky'
    def inner():
        print(name)
        print(inner.__closure__)
    return inner

f = func()
f()

# #输出的__closure__为None :不是闭包函数
name = 'weige'
def func2():
    def inner():
        print(name)
        print(inner.__closure__)
    return inner

f2 = func2()
f2()
# C:\Users\Jacky\AppData\Local\Programs\Python\Python37\python.exe "C:/python文件都在这里day10.py"
# jacky
# (<cell at 0x0000026AB84F0108: function object at 0x0000026ABA3F1EA0>, <cell at 0x0000026ABA1C82E8: str object at 0x0000026AB85E73E8>)
# weige
# (<cell at 0x0000026ABA3ED0D8: function object at 0x0000026AC14236A8>,)
  • 闭包嵌套
def func():
    name = 'jacky'
    def inner():
        sex = "man"
        def inner2():
            print("hello, little %s" % "boy" if sex == "man" else "girl")
        return inner2
    return inner


f = func()
f()()
  • 闭包获取网络的应用
from urllib.request import urlopen
# 多次调用get_message_func ()函数时,变量url以只需要被定义一次,相比于调用get_url函数而言,
# 省去了多次生成改变量之后又销毁
def get_url():
    url = r"https://blog.csdn.net/qq_41823444/article/details/94393150"
    def get_message():
        return urlopen(url).read()
    return get_message

get_message_func = get_url()
content = get_message_func()
print(content)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值