Python奇幻之旅(从入门到入狱基础篇)——附相关资料【中】

目录

目录

引言

1. 文件操作相关

1.1. 读文件

1.2. 写文件

1.3. 文件打开模式

1.4. 常见功能

1.5. 上下文管理

2. 函数

2.1. 初识函数

2.2. 函数的参数

2.2.1. 参数

2.2.2. 默认参数

2.2.3. 动态参数

2.3. 函数返回值

3. 函数高级

3.1. 参数的补充

3.2. 参数内存地址相关

3.2.1. 函数的返回值是内存地址

3.3. 函数和函数名

3.3.1. 函数做元素

3.3.2. 函数名赋值

3.3.3. 函数名做参数和返回值

3.4. 返回值和print

3.5. 作用域

3.5.1. 函数为作用域

3.5.2. 全局和局部

3.5.3. global关键字

3.8. 函数嵌套

3.8.1. 函数在作用域中

3.8.2. 函数嵌套定义

3.8.3. 作用域

3.9. 闭包

3.10. 装饰器

3.10.1. 优化

3.10.2. 应用场景

3.10.3. functools

结尾


引言

本篇文章是继上而言的,主要内容是文件操作、函数入门、函数高级的一些内容,比如闭包、装饰器、传参等。

1. 文件操作相关

1.1. 读文件

主要是读取所保存的文件内容

  • 读文本文件
# 1.打开文件
file_object = open('info.txt', mode='r')
# 2.读取文件内容,并赋值给data
data = file_object.read()
# 3.关闭文件
file_object.close()

print(data) # 'jiaoxingk-123\n'
  • 读图片等非文本内容文件。
file_object = open('a1.png', mode='rb')
data = file_object.read()
file_object.close()

print(data) # \x91\xf6\xf2\x83\x8aQFfv\x8b7\xcc\xed\xc3}\x7fT\x9d{.3.\xf1{\xe8\...

注意事项:

路径

  • 相对路径:相对于自己的目标文件位置(/info.txt)
  • 绝对路径:文件在硬盘上真正存在的路径(/Users/jiaoxingk/PycharmProjects/info.txt)
# 1.打开文件
file_object = open('/Users/jiaoxingk/PycharmProjects/info.txt', mode='rt', encoding='utf-8')
# 2.读取文件内容,并赋值给data
data = file_object.read()
# 3.关闭文件
file_object.close()
  • 读文件时,文件不存在程序会报错。
Traceback (most recent call last):
  File "/Users/jiaoxingk/PycharmProjects/2.读文件.py", line 2, in <module>
    file_object = open('infower.txt', mode='rt', encoding='utf-8')
FileNotFoundError: [Errno 2] No such file or directory: 'infower.txt'

1.2. 写文件

  • 写文本文件
# 1.打开文件
# 路径:t1.txt
# 模式:wb(要求写入的内容需要是字节类型)
file_object = open("t1.txt", mode='wb')

# 2.写入内容
file_object.write("jiaoxingk".encode("utf-8"))

# 3.文件关闭
file_object.close()

  • 写图片等文件
f1 = open('a1.png',mode='rb')
content = f1.read()
f1.close()

f2 = open('a2.png',mode='wb')
f2.write(content)
f2.close()

注意事项:

  • 文件不存在时,w模式会新建然后再写入内容;文件存在时,w模式会清空文件再写入内容。

1.3. 文件打开模式

上文我们基于文件操作基本实现了读、写的功能,其中涉及的文件操作模式:rt、rb、wt、wb,其实在文件操作中还有其他的很多模式。

关于文件的打开模式常见应用有:

不加:任何文件

t:文本文件

b:二进制文件

只读:rrtrb

只写:wwtwb

追加:aatab【尾部追加】

读写(既可以读,也可以写):

  • r+、rt+、rb+,默认光标位置:起始位置
file_object = open('info.txt', mode='rt+')

# 读取内容
data = file_object.read()
print(data)

# 写入内容
file_object.write("你好呀")

file_object.close()

  • w+、wt+、wb+,默认光标位置:起始位置(清空文件)
file_object = open('info.txt', mode='wt+')

# 读取内容
data = file_object.read()
print(data)

# 写入内容
file_object.write("你好呀")

# 将光标位置重置起始
file_object.seek(0)

# 读取内容
data = file_object.read()
print(data)

file_object.close()

  • x+、xt+、xb+,默认光标位置:起始位置(新文件)
  • a+、at+、ab+,默认光标位置:末尾
file_object = open('info.txt', mode='at+')

# 写入内容
file_object.write("jiaoxingk")

# 将光标位置重置起始
file_object.seek(0)

# 读取内容
data = file_object.read()
print(data)

file_object.close()

1.4. 常见功能

在上述对文件的操作中,我们只使用了write和read来对文件进行读写,其实在文件操作中还有很多其他的功能来辅助实现更好的读写文件的内容。

  • read,读        

        读所有【常用】

f = open('info.txt', mode='r',encoding='utf-8')
data = f.read()
f.close()

        读n个字符(字节)

f = open('info.txt', mode='r', encoding='utf-8')
# 读1个字符
data = f.read(1)
f.close()

print(data) # 荒

  • readline,读一行
f = open('info.txt', mode='r', encoding='utf-8')

v1 = f.readline()
print(v1)

v2 = f.readline()
print(v2)

f.close()

  • readlines,读所有行,每行作为列表的一个元素
f = open('info.txt', mode='rb')

data_list = f.readlines()

f.close()

print(data_list)

  • 循环,读大文件(readline加强版)【常见】
f = open('info.txt', mode='r', encoding='utf-8')
for line in f:
    print(line.strip())
f.close()

  • write,写
f = open('info.txt', mode='a',encoding='utf-8')
f.write("jiaoxingk")
f.close()

  • flush,刷到硬盘
f = open('info.txt', mode='a',encoding='utf-8')

while True:
    # 不是写到了硬盘,而是写在缓冲区,系统会将缓冲区的内容刷到硬盘。
	f.write("jiaoxingk")
    f.flush()

f.close()

  • 移动光标位置(字节)
f = open('info.txt', mode='r+', encoding='utf-8')

# 移动到指定字节的位置
f.seek(3)
f.write("jiaoxingk")

f.close()


注意:在a模式下,调用write在文件中写入内容时,永远只能将内容写入到尾部,不会写到光标的位置。

  • 获取当前光标位置
f = open('info.txt', mode='r', encoding='utf-8')

p1 = f.tell()
print(p1)  # 0

f.read(3)  # 读3个字符 3*3=9字节

p2 = f.tell()
print(p2)  # 9

f.close()

1.5. 上下文管理

之前对文件进行操作时,每次都要打开和关闭文件,比较繁琐且容易忘记关闭文件。

以后再进行文件操作时,使用with上下文管理,它可以自动实现关闭文件。

with open("xxxx.txt", mode='rb') as file_object:
    data = file_object.read()
    print(data)

在Python 2.7 后,with又支持同时对多个文件的上下文进行管理,即:

with open("xxxx.txt", mode='rb') as f1, open("xxxx.txt", mode='rb') as f2:
    pass

2. 函数

2.1. 初识函数

函数到底是个什么东西?

函数,可以当做是一大堆功能代码的集合。

这是他的定义方式:

def 函数名():
    函数内编写代码
    ...
    ...
    
函数名()

例如:

# 定义名字叫info的函数
def info():
    print("第一行")
    print("第二行")
    print("第n行...")
    
info()

什么时候会用到函数?

什么时候会用到函数呢?一般在项目开发中有会有两种应用场景:

  • 有重复代码,用函数增加代码的重用性。
def send_email():
    # 10行代码

print("欢迎使用计算机监控系统")
if CPU占用率 > 90%:
    send_email()

if 硬盘使用率 > 99%:
    send_email()
    
if 内存使用率 > 98%:
    send_email()
...

  • 代码太长,用函数增强代码的可读性。每一个函数都代表一个功能
def calculate_same_num_rule():
    """判断是否是豹子"""
    pass

def calculate_same_color_rule():
    """判断是否是同花"""
    pass

def calculate_straight_rule():
    """判断是否顺子"""
	pass

def calculate_double_card_rule(poke_list):
    """判断是否对子"""
	pass

def calculate_single_card_rule():
    """判断是否单牌"""
    pass

以前我们变成是按照业务逻辑从上到下逐步完成,称为:面向过程编程;现在学了函数之后,利用函数编程称为:函数式编程。

调用方式: 函数名 + ()  :例如:fn()

2.2. 函数的参数

现在有个需求就是给其他人发送邮件:

def send_email():
    """给x人发送邮件"""
    print('给你发邮件啦!')


send_email()

你使用这个函数发送了邮件,但是并不知道是在给谁发邮件

那么需求来了,需求:根据上述代码实现给3个用户发邮件。

v1 = "4236362408@qq.com"
v2 = "4464662509@qq.com"
v3 = "jiaoxingk@live.com"

这个时候就需要指定人了:

def send_email(name):
    """给x人发送邮件"""

    print(f'给{name}发邮件啦!')

v1 = "4236362408@qq.com"
v2 = "4464662509@qq.com"

send_email(v1)
send_email(v2)


#
给4236362408@qq.com发邮件啦!
给4464662509@qq.com发邮件啦!

2.2.1. 参数

在定义函数时,如果在括号中添加变量,我们称它为函数的形式参数:

# ###### 定义有三个参数的函数(a1/a2/a3一般称为形式参数-形参) #####
def func(a1,a2,a3):
    print(a1+a2+a3)

# 执行函数并传入参数(执行函数传值时一般称为实际参数-实参)
func(11,22,33)

# 执行函数并传入参数
func(9,2,103)

  • 位置传参

按照从左往右进行依次传参,如果多了或者少了都会报错哟

def add(n1,n2):
    print(n1+n2)
    
add(1,22)

  • 关键字传参

直接指定形参名

def add(n1,n2):
    print(n1+n2)
    
add(n1=1,n2=22)

"""
1. 形参
2. 实参
3. 位置传参
4. 关键字传参
"""


# ###### 定义有三个参数的函数(a1/a2/a3一般称为形式参数-形参) #####
def func(a1, a2, a3):
    print(a1 + a2 + a3)


# 执行函数并传入参数(执行函数传值时一般称为实际参数-实参)
func(11, 22, 33)

# 执行函数并传入参数
func(9, 2, 103)

# 执行函数
func(a1=99, a2=88, a3=1)
func(a1=99, a3=1, a2=88)

2.2.2. 默认参数

加一个默认值,混合传参时,要放在后面

def func(a1, a2, a3=10):
    print(a1 + a2 + a3)


# 位置传参
func(8, 19)
func(1, 2, 99)

# 关键字传参(位置和关键混合时,关键字传参要在后面)
func(12, 9, a3=90)
func(12, a2=9, a3=90)
func(a1=12, a2=9, a3=90)

2.2.3. 动态参数

  • *  (可以传任意位置参数)
def func(*args):
    print(args) # 元组类型 (22,)   (22,33,99,) ()

# 只能按照位置传参
func(22)
func(22,33)
func(22,33,99)
func()
  • **  (可以传任意关键字参数)
def func(**kwargs):
    print(kwargs) # 字典类型 {"n1":"jiaoxingk"}    {"n1":"jiaoxingk","age":"18","email":"xxxx"}  {}
    
# 只能按关键字传参
func(n1="jiaoxingk")
func(n1="jiaoxingk",age=18)
func(n1="jiaoxingk",age=18,email="xx@live.com")

  • *,**  (位置参数和关键字参数混合,关键字参数要在后面才行,不然就会出现匹配不到的情况)
def func(*args,**kwargs):
    print(args,kwargs) # (22,33,99) {}

func(22,33,99)
func(n1="jiaoxingk",age=18)
func(22,33,99,n1="jiaoxingk",age=18)
func()

注意点:

# 1. ** 必须放在 * 的后面
def func1(*args, **kwargs):
    print(args, **kwargs)


# 2. 参数和动态参数混合时,动态参数只能放在最后。
def func2(a1, a2, a3, *args, **kwargs):
    print(a1, a2, a3, args, **kwargs)


# 3. 默认值参数和动态参数同时存在
def func3(a1, a2, a3, a4=10, *args, a5=20, **kwargs):
    print(a1, a2, a3, a4, a5, args, kwargs)


func3(11, 22, 33, 44, 55, 66, 77, a5=10, a10=123)

2.3. 函数返回值

在开发过程中,我们希望函数可以帮助我们实现某个功能,但让函数实现某功能之后有时也需要有一些结果需要反馈给我们,例如:

def func():
    return 666

res = func()
print(res) # 666

在了解了返回值的基本使用之后,接下来在学3个关键知识:

  • 返回值可以是任意类型,如果函数中没写return,则默认返回None
def func():
    return [1,True,(11,22,33)]

result = func()
print(result)   # [1,True,(11,22,33)]


当在函数中未写返回值returnreturn None ,执行函数获取的返回值都是None。

  • return后面的值如果有逗号,则默认会将返回值转换成元组再返回。
def func():
    return 1,2,3

value = func()
print(value) # (1,2,3)

  • 函数一旦遇到return就会立即退出函数(终止函数中的所有代码)
def func():
    print(1)
    return "结束吧"
	print(2)   # 不会执行
    
ret = func()
print(ret)  # "结束吧"

3. 函数高级

3.1. 参数的补充

在函数基础部分,我们掌握函数和参数基础知识,掌握这些其实完全就可以进行项目的开发。

3.2. 参数内存地址相关

在开始开始讲参数内存地址相关之前,我们先来学习一个技能:

如果想要查看下某个值的在内存中的地址?

v1 = "jiaoxingk"
addr = id(v1)

print(addr) # 140691049514160

记住一句话:函数执行传参时,传递的是内存地址。

def func(data):
    print(data, id(data))  # jiaoxingk  140247057684592


v1 = "jiaoxingk"
print(id(v1))  # 140247057684592

func(v1)

Python参数的这一特性有两个好处:

  • 节省内存
  • 对于可变类型且函数中修改元素的内容,所有的地方都会修改。可变类型:列表、字典、集合。
# 可变类型 & 修改内部修改
def func(data):
    data.append(999)
    
v1 = [11,22,33]
func(v1)

print(v1) # [11,22,33,666]

其他很多编程语言执行函数时,默认传参时会将数据重新拷贝一份,会浪费内存。

当然,如果你不想让外部的变量和函数内部参数的变量一致,也可以选择将外部值拷贝一份,再传给函数。

import copy


# 可变类型 & 修改内部修改
def func(data):
    data.append(999)


v1 = [11, 22, 33]
new_v1 = copy.deepcopy(v1) # 拷贝一份数据(深拷贝,全部拷贝到一个新地址)
func(new_v1)

print(v1)  # [11,22,33]

3.2.1. 函数的返回值是内存地址

def func():
    data = [11, 22, 33]
    return data

v1 = func()
print(v1) # [11,22,33]

上述代码的执行过程:

  • 执行func函数
  • data = [11, 22, 33] 创建一块内存区域,内部存储[11,22,33],data变量指向这块内存地址。
  • return data 返回data指向的内存地址
  • v1接收返回值,所以 v1 和 data 都指向  [11,22,33] 的内存地址(两个变量指向此内存,引用计数器为2)
  • 由函数执行完毕之后,函数内部的变量都会被释放。(即:删除data变量,内存地址的引用计数器-1)

所以,最终v1指向的函数内部创建的那块内存地址。

3.3. 函数和函数名

函数名其实就是一个变量,这个变量只不过代指的函数而已。

name = "jiaoxingk"

def add(n1,n2):
    return n1 + n2

注意:函数必须先定义才能被调用执行(解释型语言)。

# 正确
def add(n1,n2):
    return n1 + n2

ret = add(1,2)
print(ret)

# 错误
ret = add(1,2)
print(ret) 

def add(n1,n2):
    return n1 + n2

3.3.1. 函数做元素

既然函数就相当于是一个变量,那么在列表等元素中是否可以把行数当做元素呢?

def func():
    return 123

data_list = ["jiaoxingk", "func", func , func() ]

print( data_list[0] ) # 字符串"jiaoxingk"
print( data_list[1] ) # 字符串 "func"
print( data_list[2] ) # 函数 func
print( data_list[3] ) # 整数 123

res = data_list[2]()
print( res ) # 执行函数 func,并获取返回值;print再输出返回值。

print( data_list[2]() ) # 123

注意:函数同时也可被哈希,所以函数名也可以当做 集合的元素、字典的键。

掌握这个知识之后,对后续的项目开发有很大的帮助

3.3.2. 函数名赋值

  • 将函数名赋值给其他变量,函数名其实就个变量,代指某函数;如果将函数名赋值给另外一个变量,则此变量也会代指该函数,例如:
def func(a1,a2):
    print(a1,a2)

xxxxx = func

# 此时,xxxxx和func都代指上面的那个函数,所以都可以被执行。
func(1,1)
xxxxx(2,2)

  • 对函数名重新赋值,如果将函数名修改为其他值,函数名便不再代指函数,例如:
def func(a1,a2):
    print(a1,a2)

# 执行func函数
func(11,22)

# func重新赋值成一个字符串
func = "jiaoxingk"

print(func)


注意:由于函数名被重新定义之后,就会变量新被定义的值,所以大家在自定义函数时,不要与python内置的函数同名,否则会覆盖内置函数的功能,例如:

3.3.3. 函数名做参数和返回值

函数名其实就一个变量,代指某个函数,所以,他和其他的数据类型一样,也可以当做函数的参数和返回值。

  • 参数
def plus(num):
    return num + 100

def handler(func):
    res = func(10) # 110
    msg = "执行func,并获取到的结果为:{}".format(res)
    print(msg) # 执行func,并获取到的结果为:110
   
# 执行handler函数,将plus作为参数传递给handler的形式参数func
handler(plus)

  • 返回值
def plus(num):
    return num + 100

def handler():
	print("执行handler函数")
    return plus
    
result = handler()
data = result(20) # 120
print(data)

3.4. 返回值和print

对于初学者的同学,很多人都对print和返回值分不清楚,例如:

def add(n1,n2):
    print(n1 + n2)

v1 = add(1,3)
print(v1)

# 输出
4
None



def plus(a1,a2):
    return a1 + a2

v2 = plus(1,2)
print(v2)
# 输出
3

这两个函数是完全不同的

  • 在函数中使用print,只是用于在某个位置输出内容而已。
  • 在函数中使用return,是为了将函数的执行结果返回给调用者,以便于后续其他操作。

在调用并执行函数时,要学会分析函数的执行步骤。

def f1():
    print(123)


def f2(arg):
    ret = arg()
    return ret


v1 = f2(f1)
print(v1)

# 输出
123
None

def f1():
    print(123)


def f2(arg):
    ret = arg()
    return f1


v1 = f2(f1)

v2 = v1()
print(v2)

# 输出
123
123
None

3.5. 作用域

作用域,可以理解为一块空间,这块空间的数据是可以共享的

大概意思就是,只要你在这一片草地,你就可以获得这片草地的资源

3.5.1. 函数为作用域

Python以函数为作用域,所以在函数内创建的所有数据,可以此函数中被使用,无法在其他函数中被使用。

def func():
    name = "jiaoxingk"
    data_list = [11,22,33,44]
    print(name,data_list)
    age = 20
    print(age)

def handler():
    age = 18
    print(age)

func()
handler()

3.5.2. 全局和局部

Python中以函数为作用域,函数的作用域其实是一个局部作用域。

# 全局变量(变量名大写)
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]

def download():
    # 局部变量
    url = "http://www.xxx.com"
    ...
    
def upload():
    file_name = "rose.zip"
    ...

COUNTRYCITY_LIST是在全局作用域中,全局作用域中创建的变量称之为【全局变量】,可以在全局作用域中被使用,也可以在其局部作用域中被使用。

downloadupload函数内部维护的就是一个局部作用域,在各自函数内部创建变量称之为【局部变量】,且局部变量只能在此作用域中被使用。局部作用域中想使用某个变量时,寻找的顺序为:优先在局部作用域中寻找,如果没有则去上级作用域中寻找

注意:全局变量一般都是大写。

3.5.3. global关键字

默认情况下,在局部作用域对全局变量只能进行:读取和修改内部元素(可变类型),无法对全局变量进行重新赋值。

  • 读取
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]

def download():
    url = "http://www.xxx.com"
    print(COUNTRY)
    print(CITY_LIST)
    
download()

  • 修改内部元素(可变类型)
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]

def download():
    url = "http://www.xxx.com"
    print(CITY_LIST)
    
    CITY_LIST.append("广州")
    CITY_LIST[0] = "南京"
    print(CITY_LIST)
    
download()

  • 无法对全局变量重新赋值
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]

def download():
    url = "http://www.xxx.com"
    # 不是对全部变量赋值,而是在局部作用域中又创建了一个局部变量 CITY_LIST 。
    CITY_LIST =  ["河北","河南","山西"]
    print(CITY_LIST)

def upload():
    file_name = "rose.zip"
    print(COUNTRY)
    print(CITY_LIST)
    
download()
upload()

如果想要在局部作用域中对全局变量重新赋值,则可以基于 global关键字实现,例如:

COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]

def download():
    url = "http://www.xxx.com"
	
    global CITY_LIST
    CITY_LIST =  ["河北","河南","山西"]
    print(CITY_LIST)
    
    global COUNTRY
    COUNTRY = "中华人民共和国"
    print(COUNTRY)

def upload():
    file_name = "rose.zip"
    print(COUNTRY)
    print(CITY_LIST)
    
download()
upload()

以下内容属于高级内容,重在理解

3.8. 函数嵌套

Python中以函数为作用域,在作用域中定义的相关数据只能被当前作用域或子作用域使用。

NAME = "jiaoxingk"
print(NAME)

def func():
    print(NAME)

func()

3.8.1. 函数在作用域中

其实,函数也是定义在作用域中的数据,在执行函数时候,也同样遵循:优先在自己作用域中寻找,没有则向上一接作用域寻找,例如:

# 1. 在全局作用域定义了函数func
def func():
    print("你好")
    
# 2. 在全局作用域找到func函数并执行。
func()


# 3.在全局作用域定义了execute函数
def execute():
    print("开始")
    # 优先在当前函数作用域找func函数,没有则向上级作用域中寻找。
    func()
    print("结束")

# 4.在全局作用域执行execute函数
execute()

3.8.2. 函数嵌套定义

上述示例中的函数均定义在全局作用域,其实函数也可以定义在局部作用域,这样函数被局部作用域和其子作用于中调用(函数的嵌套)。

def func():
    print("hello")
    
def handler():
    print("word")
    def inner():
        print("haha")
	inner()
    func()
    print("diaomao")

handler()


# 
word
hello
diaomao

到现在你会发现,只要理解数据定义时所存在的作用域,并根据从上到下代码执行过程进行分析,再怎么嵌套都可以搞定。

现在的你可能有疑问:为什么要这么嵌套定义?把函数都定义在全局不好吗?

其实,大多数情况下我们都会将函数定义在全局,不会嵌套着定义函数。但是当我们需要把一个函数拆解成多个功能的时候,就需要在内部定义函数,这样更加方便

3.8.3. 作用域

三句话搞定作用域:

  • 优先在自己的作用域找,自己没有就去上级作用域。
  • 在作用域中寻找值时,要确保此次此刻值是什么。
  • 分析函数的执行,并确定函数作用域链。(函数嵌套)

3.9. 闭包

闭包,简而言之就是将数据封装在一个包(区域)中,使用时再去里面取。(本质上 闭包是基于函数嵌套搞出来一个中特殊嵌套)

示例:

def task(arg):
    def inner():
        print(arg)
    return inner

v1 = task(11)
v2 = task(22)
v3 = task(33)
v1()
v2()
v3()

# 
11
22
33

3.10. 装饰器

现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入 "before" 和 "after"。

def func():
    print("我是func函数")
    value = (11,22,33,44) 
    return value
    
result = func()
print(result)

实现思路:

这就是利用了闭包 + 函数作为参数传递的性质,仔细分析这段代码,先返回了inner函数名,然后通过调用inner函数,执行了origin函数,这个origin就是传递进来的参数func

def func():
    print("我是func函数")
    value = (11, 22, 33, 44)
    return value


def outer(origin):
    def inner():
        print('inner')
        origin()
        print("after")

    return inner

func = outer(func)
result = func()

处理返回值:

def func():
    print("我是func函数")
    value = (11, 22, 33, 44)
    return value


def outer(origin):
    def inner():
        print('inner')
        res = origin()
        print("after")
        return res
    return inner

func = outer(func)
result = func()

在Python中有个一个特殊的语法糖:

看上去会比较难懂,让我给你一步一步分析:

  • outer函数是一个闭包函数,需要传递origin作为参数,可以看到origin就是一个需要传递的函数
  • func函数正常编写,在func函数上面写一个语法糖(特殊符号)@outer(闭包函数),大概意思就是将当前func函数当做参数传递给outer
  • 最后执行func函数,其实就是先执行了outer,然后在内部执行了func函数
def outer(origin):
    def inner():
        print('inner')
        res = origin()
        print("after")
        return res
    return inner


@outer
def func():
    print("我是func函数")
    value = (11, 22, 33, 44)
    return value


func()

装饰器,在不修改原函数内容的前提下,通过@函数可以实现在函数前后自定义执行一些功能(批量操作会更有意义)。

3.10.1. 优化

优化以支持多个参数的情况。

def outer(origin):
    def inner(*args, **kwargs):
        print("before 110")
        res = origin(*args, **kwargs)  # 调用原来的func函数
        print("after")
        return res

    return inner


@outer  # func1 = outer(func1)
def func1(a1):
    print("我是func1函数")
    value = (11, 22, 33, 44)
    return value



func1(1)

其中,我的那种写法就称为装饰器。

  • 实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。
  • 实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。
  • 适用场景:多个函数系统统一在 执行前后自定义一些功能。
  • 装饰器示例
def outer(origin):
    def inner(*args, **kwargs):
		# 执行前
        res = origin(*args, **kwargs)  # 调用原来的func函数
        # 执行后
        return res
    return inner


@outer
def func():
    pass

func()

3.10.2. 应用场景

可以来看一个应用场景

在以后编写一个网站时,如果项目共有100个页面,其中99个是需要登录成功之后才有权限访问,就可以基于装饰器来实现。

基于装饰器实现的伪代码:

def auth(func):
    def inner(*args, **kwargs):
        # 在此处,判断如果用户是否已经登录,已登录则继续往下,未登录则自动跳转到登录页面。
        return func(*args, **kwargs)

    return inner


@auth
def index():
    return "首页"


@auth
def info():
    return "用户中心"


@auth
def order():
    return "订单中心"


def login():
    return "登录页面"

3.10.3. functools

你会发现装饰器实际上就是将原函数更改为其他的函数,然后再此函数中再去调用原函数。

def handler():
    pass

handler()
print(handler.__name__) # handler

def auth(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@auth
def handler():
    pass

handler()
print(handler.__name__) # inner

import functools

def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@auth
def handler():
    pass

handler()
print(handler.__name__)  # handler

其实,一般情况下大家不用functools也可以实现装饰器的基本功能

但在以后做项目的额时候,会发现,不加functools会出错(内部会读取__name__,如果__name__重名的话就报错)

import functools


def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)  # 执行原函数
        return res

    return inner

结尾

这个系列会一直做下去,从Python基础一直到Web开发、自动化、数据分析、爬虫及其机器学习与算法相关。大部分都是平时所学的笔记加上自己的见解。所以更加适合有相关编程基础但不想重新跟着课程学的同学,文章涵盖了基础知识点,至于一些细节可能没讲到位,但是以后也会分享相关项目实战的内容,从而巩固编码能力。

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jiaoxingk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值