为了账号安全,请及时绑定邮箱和手机立即绑定

我的装饰器适用于常规功能,但不适用于实例

我的装饰器适用于常规功能,但不适用于实例

浮云间 2021-11-02 16:14:14
我有一个工作装饰器,用于在一段时间内运行一个方法 True 并且它在常规函数上运行良好。当我尝试装饰实例的函数时会出现问题。这是装饰器:from threading import Threaddef run_in_while_true(f):    def decorator(break_condition=False):        def wrapper(*args, **kwargs):            while True:                if break_condition:                    return                f(*args, **kwargs)         return wrapper    return decoratorclass A(object):    @run_in_while_true    def print_ch(self, ch):         print ch@run_in_while_truedef print_with_dec(ch):     print chprint_with_dec()('f')  # Call 1# If i would want to pass a break condition i would write thisprint_with_dec(1==1 and 2*2==4)('f')a = A()a.print_ch()('4')  # Call 2`Call 1 按预期运行并打印了很多。出于某种原因,调用 2 获取了 break_condition 所在的 self 参数,因为对 break_condition 的检查为真并且函数返回。我需要以何种方式更改装饰器才能使其也适用于对象?提前致谢
查看完整描述

2 回答

?
慕桂英3389331

TA贡献2036条经验 获得超8个赞

您生成的代码看起来很奇怪:


a.print_ch()('4')  # Call 2

这是因为您的装饰器中有一个额外的层:


def run_in_while_true(f):

    def decorator(break_condition=False):

        def wrapper(*args, **kwargs):

该@run_in_while_true装饰是要回报decorator,其中有被称为返回wrapper,其中有被称为评估结果。该@run_in_while_true作为装饰的一部分被自动调用。另外两个需要两组括号,如您的代码所示。


这是一个问题,因为方法调用,如a.print_ch(),会自动将调用者传递给第一次调用:


a.print_ch()('4')

# is much the same as

# A.print_ch(self=a)('4')

这解释了为什么你在你的 break_condition.


我建议您尝试统一两个内部功能。只需将命名参数(如break_condition或break_when或)break_if传递给函数/方法,并让包装器拦截该值:import functools


def run_until_done(func):

    @functools.wraps

    def wrapper(*args, break_if=None, **kwargs):

        done = break_if if callable(break_if) else lambda: break_if


        while not done():

            func(*args, **kwargs)

     return wrapper


@run_until_done

def print_with_dec(ch):

    print ch


print_with_dec('4', break_if=lambda: 1==1 and is_done())


查看完整回答
反对 回复 2021-11-02
?
明月笑刀无情

TA贡献1828条经验 获得超4个赞

感谢大家的帮助,在研究了更多关于从对象调用函数的方式之后,我编写了这些最终的装饰器。它们都适用于对象的常规函数和方法。一个在循环中运行函数直到满足条件,另一个在线程中运行第一个函数,因此程序不会等待。


装饰者

def loop_in_while_oop(f):

    """ Runs a function in a loop, params have to be passed by name"""

    def decorated(self=None, break_if=None, *args,**kwargs):

        """

        :param self: Will be passed automatically if needed

        :param break_if: Lambada expression for when to stop running the while loop

        """

        done = break_if if callable(break_if) else lambda: break_if

        while not done():

            if self is not None:

                f(self, *args, **kwargs)

            else:

                f(*args, **kwargs)

    return decorated


def loop_in_thread_oop(f):

    """ Runs function in a loop in a thread, MUST: pass arguments by name"""

    def decorated(self=None, break_if=lambda: False, *args, **kwargs):

        """

        :param self: Will be passed automatically if needed

        :param break_if: Lambada expression for when to stop running the while loop, if value not passed will run forever

        """

        f1 = loop_in_while_oop(f)

        t = Thread(target=f1, args=args, kwargs=dict(self=self, break_if=break_if, **kwargs))

        t.start()

    return decorated

使用装饰器

class SomeObj(object):


    @loop_in_thread_oop

    def print_c(self, c):

        print c



@loop_in_thread_oop

def p1(f):

    print f



@loop_in_thread_oop

def p2(f):

    print f


if __name__ == '__main__':

    a = SomeObj()

    start = time.time()

    a.print_c(c='a')  # Will run forever because break_if was not specified

    p1(f='3', break_if=lambda: time.time() - start > 3)  # Will stop after 3 seconds

    p2(f='5', break_if=lambda: time.time() - start > 5)  # Will stop after 5 seconds

输出:


0-3 秒之间:打印a35(顺序不固定)

3-5 秒之间:打印a5(顺序不固定)

3-5 秒之间:打印a


查看完整回答
反对 回复 2021-11-02
  • 2 回答
  • 0 关注
  • 186 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信