吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 588|回复: 3
收起左侧

[学习记录] 30Days Python——Day08 Function(函数)

[复制链接]
nocofffe 发表于 2023-8-9 19:48
本帖最后由 nocofffe 于 2023-8-10 21:38 编辑

Day 08 Function(函数)

我们对Python内置的函数已经熟悉的差不多了,例如len()、add()、remove()、等

8.0 函数的定义

函数的语法格式如下:

def 函数名称(参数值1[,参数值2, ...]):
"""函数批注(docstring)"""
程序代码区块                                                        #需要缩进
return [回传值1, 回传值2 , ...]                 #中括号可有可无

函数名称: 名称必须是唯一的。

参数值: 可有可无,完全视函数设计需要。

函数批注: 可有可无,开发大型程序时便于他人阅读(docString 是 document string 的缩写);我们可以用help(函数名称)列出此函数的文件字符串

return:无论是return还是右边的回传值都是可有可无的。

8.1函数的基本

有关函数的:参数设计、函数回传值、参数的传递涉及内容比较基础,本小结略,等有机会再补充

8.2 进一步理解函数

在Python中所有东西皆是对象,例如:字符串、列表、字典等,甚至函数也是对象,我们可以将函数赋值给一个变量,也可以将函数当作参数传送,甚至将函数回传,当然也可以动态建立或是销毁。

8.2.1 函数是一个对象

从上述执行可以知道upperStr和upperLetter 指的是同一个函数对象。此外一个函数若是去掉小括号,这个函数就是一个内存地址,可参考下列验证。由于upperStr和upperLetter是指相同对象,所以它们的内存地址相同。

def upperStr(text):
    return text.upper()
print(upperStr("deepstone"))

upperLetter = upperStr
print(upperLetter("deepstone"))
print(upperLetter)
DEEPSTONE
DEEPSTONE
<function upperStr at 0x0000019CCFDB09D8>
8.2.2 函数可以是数据结构成员

可以将函数当作数据结构(例如:列表、元组等)的元素,自然也可以迭代这些函数,这个概念可以应用在自建函数或内置函数。

def total(data):
        return sum(data)
x=(1,5,10)
myList = [min, max, sum, total]
for f in myList:
        print(f)
<built-in function min>
<built-in function max>
<built-in function sum>
<function total at 0x000001D869112708>
8.2.3 函数可以当作参数传递给其他函数
def add(x,y):
    return x+y
def mul(x, y):
    return x*y

def running(func,arg1,arg2):
    return func(arg1,arg2)
result1 = running(add,5,10)
print(result1)
result2 = running(mul,5,10)
print(result2)
15
50
8.2.4 函数当参数与*args不定量的参数

可以将函数当作传递参数使用,其实也可以配合*args与**kwargs共同使用。

def mysum(*args):
    return sum(args)
def run_with_multiple_args(func,*args):
    return func(*args)
print(run_with_multiple_args(mysum,1,2,3,4,5))
print(run_with_multiple_args(mysum,6,7,8,9))
15
30
8.2.5 嵌套函数

函数内部也可以有函数;嵌套函数具有可重复使用、封装、隐藏数据的效果。

def dist(x1,y1,x2,y2):
    def mySqrt(z):
        return z**0.5
    dx = (x1-x2) **2
    dy = (y1-y2) **2
    return mySqrt(dx+dy)
print(dist(0,0,1,1))
# 1.4142135623730951
8.2.6 嵌套函数

在嵌套函数的应用中,常常会应用到将一个内层函数当作回传值,这时所回传的是内层函数的内存地址。

# 这是一个计算 1~(n-1)的总和
def outer():
    def inner(n):
        print("inner running")
        return sum(range(n))
    return inner

f = outer()         # outer()回传inner地址
print(f)            # 打印inner内存
print(f(5))         # 实际执行的是inner()

y = outer()
print(y)
print(y(10))

"""
<function outer.<locals>.inner at 0x00000216FAAD4708>
inner running
10
<function outer.<locals>.inner at 0x00000216FAAD4E58>
inner running
45
"""

执行第8行时,outer()会回传lnner的内存地址,对f而言所获得的只是inner()的内存地址。所以第9行可以列出inner()的内存地址。当执行到第10行f(5)时,才是真正执行计算总和。

由于inner()是在执行期间被定义,所以第12行时会产生新的inner()地址,所以主程序二次调用,会有不同的inner()。

最后读者必须了解,我们无法在主程序直接调用内部函数,这会产生错误!

8.2.7 闭包(closure)

内部函数是一个动态产生的程序,当它可以记住函数以外的程序所建立的环境变量值时,我们称这个内部函数是闭包(closure)。

# 一个线性函数ax+b的 闭包说明
def outer():
    b=10                                        #inner所使用的变量值
    def inner(x):
        return 5 * x + b        #应用第三行的b
    return inner
b = 2
f = outer()
print(f(b))
#20

上述第3行b是一个环境变量,这也是定义在inner()以外的变量,由于第6行使用inner当作回传值,inner()内的b其实就是第3行所定义的b,其实变量b和inner()就构成了一个closure。

程序第10行的f(b)其实这个b将是inner(x)的x参数,所以最后可以得到5*2+10结果是20.

print(f)
#<function outer.<locals>.inner at 0x0000023DCF994708>
print(f.__closure__)
# (<cell at 0x0000023DCF6AC738: int object at 0x00007FFBEB3CA2B0>,)
print(f.__closure__[0].cell_contents)
# 10

其实 closure 内是一个元组,环境变量b就是存在cell_contents内。

# 闭包的另一个应用
def outer(a,b):
    """ a 和 b 将是inner()的环境变量"""
    def inner(x):
        return a * x + b
    return inner

f1 = outer(1,2)
f2 = outer(3,4)
print(f1(1),f2(3))
# 3 13

这个程序第8行建立了x+2,第9行建立了3x+4,相当于使用了closure将最终线性函数确定下来,第10行传递适当的值’就可以获得结果。

8.3 递归(Recursive)

一个函数可以调用其他函数也可以调用自己,其中调用本身的动作称递归式(recursive)调用,递归式调用有下列特色:

1、每次调用自己时,都会使范围越来越小。

2、必须要有—个终止的条件来结束递归函数。

# 递归函数执行阶乘运算
def factorial(n):
    if n == 1:
        return 1
    else:
        return (n * factorial(n-1))
value = 3
print(f"{value}的阶乘结果是:{factorial(value)}")
# 3的阶乘结果是:6

在编译程序中使用堆栈(stack)处理上述递归式调用,这是一种后进先出(last in first out)的数据结构,下列是编译程序实际使用堆栈方式使用内存的情形。
[img=562,184]https://s1.ax1x.com/2023/08/08/pPZi0xJ.png[/img]

<a

在计算机术语中又将数据放入堆栈称堆入(push)。上述3的阶乘,编译程序实际回归的处理过程,其实就是将数据从堆栈中取出,此动作在计算机术语中称取出(pop),整个概念如下:

<a
[img=631,200]https://s1.ax1x.com/2023/08/08/pPZi6Vx.png[/img]

PS:Python预设最大递归次数为1000次,我们可以先导入sys模块,可以使用sys.getrecursionlimit()列出Python预设或目前递归的最大次数。

8.4 全局变量和局部变量

局部变量(local variable):某个变量只有在该函数内使用,影响范围限定在这个函数内。

全局变量(global variable):影响范围限定是在整个程序。

Python程序在调用函数时会建立一个内存工作区间,在这个内存工作区间可以处理属于这个函数的变量,当函数工作结束,返回原先调用程序时,这个内存工作区间就被收回。原先存在的变量也将被销毁,这也是为何局部变量的影响范围只限定在所属的函数内。

对于全局变量而言,—般是在主程序内建立,程序在执行时,不仅主程序可以引用,所有属于这个程序的函数也可以引用,所以它的影响范围是整个程序,直到整个程序执行结束。

注意事项:

1、局部变量内容无法在其他函数引用

2、局部变量内容无法在主程序引用

3、在函数内不能更改全局变量的值

4、果要在函数内存取或修改全局变量,必须在函数内使用global定义此变量

Python有提供函数让我们了解目前变量名称与内容。

locals():可以用字典方式列出所有的局部变量名称与内容。

globals():可以用字典方式列出所有的全局变量名称与内容。

nonlocal变量:

在Python的程序设计中还提供—种变量称nonlocal变量,它的用法与global相同,不过global 是指最顶层变量,nonlocal指的是上一层变量。

# nonlocal变量
def local_fun():
    var_nonlocal = 22
    def local_inner():
        global var_global
        nonlocal var_nonlocal
        var_nonlocal =222
        var_global =111
    local_inner()
    print("local_fun输出 var_global = ",var_global)
    print("local_fun输出 var_nonlocal = ",var_nonlocal)

var_global = 1
var_nonlocal = 2
print("主程序输出 var_global =  ",var_global)
print("主程序输出 var_nonlocal =  ",var_nonlocal)
local_fun()
print("主程序输出 var_global =  ",var_global)
print("主程序输出 var_nonlocal =  ",var_nonlocal)
"""
主程序输出 var_global =   1
主程序输出 var_nonlocal =   2
local_fun输出 var_global =  111
local_fun输出 var_nonlocal =  222
主程序输出 var_global =   111
主程序输出 var_nonlocal =   2
"""

上述程序内的local_inner()函数笔者尝试使用nonlocal和global定义更改变量,但是最后只有更改global的变量val_global,所以var_global输出111。nonlocal变量在上一层函数结束就结束, 相当于内存空间被收回。

8.5 匿名函数lambda的语法

匿名函数最大特色是可以有许多的参数,但是只能有一个程序码表达式,然后可以将执行结果回传。

lambda arg1[, arg2, ... argn]:expressionless # arg1是参数,可以有多个参数

上述expression就是匿名函数lambda表达式的内容

product = lambda x,y:x*y
print(product(5,10))
#50
8.5.1 使用lambda的理由

使用lambda的更佳时机是在一个函数的内部,这是一个方程式2x+b,有两个变量,第5行定义linear时,才能确定 lambda方程式是2x+5

def func(b):
    return lambda x : 2 * x + b
linear = func(5)            #将5传给lambda的b
print(linear(10))           #将10传给lambda的x
# 25
8.5.2 lambda应用高阶函数的参数

匿名函数一般用任不需要函数名称的场合,例如:一些高阶函数(Higher—order—function)的部分参数是函数,这时就很适合使用匿名函数, 同时可以让程序变得更简洁。

def mycar(cars,func):
    for car in cars:
        print(func(car))
def wdcar(carbrand):
    return "My dream car is "+carbrand.title()

dreamcars = ["porsche","rolls royce","maserati"]
mycar(dreamcars,wdcar)
"""
My dream car is Porsche
My dream car is Rolls Royce
My dream car is Maserati
"""

其实上述wdcar()函数就是使用匿名函数的好时机,尝试重新设计上程序,用lambda取代wdcar()

def mycar(cars,func):
    for car in cars:
        print(func(car))
dreamcars = ["porsche","rolls royce","maserati"]
mycar(dreamcars,lambda carbrand:"My dream car is "+carbrand.title())
8.5.3 lambda与filter()

有一个内置函数filter(),主要是筛选序列,它的语法格式如下:

filter(func,iterable)
#上述函数将依次对iterable的元素(item)放入func(item)内,然后将func()函数执行结果是True的元素(item)组成新的筛选对象(filter object)回传。
def oddfn(x):
    return x if (x % 2 == 1) else None

mylist = [5 ,10 ,15 ,20 ,25, 30]
filter_object = filter(oddfn, mylist)

print("奇数列表:",[item for item in filter_object])
# 奇数列表: [5, 15, 25]

使用lambda函数重新设计上程序

mylist = [5 ,10 ,15 ,20 ,25, 30]
oldlist = list(filter(lambda x :( x % 2 == 1 ),mylist ))        
print("奇数列表:",oldlist)
8.5.4 lambda与map()、reduce()
map(func,iterable)
# 上述函数将依次对iterable重复执行,即用item遍历iterable,执行func(item)
reduce(func,iterable)
# 它会先对可迭代对象的第l和第2个元素操作,结果再和第3个元素操作,直到最后一个元素。
# 假设iterable有4个元素,可以用下列方式解说:reduce(f,[a,b,c,d]) = f(f(f(a,b),c),d)

早期reduce()是内置函数’现在被移至functools模块,所以在使用前须在程序前方加上

from functools import reduce

8.6 装饰器

有时候我们想在函数内增加一些功能,但是又不想更改原先的函数,这时可以使用Python所提供的装饰器(decorator)。

# 假设我们不想更改greeting()函数的内容,但是希望可以将输出改成大写
def upper(func):
    def newFunc(args):
        oldresult = func(args)
        newresult = oldresult.upper()
        print("函数名称:",func.__name__)
        print("函数参数:",args)
        return newresult
    return newFunc

def greeting(String):       #问候函数
    return String

mygreeting = upper(greeting)#手动装饰器
print(mygreeting("hello world!"))
"""
函数名称: greeting
函数参数: hello world!
HELLO WORLD!
"""

装饰器设计的原则是有—个函数当作参数,然后在装饰器内重新定义一个含有装饰功能的新函数,可参考第3~8行。第6行是打印原函数的名称’在这里我们使用了func. _ name _ ’这是函数名称变量。

上述第14行是手动设定装饰器,Python中可以在欲装饰的函数前面加上@decorator,decorator 是装饰器名称。将11~15行内容改成:也一样

@upper                                                #设定装饰器
def greeting(String):       #问候函数
    return String
print(greeting("hello world!"))

装饰器另一个常用概念是为一个函数增加除错的检查功能,例如有一个除法函数如下:

def mydiv(x,y):
    return x/y

很明显若是div()的第2个参数是0时,将造成除法错误,我们可以使用装饰器改善此除法功能。

def errcheck(func):
    def newFunc(*args):
        if args[1 !=0 ]:
            result = func(*args)
        else:
            result = "除数不可为0"
        print("函数名称:",func.__name__)
        print("函数参数:",args)
        print("执行结果:",result)
        return result
    return newFunc
@errcheck
def mydiv(x,y):
    return x/y

print(mydiv(6,0))
"""
函数名称: mydiv
函数参数: (6, 0)
执行结果: 除数不可为0
除数不可为0
"""

在上述程序第3行的newFunc(*args)中出现*args,这会接收所传递的参数’同时以元组(tuple) 方式存储。

一个函数可以有2个以上的装饰器,方法是在函数上方设定装饰器函数,当有多个装饰器函数时,会由下往上依次执行装饰器,这个概念又称装饰器堆栈(decorator stacking)。

8.7 小测验

T1. 用函数重新设计文章单词出现的次数。

提示:整个功能分为两大块,第一块功能:修改文本格式:去掉标点符号、将字符串单词提取入列表;第二块功能:用字典生成式统计列表中的单词出现次数。

def modifySong(songStr):
    songStr1 = songStr.lower()
    for wd in songStr1:
        if wd in ",?\n.":
            songStr1 = songStr1.replace(wd," ")
    songlist = songStr1.split()
    return songlist
def wordCount(songCount):
     songdict = {ch : songCount.count(ch) for ch in set(songCount)}
     return songdict
song = """
Are you sleeping,
Are you sleeping?
Brother John?
Brother John?
Morning bells are ringing,
Morning bells are ringing,
Ding ding dong,
Ding ding dong.
"""
print(wordCount(modifySong(song)))
"""
{'sleeping': 2, 'ding': 4, 'john': 2, 'dong': 2, 'you': 2, 'morning': 2, 'ringing': 2, 'bells': 2, 'are': 4, 'brother': 2}
"""

T2. 设计isPrime()函数,检查所输入的数字是否为质数,是返回True,否则False。

def isPrime(num):
    flag = False
    for n in range(2,num):
        if num % n == 1:
            return True
    return False
num = int(input("请输入大于1的整数来检验:"))
if isPrime(num):
    print(f"{num}是质数")
else:
    print(f"{num}非质数")

T3. 设计一个程序实现输入两个数,得到两个数的最大公约数、最小公倍数

提示:最大公约数可以用辗转相除法来完成,步骤如下

1、比较两数的大小。

2、大数除以小数。

3、两数相除的余数当作下一次的除数,原除数变成被除数。

4、如此循环直到余数为0,此时除数即为最大公约数。

def func1(a,b):
    # 辗转相除法最大公约数过程
    max1 = max(a,b)
    min1 = min(a,b)
    while max1 % min1:
        c = max1 / min1
        max1 = min1
        min1 = c
    print(f"{a},{b}的最大公约数为{max1}")
    # 求最小公倍数过程即两数相乘除以最大公约数
    lcm= a * b // max1                                        
    print(f"{a},{b}的最大公约数为{lcm}")
func1(3,9)
#3,9的最大公约数为9
#3,9的最大公约数为3

实际上如果你更加熟练python,可以使用递归式函数设计,最大公约数过程:

def gcd(a,b):
    return a if b == 0 else gcd(b ,a % b)

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
lookfeiji + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

asd124689 发表于 2023-8-10 06:58
就30天吗 第8天才到函数
 楼主| nocofffe 发表于 2023-8-10 18:53
258239234 发表于 2023-8-30 07:07
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止回复与主题无关非技术内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-6-6 00:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表