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

前言:这个问题可能比标题所暗示的要简单


我正在尝试实现一个简单的事件处理程序系统,使用装饰器将各种方法和函数“订阅”到一个事件。



1. 事件处理程序的简单方法

一种简单的方法是创建一个类来添加和运行事件:


# class for adding and running functions, aka an event handler

class 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 function

def myFunction():

    print('myFunction')


# random example class

class MyClass:

    def myMethod(self):

        print('myMethod')


# getting instance of class & method

myObject = MyClass()

myObjectMethod = myObject.myMethod


runner.add(myFunction)

runner.add(myObjectMethod)

runner.run()

这导致:


> py main.py

myFunction

myMethod

好的!它按预期工作



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.add

def myFunction():

    print('myFunction')


class MyClass:

    @runner.add

    def myMethod(self):

        print('myMethod')


# myObject = MyClass()

# myObjectMethod = myObject.myMethod


runner.run()


这显然会导致:


> py main.py

myFunction

Traceback (most recent call last):

...

TypeError: myMethod() missing 1 required positional argument: 'self'

显然,这里的问题是我将“静态”(或者它是“未绑定”?)添加myMethod到事件中,该事件与实例无关,因此没有任何实例self。


诸如此类的答案指的是实现装饰器以在调用时包装方法,但是我使用装饰器的原因是您在初始代码执行时获得的访问权限。


一个想法是必须在MyClass.__init__()构造函数中运行一些东西,因为只有这样才能创建一个实例并且您可以访问self,但是那么如何仅在实例化时运行装饰器呢?更不用说实现我们在这里追求的结果了。



问题

我有什么选择来实现示例 1 中所示的行为,但语法尽可能接近示例 2(或任何其他“看起来干净”的变体),因为它有点冗长、丑陋,而且肯定不像装饰器那样精简runner.add()。


偶然的你
浏览 90回答 1
1回答

慕桂英4014372

函数和方法的处理方式略有不同,而不是一视同仁:功能:[A]正常添加函数到列表方法:self调用装饰器时的未绑定方法,因此还没有关联的实例或值[B]_tagged我们通过设置其属性在方法未绑定时(在创建实例之前)“标记”该方法[C]一旦创建了一个实例(并且self变量已经被填充),__init__()我们在里面搜索并添加所有标记的方法我们可以在之后添加方法,因为一旦它们被绑定到一个实例,参数self就会被填充(这意味着它在技术上没有参数,类似于functools.partial())import inspectclass Runner:&nbsp; &nbsp; def __init__(self):&nbsp; &nbsp; &nbsp; &nbsp; self.functions = []&nbsp; &nbsp; def add(self, function):&nbsp; &nbsp; &nbsp; &nbsp; if len(inspect.signature(function).parameters) == 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # [A] adds if has no parameters&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.functions.append(function)&nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # [B] tags if has 1 parameter (unbound methods have "self" arg)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; function._tagged = True&nbsp; &nbsp; &nbsp; &nbsp; return function&nbsp; &nbsp; # [C] search through object and add all tagged methods&nbsp; &nbsp; def add_all_tagged_methods(self, object):&nbsp; &nbsp; &nbsp; &nbsp; for method_name in dir(object):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; method = getattr(object, method_name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if hasattr(method, '_tagged'):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.functions.append(method)&nbsp; &nbsp; def run(self):&nbsp; &nbsp; &nbsp; &nbsp; for function in self.functions:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; function()runner = Runner()然后使用与下面相同的初始代码@runner.adddef myFunction():&nbsp; &nbsp; print('myFunction')class MyClass:&nbsp; &nbsp; def __init__(self):&nbsp; &nbsp; &nbsp; &nbsp; runner.add_all_tagged_methods(self)&nbsp; &nbsp; @runner.add&nbsp; &nbsp; def myMethod(self):&nbsp; &nbsp; &nbsp; &nbsp; print('myMethod')myObject = MyClass()myObjectMethod = myObject.myMethodrunner.run()
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python