XX学Python·高级语法

闭包和装饰器

  • 当返回的内部函数使用了外部函数的变量就形成了闭包,闭包可对外部函数变量进行保存
# 使用闭包的格式
def out_func():
    print('外部函数调用')
    a = 22

    def inner_func():
        print('内部函数调用')
        b = 20
        print(a + b)  # 内部函数使用了外部函数的变量或参数

    return inner_func  # 外部函数需将内部函数名进行返回,返回的内部函数就是闭包实例


f = out_func()  # f接收外部函数返回值,即inner_func
print(f)  # 所以打印结果是函数
f()  # 调用执行inner_func

# 闭包参数使用实例
def who(name):
    def do_sth(action):
        print(name + action)

    return do_sth


p = who('xx')
p('在唱歌')
p = who('lsl')
p('在跳舞')

# 函数的参数可以是普通变量,也可以是函数名、对象
class Stu():
    def study_IT(self):
        print('学生开始学习IT课程')


student1 = Stu()


def func1():
    print('func1被执行')


def func2(a, ob):
    # a参数接收一个函数,ob参数接收一个对象
    print('func2被执行')
    a()
    ob.study_IT()


func2(func1, student1)
  • 装饰器在不改变原有业务代码基础上(调用也不能修改),给函数增加新的业务逻辑
  • 装饰器本质就是一个闭包,把要装饰的函数当作参传给闭包
# 需求:不改变原代码和调用,增加判断是否登录,再显示信息
def out(f):
    def inner():
        print('判断用户是否登录')
        f()
    return inner


# 装饰器语法糖@out写法,等价于a = out(show_use)再调用a()
# a接收out函数返回值inner,a就是内部函数
@out
def show_use():
    print('shoe_use执行')
    print('显示用户信息')


show_use()
# 不同类型参数分开传递
def func1(f):  # 外部参数负责接收函数
    def func2(n1, n2):  # 内部参数负责接收要计算的数据
        print('正在计算,结果为:')
        f(n1, n2)
    return func2

@func1
def add1(num1, num2):
    print(num1 + num2)


add1(2, 5)
  • 装饰器不定长参数的使用
def func1(f):
    def func2(*args):
        print(args)  # (2, 5, 8)
        print(*args)  # 2 5 8
        print('正在乘法计算,结果为:')
        f(*args)

    return func2


@func1
def mul1(num1, num2, num3):
    print(num1 * num2 * num3)


li1 = [2, 5, 8]  # 在函数外得到一个list或tuple,想调用一个可变参数,用*+变量名调用,
mul1(*li1)  # *li1等价于mul1(2,5,8)
def func1(f):
    def func2(**kwargs):
        print(kwargs)  # {'num1': 2, 'num2': 5, 'num3': 8}
        print(*kwargs)  # num1 num2 num3,得到的字典的key
        print('正在乘法计算,结果为:')
        f(kwargs['num1'], kwargs['num2'], kwargs['num3'])

    return func2


@func1
def mul1(num1, num2, num3):
    print(num1 * num2 * num3)


data = {'num1': 2, 'num2': 5, 'num3': 8}
mul1(**data)
  • 通用装饰器基本使用
# 登录判断 需求:展示用户信息,购买等操作前进行登录判断
def jud(f):
    def log_jud(*args, **kwargs):  # 借助不定长参数
        print('登录判断')
        f(*args, **kwargs)

    return log_jud


@jud
def show_user(name, age):
    print('显示用户信息')
    print(name, age)


@jud
def buy():
    print('购买商品')


show_user('lsl', 19)
show_user(name='xx', age=18)
buy()
  • 通用装饰器日志写入操作
def func1(f):
    def func2(*args, **kwargs):
        print('日志写入')
        try:
            f(*args, **kwargs)  # 如果装饰的目标函数执行有错误
        except Exception as e:  # 就把错误写入日志
            print(e)  # division by zero 但类型不是字符串
            print(type(e))  # <class 'ZeroDivisionError'>
            print(str(e))  # division by zero 转为了字符串
            file = open('err.log', 'a')  # 以追加模式打开
            file.write(str(e))  # 转为字符串才能写入
            file.close()

    return func2


@func1
def func3():
    1/0


func3()
  • 装饰器返回值的传递
# 添加输出日志的功能
def logging(fn):
    def inner(num1, num2):
        print("--正在努力计算--")
        sum1 = fn(num1, num2)
        return sum1

    return inner


# 使用装饰器装饰函数
@logging
def sum_num(a, b):
    sum1 = a + b
    return sum1


result = sum_num(1, 2)
print(result) 

with语句文件操作

  • 用于简化资源释放的操作
# f = open('test.log', 'wb')
# try:
#     f.write('aaa')
# except:
#     print('文件写入失败')
# finally:
#     f.close()

# with实现文件读写,无论是否读写成功都会关闭文件
with open('test.log', 'w') as f:
    f.write('aaa')

深拷贝和浅拷贝

  • 不可变类型的浅拷贝copy不会开辟新空间,只是拷贝了对象的引用,操作同一空间数据
  • 可变类型浅拷贝只对可变类型的第一层对象进行拷贝,对拷贝对象开辟新内存空间进行存储,子对象不进行拷贝。所以有多层数据嵌套,修改拷贝后的嵌套数据会影响原始嵌套的数据。
  • 使用深拷贝deepcopy解决浅拷贝多层数据嵌套的问题
    • 不可变类型深拷贝若子对象没有可变类型则不会进行拷贝,只是拷贝了对象的引用;否则会对该对象到最后一个可变类型的每一层对象进行拷贝, 对每一层拷贝的对象都会开辟新内存空间进行存储。
    • 可变类型深拷贝会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。
import copy
# 元组里有可变类型对象,发现有可变类型就会该对象到最后一个可变类型的每一层对象进行拷贝
a4 = (1, ["李四"])
b4 = copy.deepcopy(a4)
# 查看内存地址
print(id(a4))
print(id(b4))  # 两个id不同
# 元组里面的可变类型子对象也会进行拷贝
print(id(a4[1]))
print(id(b4[1]))  # 两个id不同
print(id(a4[0]))
print(id(b4[0]))  # 两个id相同

正则表达式

  • 匹配符合某些规则的字符串数据,基本使用格式:
import re  # 导入re模块

# 使用match方法进行匹配操作,从头开始匹配字符串数据
result = re.match(正则表达式,要匹配的字符串)

# 如果上一步匹配到数据的话,可使用group方法来提取数据
result.group()
  • 匹配单个字符

    • . 匹配任意1个字符(除了\n)

    • [] 匹配[ ]中列举的1个字符

    • \d 匹配一个数字,即0-9

    • \D 匹配一个非数字

    • \s 匹配一个空白字符,即空格,tab键

    • \S 匹配一个非空白字符

    • \w 匹配一个非特殊字符,即a-z、A-Z、0-9、_、汉字

    • \W 匹配一个特殊字符,即非字母、非数字、非汉字

  • 匹配多个字符

    • *匹配前一个字符出现0次或无限次,即可有可无

    • +匹配前一个字符出现1次或无限次,即至少有1次

    • ?匹配前一个字符出现1次或者0次,即要么有1次,要么没有

    • {m}匹配前一个字符出现m次

    • {m,n}匹配前一个字符出现从m到n次

import re
a = ' 中1#zhangsan@163.com'
b = 'lisi@qq.com'
c = '010-9188912'
d = 'http://www.baidu.com'
e = '<a> 百度 </a>'

# 单个字符匹配的表达式
# . 符号表达式,表示匹配任意字符
res = re.match('.....', e)
try:
    print(f'. 匹配任意字符数据:{res.group()}')
except:
    print('未匹配到数据')

# [] 范围指定
res = re.match('[a-z 0-9][a-z 0-9]', b)
try:
    print(f'[] 范围匹配数据:{res.group()}')
except:
    print('未匹配到数据')

# \d 匹配数字  \D 匹配非数字
res = re.match('\d\d', c)
try:
    print(f'\d 数字匹配数据:{res.group()}')
except:
    print('未匹配到数据')

# \S 非空格数据  \s匹配空格
res = re.match('\s\S\S\S',a)
# 获取匹配的数据内容
try:
    print(f'\S 字母匹配数据:{res.group()}')
except:
    print('未匹配到数据')

# 匹配多个字符数据
# * 匹配0个或多个数据 * 匹配不到数据会把数据赋值为空
res = re.match('\d*',c)
try:
    print(res.group())
except:
    print('未匹配到数据')

# + 匹配1个或多个数据 +数据匹配不到抛出异常
res = re.match('\d+',b)
try:
    print(res.group())
except:
    print('未匹配到数据')
posted @ 2022-10-04 23:06  PORTB  阅读(19)  评论(0编辑  收藏  举报