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

蟒蛇 | 实例化前的类方法装饰器

蟒蛇 | 实例化前的类方法装饰器

偶然的你 2023-05-23 14:25:30
前言:这个问题可能比标题所暗示的要简单我正在尝试实现一个简单的事件处理程序系统,使用装饰器将各种方法和函数“订阅”到一个事件。1. 事件处理程序的简单方法一种简单的方法是创建一个类来添加和运行事件:# class for adding and running functions, aka an event handlerclass Runner:    def __init__(self):        self.functions = []    def add(self, function):        self.functions.append(function)    def run(self):        for function in self.functions:            function()runner = Runner()然后添加各种功能或方法:# random example functiondef myFunction():    print('myFunction')# random example classclass MyClass:    def myMethod(self):        print('myMethod')# getting instance of class & methodmyObject = MyClass()myObjectMethod = myObject.myMethodrunner.add(myFunction)runner.add(myObjectMethod)runner.run()这导致:> py main.pymyFunctionmyMethod好的!它按预期工作2.装饰器方法所以这没关系,但我最近了解了装饰器,并认为我可以通过替换有点丑陋的runner.add()方法来整理语法。首先,我将Runner.add()方法更改为装饰器:class Runner:    def __init__(self):        self.functions = []    def add(self, function):        self.functions.append(function)        return function # <-- returns function    def run(self):        for function in self.functions:            function()runner = Runner()然后我删除runner.add()调用并插入装饰器:@runner.adddef myFunction():    print('myFunction')class MyClass:    @runner.add    def myMethod(self):        print('myMethod')# myObject = MyClass()# myObjectMethod = myObject.myMethodrunner.run()这显然会导致:> py main.pymyFunctionTraceback (most recent call last):...TypeError: myMethod() missing 1 required positional argument: 'self'显然,这里的问题是我将“静态”(或者它是“未绑定”?)添加myMethod到事件中,该事件与实例无关,因此没有任何实例self。诸如此类的答案指的是实现装饰器以在调用时包装方法,但是我使用装饰器的原因是您在初始代码执行时获得的访问权限。一个想法是必须在MyClass.__init__()构造函数中运行一些东西,因为只有这样才能创建一个实例并且您可以访问self,但是那么如何仅在实例化时运行装饰器呢?更不用说实现我们在这里追求的结果了。问题我有什么选择来实现示例 1 中所示的行为,但语法尽可能接近示例 2(或任何其他“看起来干净”的变体),因为它有点冗长、丑陋,而且肯定不像装饰器那样精简runner.add()。
查看完整描述

1 回答

?
慕桂英4014372

TA贡献1871条经验 获得超13个赞

函数和方法的处理方式略有不同,而不是一视同仁:

功能:

  • [A]正常添加函数到列表

方法:

  • self调用装饰器时的未绑定方法,因此还没有关联的实例或值

  • [B]_tagged我们通过设置其属性在方法未绑定时(在创建实例之前)“标记”该方法

  • [C]一旦创建了一个实例(并且self变量已经被填充),__init__()我们在里面搜索并添加所有标记的方法

  • 我们可以在之后添加方法,因为一旦它们被绑定到一个实例,参数self就会被填充(这意味着它在技术上没有参数,类似于functools.partial()

import inspect


class Runner:

    def __init__(self):

        self.functions = []


    def add(self, function):

        if len(inspect.signature(function).parameters) == 0:

            # [A] adds if has no parameters

            self.functions.append(function)

        else:

            # [B] tags if has 1 parameter (unbound methods have "self" arg)

            function._tagged = True

        return function


    # [C] search through object and add all tagged methods

    def add_all_tagged_methods(self, object):

        for method_name in dir(object):

            method = getattr(object, method_name)

            if hasattr(method, '_tagged'):

                self.functions.append(method)


    def run(self):

        for function in self.functions:

            function()


runner = Runner()

然后使用与下面相同的初始代码


@runner.add

def myFunction():

    print('myFunction')


class MyClass:

    def __init__(self):

        runner.add_all_tagged_methods(self)


    @runner.add

    def myMethod(self):

        print('myMethod')


myObject = MyClass()

myObjectMethod = myObject.myMethod


runner.run()


查看完整回答
反对 回复 2023-05-23
  • 1 回答
  • 0 关注
  • 86 浏览
慕课专栏
更多

添加回答

举报

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