基于python下的函数(装饰器)

这篇博客详细介绍了Python中的函数概念、定义、参数作用及调用。深入探讨了局部和全局变量,以及如何通过装饰器增强函数功能。还涵盖了装饰器的多个实例,包括带有交互式的装饰器应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数的概念:

所谓函数,就是把具有独立功能的代码块组织成为一个小模块,在需要的时候调用函数的使用包含两个步骤
        1.定义函数--封装独立的功能
        2.调用函数--享受封装的成果
函数的作用:在开发时,使用函数可以提高编写的效率以及代码的重用
函数:
函数是带名字的代码块,用于完成具体的工作需要在程序中多次执行同一项任务时,
你无需反复编写完成该任务的代码,而只需调用该任务的函数,
让python运行其中的代码,你将发现,通过使用函数,程序编写,阅读,测试和修复都将更容易

函数的定义:

def 函数名():
        函数封装的代码
1.def是英文define的缩写
2.函数名称应该能够表达函数封装代码的功能,方便后续的调用
3.函数名称的命名应该符合标识符的命名规则
        可由字母,下划线和数字组成
	    不能以数字开头
        不能与关键字重名

函数中参数的作用

函数,把具有独立功能的代码块组织成为一个小模块,在需要的时候调用函数的参数,
增加函数的通用性,针对相同的数据处理逻辑,能够适应更多的数据
        1.在函数内部,把参数当作变量使用,进行需要的数据处理
        2.函数调用时,按照函数定义的参数顺序,把希望在函数内部处理的数据,通过参数传递

定义函数:

#  定义好一个函数之后,只是表示这个函数封装了一段代码
#  如果不主动调用函数,函数是不会主动执行的
def say_hello():
    print 'hello1'
    print 'hello2'
    print 'hello3'
# 调用函数
say_hello()

这里写图片描述
函数01–浮点型:

def abc_2_abc(price,weight):
    money = float(price) * float(weight)   计算价格
    print  '%.2f * %.2f = %.2f' %(price,weight,money)    调用的浮点型
abc_2_abc(2.5,4.6)

这里写图片描述
函数02–整型:

def abc_2_abc(price,weight):
    money = (price) * (weight)   计算价格
    print  '%d * %d = %d' %(price,weight,money)  调用的整型
abc_2_abc(2,4)

这里写图片描述
pycharm使用中的一些小技巧:

crtl+/全部注释
ctrl+q查看函数功能
不知道函数接受什么用*来表示
函数与注释空两行
pass命令进行占位

函数调用:

# 1.定义一个函数,能够打印*组成的一条分割线
def print_line():
    print '^' * 5
print_line()
# 2.定义一个函数能够打印由任意字符组成的分割线
def print_line(char):
    print char * 5
print_line('@')
# 3.定义一个函数能够打印任意重复次数的分割线
def print_line(char,times):
    print char * times
print_line('%',5)
# 4.定义一个函数能够打印五条分割线,
# 分割线要求符合需求3
def print_lines(char,times):
    """
    打印多行分割线
    :param char: 分割字符
    :param times:分割线次数
    """
    row = 0
    while row < 5:
        print_line(char,times)
        row += 1
print_lines('!',5)

这里写图片描述
对两个数字求和:

#  能实现两个数字求和
def sum_sum():
    number1 = 10
    number2 = 20
    result = number1 + number2
    print '%d + %d = %d' % (number1,number2,result)
sum_sum()

这里写图片描述
形参与实参:

#  形参:定义函数时,小括号中的参数,是用来接收参数使用,在函数内部,是当作变量来用的
#  实参:调用函数时使用的,小括号中的参数,是用来把数据传递到函数的内部
def sum_sum(num1,num2):
    """对两个数字求和"""
    result = num1 + num2
    print '%d+%d=%d' % (num1,num2,result)
sum_sum(27,13)

这里写图片描述
对两个数字求和:(加返回值)

返回值:是函数完成工作后,最后给调用者的一个结果,在函数中使用return关键字可以返回结果,
调用函数一方,可以使用来接收函数的返回结果.

def sum_2_sum(num1,num2):  # 对两个数字求和
    result = num1 + num2
    return result
sum_result = sum_2_sum(60,60)
print sum_result

这里写图片描述
变量的引用:

在python中:
        变量和数据是分开存储的
        数据保存在内存中的一个位置
        变量中保存的是数据在内存中的地址
        变量中记录数据的地址,就叫做引用
        使用id()函数可以查看变量中保存数据所在内存的地址

使用ipython解释器进行引用的举例:

In [1]: a = 1

In [2]: id(a)   查看a的内存地址
Out[2]: 25861224

In [3]: id(1)   查看1的内存地址
Out[3]: 25861224

In [4]: b = a

In [5]: id(b)   查看b的内存地址
Out[5]: 25861224

In [6]: a = 2

In [7]: id(a)   查看a的内存地址
Out[7]: 25861200

In [8]: a = 1

In [9]: id(a)   重新赋值查看a的内存地址
Out[9]: 25861224

In [10]: a = 'banana'

In [11]: id(a)   重新赋值查看a的内存地址
Out[11]: 33316416

In [12]: a = 3   给a赋值

In [13]: b = 3  给b赋值

In [14]: id(a)  查看a的内存地址
Out[14]: 25861176

In [15]: id(b)   查看b的内存地址
Out[15]: 25861176

这里写图片描述
引用01:

# 定义了一个函数,准备了一个叫num的变量
# 引用的数字10的内存地址

def test(num):
    print '在函数内部%d对应的内存地址是%d' % (num, id(num))
    # 1.定义了一个数字变量
a = 10
print 'a变量保存数据内存的地址是%d' % id(a)
test(a)

这里写图片描述
引用02:

def test(num):
    print '在函数内部%d对应的内存地址是%d' % (num, id(num))
    # 1.定义了一个字符串变量
    result = 'hello'
    print '函数要返回数据的地址是%d' % id(result)
    return result


# 定义一个数字变量
a = 10
print 'a变量保存数据内存的地址是%d' % id(a)
r = test(a)
print '%s的内存地址是%d' % (r, id(r))

这里写图片描述
局部变量和全局变量:

局部变量是在函数内部定义的变量,只能在函数内部使用。
全局变量是在函数外部定义的变量,(没有定义在某一个函数内),所有函数内部都可以使用这个变量。
局部变量:
函数执行结束后,函数内部的局部变量,会被系统回收。不同的函数,可以定义相同的名字的局部变量,
但是各用各的不会产生影响。
局部变量的作用:
在函数内部使用,临时保存函数内部需要使用的数据。

局部变量01:

def demo1():
    # 定义一个局部变量
    num = 10
    print num
# 在函数内部定义的变量不能在其他位置使用
#print num
demo1()

这里写图片描述
局部变量02:

def demo1():
    num = 10
    print num
def demo2():
    #在demo1中定义的局部变量,不能在其他函数中使用
    pass    # 是一个占位符号
demo1()
demo2()

这里写图片描述
局部变量03:

不同的函数,可以定义相同名字的局部变量,但是不会产生影响

def demo1():
    demo1_num=10
    print demo1_num
def demo2():
    demo2_num=20
    print demo2_num
demo1()
demo2()

这里写图片描述
全局变量01:

全局变量,任意函数都可以使用

num = 10
def demo1():
    print 'demo1------>%d' %num
def demo2():
    print 'demo2------>%d' %num
demo1()
demo2()

这里写图片描述
全局变量02:

num = 10
def demo1():
    # 使用赋值语句会在函数内部,定义一个局部变量
    # 更改的是局部变量的值,全局变量不发生变化
    num = 99
    print 'demo1------>%d' %num
def demo2():
    print 'demo2------>%d' %num
demo1()
demo2()

这里写图片描述
全局变量03:

num = 10
def demo1():
    # 修改全局变量的值,使用关键字global,声明即可
    # global关键字会告诉解释器后面是一个全局变量
    # 再次使用赋值语句使,就不会创建局部变量了,
    # 会直接更改全局变量
    global num
    num =99
    print 'demo1----->%d' %num
def demo2():
    print 'demo2----->%d' %num
demo1()
demo2()

这里写图片描述
全局变量04:

注意:在开发的时候,应该把模块中的所有全局变量定义到其他函数的上方.

gl_num = 10
gl_title = 'westos'
gl_name = 'xfl'
def demo():
    print '%d' % gl_num
    print '%s' % gl_title
    print '%s' % gl_name
demo()

这里写图片描述
全局变量和局部变量:

gl_num = 10   定义全局变量

def demo1():
    num = 99   定义局部变量
    print num   输出局部变量
    print gl_num   输出全局变量

def demo2():
    print gl_num  输出全局变量
demo1()  调用函数
demo2()  调用函数

这里写图片描述
return返回值后面代码不会执行:

# return表示返回,后续代码不会执行
def sum_2_num(num1,num2):
    """对两个数字求和"""
    result = num1 + num2
    return result
    num = 1000  return返回值后面代码不会执行
    print num    1000不会被输出
sum_result = sum_2_num(3,7)
print sum_result

这里写图片描述
调用函数交互式相加:

def sum_2_num():
    """对两个数字求和"""
    num1 = int(raw_input('请输入一个数字:'))
    num2 = int(raw_input('请输入另一个数字:'))
    result = num1 + num2
    print result
sum_2_num()

这里写图片描述
函数的模块文件:

# import 模块文件      不可使用中文调用
模块文件:每一个python源文件都可以是一个模块文件
可以在一个python中定义函数或者变量,然后在另外一个文件中使用import filename导入这个模块。
导入之后就可以使用模块名.变量/模块名.函数的方式,使用这个模块中定义的函数或者变量。

import fun02
fun02.sum_2_num()
# 函数是直接调用的,变量是通过print调用进行输出的

这里写图片描述
上例中调用的fun02函数:
在这里插入图片描述
装饰器:

装饰器实际上就是为了给某段程序添加功能,但该程序已经上线或者已经被使用,那么就不能大批量的修改源代码,
这样就产生了装饰器。

最简单的装饰器:

概念:把一个函数当作参数,传递给函数返回一个加强版的函数
本质上是一个返回函数的函数"在不改变原函数的基础上,给函数增加功能"

def func1():
    print 'have a nice day!'
# 需求:让func1函数的执行结果前面加上一串'*',但是不改变原函数
def outer(func):
    def inner():
        print '*******************'
        func()
    # 将装饰好的返回
    return inner
# f是func1的加强版本,其实就是inner()
f = outer(func1)
f()
q = outer(func1)
q()

这里写图片描述
装饰器01:

def func1():
    print 'have a nice day!'
# 需求:让func1函数的执行结果前面加上一串'*',但是不改变原函数。

def outer():
    print '*******************'
    func1()
outer()

这里写图片描述
装饰器02:

def say(age):
    print "man is %d years old" % age

def outer(func):
    def inner(age):
        if age < 0:
            age = (-age)
        func(age)
    return inner
say = outer(say)   # 调用装饰器
say(-11)

这里写图片描述
装饰器03:

def outer(func):
    def inner(age):
        if age < 0:
            age = (-age)
        func(age)
    return inner
# 使用@符号将装饰器应用到函数
@outer
def say(age):
    print "man is %d years old" % age
say(11)

这里写图片描述
装饰器04:(增加交互式)

def func1():
    print 'have a nice day!'

def say(age):
    print "man is %d years old" % age

def outer(func):
    def inner():
         c = raw_input('请输入你想在开头添加的代码:')
         print c
         func()
    return inner
f = outer(func1)
f()

这里写图片描述
输出结果:
这里写图片描述
装饰器05:

def outer(fun):
    def wrapper():
        print '验证信息'
        fun()
        print '!!!!!!!'
    return wrapper
@outer
def func1():
    print 'func1'
func1()
@outer
def func2():
    print 'func2'
func2()
@outer
def func3():
    print 'func3'
func3()

在这里插入图片描述
装饰器06:

def outer(func):
    def inner(*args):
        print '%%%%%%'
        func(*args)
    return inner
@outer
def say(name,age):
    print 'my name is %s, I am %d years old!'%(name,age)
say('xfl',18)

这里写图片描述
装饰器07:

def outer(fun):
    def wapper(arg):
        print '验证信息'
        result = fun(arg)
        print '@@@@@@'
        return result
    return wapper

@outer
def func1(arg):
    print 'func1',arg
    print '******'
    return 'return'
response = func1('xfl')
print response

这里写图片描述

<think>好的,我现在需要回答用户关于Python函数超时装饰器的问题。首先,我需要回忆一下装饰器的基本概念,以及如何在Python中实现超时机制。装饰器Python中用来修改或增强函数行为的一种方式,通常通过@语法糖来使用。 接下来,用户问的是超时装饰器,也就是能够限制函数执行时间的装饰器。我需要考虑如何实现一个这样的装饰器。可能的思路是使用信号(signal)模块或者多线程/多进程来实现超时控制。不过,signal模块在Windows上可能不太稳定,或者有兼容性问题,而多线程的方式可能更通用一些。 首先,我应该想到使用signal.alarm函数,这在Unix-like系统上有效。当函数执行超过指定时间时,触发一个超时异常。但需要注意,signal模块在不同平台上的行为可能不同,比如Windows可能不支持某些信号。因此可能需要考虑替代方案,比如使用threading模块中的Timer来中断函数执行。 不过,直接中断一个正在运行的线程可能会有问题,因为Python的全局解释器锁(GIL)可能让线程无法被强制终止。这时候可能需要使用多进程,因为进程可以被终止而不会影响主进程。但这样会增加复杂度,因为进程间通信更麻烦。 所以,可能的实现方法有两种:基于signal的方式(适用于Unix)和基于multiprocessing的方式(跨平台)。需要向用户说明这两种方法的优缺点,并给出示例代码。 另外,Python中有没有现成的库可以实现函数超时?比如使用第三方库如wrapt、timeout-decorator等。如果有的话,也应该推荐给用户,这样他们不需要自己实现,避免潜在的错误。 在写代码示例时,需要注意装饰器的正确写法,可能需要使用functools.wraps来保留原函数的元数据。同时,处理异常的部分也需要详细说明,比如捕获超时异常后应该抛出什么样的错误,或者让用户自定义处理方式。 可能还需要提醒用户,超时装饰器在某些情况下可能无法正常工作,比如函数内部执行的是不可中断的操作(如某些IO操作),或者涉及到多线程/多进程环境下的同步问题。此外,使用信号的方法在主线程中可能有效,但在子线程中可能无法处理信号,需要特别注意。 总结下来,回答的结构应该是: 1. 介绍装饰器的概念。 2. 超时装饰器的作用:限制函数执行时间。 3. 实现方式一:使用signal模块(代码示例,适用平台)。 4. 实现方式二:使用multiprocessing模块(代码示例,跨平台)。 5. 提到第三方库,如timeout-decorator。 6. 注意事项和潜在问题。 需要确保代码示例正确,比如在signal的例子中,设置信号处理函数,然后设置alarm,在装饰器中处理超时异常。在multiprocessing的例子中,使用Process类来启动子进程,然后join超时后终止进程。 还要注意,在multiprocessing的例子中,函数参数和返回值的传递可能需要使用队列(Queue)或者其他的进程间通信方式,否则返回值可能无法正确获取。因此,在示例中需要处理如何传递参数和获取结果,或者处理可能的异常。 此外,需要测试这些代码是否存在问题,比如在装饰器中使用多进程时,如果函数有副作用或者修改了某些共享状态,可能会导致问题。因此,应该建议用户仅在必要时使用,并了解其限制。 最后,应该总结不同方法的优缺点,帮助用户根据实际情况选择适合的方案。</think>下面是对Python函数超时装饰器的分步介绍: ### 一、核心作用 通过装饰器限制函数最大执行时间,超时后自动中断执行并抛出异常。典型应用场景: - 网络请求防阻塞 - 算法执行时间限制 - 防止死循环 ### 二、实现方案对比 | 方案 | 优点 | 缺点 | |---------------------|-----------------------|------------------------------| | signal信号机制 | 实现简单 | 仅Unix系统有效 | | multiprocessing进程 | 跨平台 | 需要处理进程间通信 | | 第三方库 | 开箱即用 | 需额外安装依赖 | ### 三、原生实现示例 #### 方案1:signal信号(Unix系统) ```python import signal from functools import wraps from contextlib import contextmanager class TimeoutError(Exception): pass @contextmanager def time_limit(seconds): def signal_handler(signum, frame): raise TimeoutError("函数执行超时") signal.signal(signal.SIGALRM, signal_handler) signal.alarm(seconds) try: yield finally: signal.alarm(0) def timeout(seconds): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): with time_limit(seconds): return func(*args, **kwargs) return wrapper return decorator # 使用示例 @timeout(2) def long_running_task(): import time time.sleep(3) ``` #### 方案2:multiprocessing进程(跨平台) ```python from multiprocessing import Process, Queue from functools import wraps import time def timeout(seconds): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): result_queue = Queue() def worker(): try: result = func(*args, **kwargs) result_queue.put(('success', result)) except Exception as e: result_queue.put(('error', e)) p = Process(target=worker) p.start() p.join(timeout=seconds) if p.is_alive(): p.terminate() p.join() raise TimeoutError(f"函数执行超过 {seconds} 秒") status, data = result_queue.get() if status == 'success': return data else: raise data return wrapper return decorator # 使用示例 @timeout(3) def my_function(x): time.sleep(x) return "完成" try: print(my_function(2)) # 正常执行 print(my_function(5)) # 触发超时 except TimeoutError as e: print(e) ``` ### 四、推荐第三方库 1. **timeout-decorator** ```bash pip install timeout-decorator ``` ```python import timeout_decorator @timeout_decorator.timeout(5, timeout_exception=TimeoutError) def my_function(): # 长时间操作 ``` 2. **wrapt_timeout_decorator**(支持Windows) ```python from wrapt_timeout_decorator import timeout @timeout(5) def my_function(): # 长时间操作 ``` ### 五、注意事项 1. 信号方案在Windows下不可靠 2. 被装饰函数若涉及共享资源操作,使用进程方案可能导致数据不一致 3. 无法中断处于I/O等待状态的函数 4. 装饰器会影响原函数的元信息,建议始终使用`@wraps` ### 六、最佳实践建议 1. 优先考虑使用经过验证的第三方库 2. 超时时间设置应大于函数正常执行的最长时间 3. 配合日志记录超时事件 4. 在单元测试中验证超时逻辑 完整技术实现路线: 1. 确定目标平台(Unix/跨平台) 2. 选择实现方案 3. 添加异常处理逻辑 4. 编写单元测试验证 5. 集成到项目错误处理体系 这些方案可根据具体需求组合使用,例如:主程序使用进程方案保证跨平台,在Linux服务器环境可额外添加信号方案进行双重保护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值