python05 - 其他基本操作

python其他基本操作

一:推导式

1:列表推导式

[表达式 for 变量 in 列表] 
[out_exp_res for out_exp in input_list]

或者 

[表达式 for 变量 in 列表 if 条件]
[out_exp_res for out_exp in input_list if condition]
names = ['Bob','Tom','alice','Jerry','Wendy','Smith']
new_names = [name.upper() for name in names if len(name) > 3]

# 上述内容等价于
names = ['Bob','Tom','alice','Jerry','Wendy','Smith']
new_names = []
for name in names:
    if len(name) > 3:
        new_names.append(name.upper())

# ============== 或者用于生成指定的数列 ==============
numbers = [i for i in range(30) if i % 2 == 0] # 取出30内所有的偶数

2:字典推导式

{ key_expr: value_expr for value in collection }{ key_expr: value_expr for value in collection if condition }
dic = {x + 1 : x**2 for x in range(3)} # x的范围是[0, 3), key的策略是x + 1, value的策略是x^2
print(dic) # {1:0, 2:1, 3:4}

names = ["张三风", "李四光", "王五", "赵六", "哈", "zhangsan"]
name_dic = {name : len(name) for name in names if len(name) > 1} # {'张三风': 3, '李四光': 3, '王五': 2, '赵六': 2, 'zhangsan': 8}

3:集合推导式

{ expression for item in Sequence }{ expression for item in Sequence if conditional }
a = {x for x in 'abracadabra' if x not in 'abc'}
print(a)  # {'d', 'r'}

4:元组推导式

元组不能直接生成,需要借助生成器表达式,然后调用tuple()生成元组

(expression for item in Sequence )(expression for item in Sequence if conditional )
a = (x for x in range(1,10) if x > 3)
tuple(a)

二:生成器

生成器一定是迭代器,迭代器一定是可迭代对象,可迭代对象通过iter之后编程迭代器

yield是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。

当在生成器函数中使用 yield 语句时,函数的执行将会暂停,并将 yield 后面的表达式作为当前迭代的值返回。

每次调用生成器的 next() 方法或使用 for 循环进行迭代时,函数会从上次暂停的地方继续执行,直到再次遇到 yield 语句。这样,生成器函数可以逐步产生值,而不需要一次性计算并返回所有结果。

调用一个生成器函数,返回的是一个迭代器对象。

def test(flag):
    print("start")
    if flag:
        print("flag is true")
        yield 666
        print("yield over")
    print("bye bye")
    print("over")

如果执行函数的时候不经过yield所属的代码块,然后如果此时调用next方法,就会抛出StopIteration异常

if __name__ == "__main__":
    ans = test(flag=False) # start, bye bye over
    next(ans)  # 此时就会抛出StopIteration异常

如果执行函数的时候经过yield所属的代码块,此时如果调用next方法,程序就会卡在yield行,直到下次调用使用next的时候才能从该位置开始继续向下执行。

如果执行到函数的结束,就会抛出StopIteration异常

  • 不管是使用了return语句显式地返回值,或者默认返回None值,返回值都只能作为异常的值一并抛出
  • 此时的生成器对象处于结束的状态
  • 对于已经结束的生成器对象再次调用next(),直接抛出StopIteration异常,并且不含返回值
import os


def test(flag):
    print("start")
    if flag:
        print("flag is true")
        yield 666
        print("yield over")
    print("bye bye")
    print("over")


if __name__ == "__main__":
    ans = test(flag=True)  # start & flag is true
    number = next(ans)  # 666
    print(number)
    # 程序会卡在yield这一行,直到下一个next()。才会从此处往下执行,直到遇到下一个yield或者程序结束
    number = next(ans)  # yield over & bye bye & over
    print(number)  # StopIteration

当然这种使用一次yield的情况没什么用,一般这种生成器用在循环生成的情况

import sys


def fibonacci(n):  # 生成器函数 - 斐波那契
    """
    生成斐波那契数列的生成器函数。

    参数:
    n: int - 生成斐波那契数列的最大长度限制

    返回:
    该函数是一个生成器,会依次生成斐波那契数列中的元素,直到达到指定长度n。
    """
    a, b, counter = 0, 1, 0  # 初始化斐波那契数列的前两个值和计数器
    while True:
        if (counter > n):  # 当计数器超过指定长度n时,结束生成
            return
        yield a  # 生成当前斐波那契数列的值
        a, b = b, a + b  # 更新斐波那契数列的值
        counter += 1  # 计数器增加


f = fibonacci(10)  # f 是一个迭代器,由生成器返回生成

while True:
    try:
        print(next(f), end=" ")
    except StopIteration: # StopIteration异常的处理,程序结束处理
        sys.exit()

三:lambda匿名函数

1:lambda概述

lambda 函数是一种小型、匿名的、内联函数,它可以具有任意数量的参数,但只能有一个表达式。

匿名函数不需要使用 def 关键字定义完整函数。

lambda 函数通常用于编写简单的、单行的函数,通常在需要函数作为参数传递的情况下使用,例如在 map()、filter()、reduce() 等函数中。

lambda 函数特点:

  • lambda 函数是匿名的,它们没有函数名称,只能通过赋值给变量或作为参数传递给其他函数来使用。
  • lambda 函数通常只包含一行代码,这使得它们适用于编写简单的函数。

lambda 语法格式:

'''
- `lambda`是 Python 的关键字,用于定义 lambda 函数。
- `arguments` 是参数列表,可以包含零个或多个参数,但必须在冒号(`:`)前指定。
- `expression` 是一个表达式,用于计算并返回函数的结果。
'''
lambda arguments: expression

2:三种情况

在这里插入图片描述

3:使用场景

lambda 函数通常与内置函数如 map()、filter() 和 reduce() 一起使用,以便在集合上执行操作

numbers = [1, 2, 3, 4, 5]
# map(每一个元素的操作,列表元素)
squared = list(map(lambda x: x**2, numbers)) # 对于numbers的每一个数都进行平方操作
print(squared)  # 输出: [1, 4, 9, 16, 25]


numbers = [1, 2, 3, 4, 5, 6, 7, 8]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)  # 输出:[2, 4, 6, 8]
from functools import reduce
 
numbers = [1, 2, 3, 4, 5]
 
# 使用 reduce() 和 lambda 函数计算乘积
# 1 * 2 * 3 * 4 * 5 = 120
product = reduce(lambda x, y: x * y, numbers)
 
print(product)  # 输出:120

四:装饰器

可以类比于Java的代理模式,可以在原函数前后增加功能 -> 类比于AOP的@After & @Before

装饰器是一种函数,它接受一个函数作为参数,并返回一个新的函数或修改原来的函数。

在这里插入图片描述

装饰器的语法使用 @decorator_name 来应用在函数或方法上。

Python 还提供了一些内置的装饰器,比如 @staticmethod 和 @classmethod,用于定义静态方法和类方法。

装饰器的应用场景:

  • 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
  • 性能分析: 可以使用装饰器来测量函数的执行时间。
  • 权限控制: 装饰器可用于限制对某些函数的访问权限。
  • 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。

1:简易版本

# 第一种:直接使用函数对象来实现
import time
 
print(time.time()) # 1685333745.048791 #1685333745叫时间戳:它是从 1970 年- 1-1 开始,到当前时间的秒数
 
def index():
    time.sleep(3)  # 2.调用 time.sleep(3) 函数,暂停程序的执行 3 秒钟。
    print('Welcome to the index page')  # 3.打印一条欢迎消息 "Welcome to the index page"。
    return 200  # 4.返回状态码 200。
 

res = index  # 1.执行 index() 函数。
# 5.函数执行完毕,程序继续往下执行。
 
"""如果不懂函数体的代码该如何计算程序执行了多少秒呢"""
 
start_time = time.time()  # 函数未执行前起始计算时间
res()  # 函数执行
stop_time = time.time()  # 函数执行之后结束时间
print('run time is %s' % (stop_time - start_time))#结束时间减去开始时间等于函数执行时间
"""考虑到还有可能要统计其他函数的执行时间,所以还要使用函数参数的形式"""
 
# *******************************************************************************************************************
 
# 第二种:使用参数的形式传入
def wrapper(func):  # 通过函数接收外部的值
    start_time = time.time()
    func()
    stop_time = time.time()
    print('run time is %s' % (stop_time - start_time))
 
 
wrapper(index)  # 如果考虑到还有可能要统计其他函数的执行时间就在下方继续wrapper(其他函数)
"""但是使用参数的话就改变了调用方式,违反了不能修改被装饰对象调用方式的原则,所以我们使用闭包函数,将值报给函数"""
 
# *******************************************************************************************************************
# 第三种:使用闭包函数的形式
def timer(func):
    def wrapper():
        start_time = time.time()
        res = func()
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
        return res
 
    return wrapper
 

asd = timer(index)
asd()
 
"""以上三种方法我们就实现了无参装饰器timer,可以在不修改被装饰对象index源代码和调用方式的前提下为其加上新功能。但我们忽略了若被装饰的函数是一个有参函数,便会抛出异常"""
 
 
 
def home(name):
    time.sleep(5)
    print('Welcome to the home page', name)
 
 
home = timer(home)
home('egon')
 
# 抛出异常:TypeError: wrapper() takes 0 positional arguments but 1 was given
""" 因为home(‘egon’)调用的其实是wrapper(‘egon’),而函数wrapper没有参数。wrapper函数接收的参数其实是给最原始的func用的,为了能满足被装饰函数参数的所有情况,便用上*args+**kwargs组合,于是修正装饰器timer如下 """
# 第四种:装饰器的进阶版本解决参数问题
 
def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
        return res
 
    return wrapper

2:使用语法糖实现

更像Java的注解了

import time
def timer(func): # 定义一个名为 timer() 的装饰器函数,它接受一个参数 func这个 func 就是即将要被修饰的函数
    def wrapper(*args, **kwargs): # 在 timer() 函数内部,定义一个名为 wrapper() 的闭包函数
        start_time = time.time() # 在调用 wrapper() 函数时,它会根据传入的参数来调用被修饰的函数 func,并在函数执行前后记录时间
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
        return res # 同时输出函数执行时间。最后,它将 func 函数的返回值返回
 
    return wrapper
 

@timer # 使用装饰器语法来对函数进行装饰,在函数定义前加上 @timer 这样的语句
def index():
    time.sleep(1)
    print('Welcome to the index page')
    return 200
 
index()#调用 index() 函数时,会自动将其转换成 timer(index)() 这样的形式,将 index 函数作为参数传递给 timer() 函数,并将返回值再次作为函数调用。由于 timer() 函数返回了一个闭包函数 wrapper(),所以最终的函数调用结果就是执行了闭包函数 wrapper()
 
"""在 index() 函数内部,我们调用了 time.sleep(1) 函数来模拟函数执行的耗时操作。然后,我们输出了欢迎信息,并返回了状态码 200。
执行 index() 函数时,由于它被装饰器修饰了,所以实际上是执行了闭包函数 wrapper()。在 wrapper() 函数中,我们记录了函数执行前后的时间,并输出了运行时间。最后,我们将 index() 函数的返回值 200 返回给调用者。
"""

3:装饰器模板

  • **语法糖的书写规范:**紧贴在被装饰对象的上方书写,语法糖语被装饰对象之间不可以有代码
  • **语法糖的内部原理:**语法糖会自动把被装饰对象的名字当成参数传给装饰器函数调用
#定义一个装饰器函数,它接受一个函数作为参数并返回一个新的函数
def decorator(func):
    # 定义一个内部函数,它接受任意数量和类型的位置参数和关键字参数
    def wrapper(*args, **kwargs):
        # 在调用被装饰函数之前执行的代码
        print("Before calling the function")
        # 调用被装饰函数,并保存其返回值
        result = func(*args, **kwargs)
        # 在调用被装饰函数之后执行的代码
        print("After calling the function")
        # 返回被装饰函数的返回值
        return result
    # 返回内部函数,以便它可以被调用
    return wrapper
 
# 定义一个函数,并使用装饰器来增加其功能
@decorator  #my_function = decorator(my_function)my_function是decorator函数的返回值(内部函数的函数名)
def my_function(a, b):
    # 模拟一个运算,并返回结果
    result = a + b
    return result
 
# 调用被装饰函数
print(my_function(1, 2))
 
# 执行结果:当调用 my_function(1, 2) 时,输出结果如下:
 
# Before calling the function
# After calling the function

登录认证示例

is_login = {'is_login':False} #判断如果有一个函数成功等候后面的函数就不需要登录了
def login_auth(func):
    def login(*args, **kwargs):
        username = input('username:').strip()
        password = input('password:').strip()
        if username == 'kevin' and password == '123':
            print('登录成功')
 
            res = func(*args, **kwargs)
            
            is_login['is_login'] = True#只要is_login为 True  就不会登录
            return res
        else:
            print('用户名或者密码错误')
    return login
 
@login_auth
def user_login():
    print('认证成功')
res = user_login
res()

4:复杂装饰器

双层装饰器

import time
 

def login_auth(func):
    def auth(*args, **kwargs):
        username = input('username:').strip()
        password = input('password:').strip()
        if username == 'kevin' and password == '123':
            print('登录成功')
            res = func(*args, **kwargs)
            return res
 
        else:
            print('用户名密码错误')
 
    return auth
 

def all_time(func1):
    def time1(*args, **kwargs):
        start_time = time.time()
        res = func1(*args, **kwargs)
        stop_time = time.time()
        print('功能执行了%s秒' % (stop_time - start_time))
        return res
 
    return time1
 

@all_time  # index = all_time(all_time内部的 time1 的函数名) 当装饰器执行到最后一层这个变量就使用跟最初装饰器同名的函数名同名
@login_auth  # all_time内部的 time1 的函数名 = login_auth(index) 语法糖会把被装饰对象的函数名当成第一个参数传给装饰器的第一个参数
def index():
    print('执行完成')
 
 
index()  # 最后一层就是 auth()

多层装饰器

def outter1(func1):
    print('加载了outter1')
 
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
 
    return wrapper1
 

def outter2(func2):
    print('加载了outter2')
 
    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
 
    return wrapper2
 

def outter3(func3):
    print('加载了outter3')
 
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
 
    return wrapper3
 

@outter1  # index(outter1的内层函数名,一般最后一层我们使用跟功能函数同名的函数名 index) = outter1(wrapper2) ---------------wrapper1
@outter2  # wrapper2(outter2的内层函数名) = outter2(wrapper3)
@outter3  # wrapper3(outter3的内层函数名) =  outter3(index)
"""
只要加了语法糖,装饰器一定会执行,不调用被装饰的函数也会执行装饰器
执行装饰器是从下往上执行,执行到最后一个装饰器之后,会把最后一个装饰器的返回值的变量名语与被装饰对象的函数名同名
语法糖不会在没有调用装饰对象之前,是不会执行内部函数的
"""
def index():
    print('from index')
 
 
index() # 只要不调用被装饰的函数就不会执行装饰器内层函数

'''
执行顺序分析:
加载了outter3
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
执行顺序原理解释如下:
 
1.定义 outter3 函数时,先打印出 "加载了outter3";
2.进入 outter2 函数,先打印出 "加载了outter2";
3.在 outter2 函数中创建了 wrapper2 函数并返回,但并不执行 wrapper2 函数,此时将 wrapper2 函数作为参数传递给 @outter1 装饰器;
4.进入 outter1 函数,先打印出 "加载了outter1";
5.在 outter1 中将 wrapper2 函数作为参数传递给 outter1 的内部函数 wrapper1,并返回 wrapper1 函数,此时 index 函数被全局变量所指向;
6.当调用 index() 函数时,实际上是调用了经过装饰器增强后的 wrapper1 函数;
7.调用 wrapper1 函数,首先打印出 "执行了wrapper1",然后调用 wrapper2 函数;
8.调用 wrapper2 函数,首先打印出 "执行了wrapper2",然后调用 wrapper3 函数;
9.调用 wrapper3 函数,首先打印出 "执行了wrapper3",然后执行函数体中的 print('from index') 语句;
最终输出函数的执行结果 "from index"。
'''

5:装饰器修复技术

装饰器通常会将一个函数替换成另一个函数,修改函数的属性,或者在函数周围添加一些代码来实现某些功能,这可能会导致函数元数据信息的丢失,比如函数名、文档字符串、参数列表等等,为了避免这种情况,可以使用 wraps 装饰器来修饰装饰器本身,从而确保被修饰的函数保留了其原有的元数据信息不变。

wraps 装饰器本身也是一个装饰器,它接受一个函数作为参数,并返回一个包装函数。wraps 装饰器会将包装函数的名称、文档字符串、参数列表等元数据信息更新为原函数的元数据信息,从而确保被修饰的函数可以正确地保留原有的元数据信息。

使用方法:导入 from functools import wraps在装饰器的外部与内部函数之间定义 @wraps(func)

wraps 装饰器确保了被装饰的函数 say_hello 保留了其原本的元数据信息,包括函数名和文档字符串。这样,即使在嵌套多个装饰器的情况下,也能够正确地保留函数的属性信息。

from functools import wraps
 
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('Before the function is called.')
        result = func(*args, **kwargs)
        print('After the function is called.')
        return result
    return wrapper
 
@my_decorator
def say_hello(name):
    """A function that says hello"""
    print(f'Hello, {name}!')
 
print(say_hello.__name__)   # 输出 'say_hello'
print(say_hello.__doc__)    # 输出 'A function that says hello'

五:错误和异常

Python 有两种错误很容易辨认:语法错误和异常。

Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。

在这里插入图片描述

1:异常的处理

try…except…

try:
    执行代码
except:
    错误处理

# =========> 举一个例子 <=================
while True:
    try:
        x = int(input("请输入一个数字: "))
        break
    except ValueError:
        print("您输入的不是数字,请再次尝试输入!")

一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。

处理程序将只针对对应的 try 子句中的异常进行处理,而不是其他的 try 的处理程序中的异常。

一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组

except (RuntimeError, TypeError, NameError):
    pass
import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    # 最后一个except子句可以忽略异常的名称,它将被当作通配符使用。
    # 你可以使用这种方法打印一个错误信息,然后再次把异常抛出。
    print("Unexpected error:", sys.exc_info()[0])
    raise

try…except…else

如果使用这个子句,那么必须放在所有的 except 子句之后。

else 子句将在 try 子句没有发生任何异常的时候执行。

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

try…finally

try-finally 语句无论是否发生异常都将执行最后的代码,一般用于连接池,IO等关闭

try:
    runoob()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)
finally:
    print('这句话,无论异常是否发生都会执行。')

2:异常的抛出

Python 使用 raise 语句抛出一个指定的异常。

在这里插入图片描述

raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类。

如果你只想知道这是否抛出了一个异常,并不想去处理它,那么一个简单的 raise 语句就可以再次把它抛出。

x = 10
if x > 5:
    raise Exception('x 不能大于 5。x 的值为: {}'.format(x))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值