简体   繁体   English

通过装饰器向类动态添加函数

[英]Dynamically add function to class through decorator

I'm trying to find a way to dynamically add methods to a class through decorator.我试图找到一种通过装饰器向类动态添加方法的方法。 The decorator i have look like:我的装饰器看起来像:

def deco(target):

    def decorator(function):
        @wraps(function)
        def wrapper(self, *args, **kwargs):
            return function(*args, id=self.id, **kwargs)

        setattr(target, function.__name__, wrapper)
        return function

    return decorator

class A:
    pass

# in another module
@deco(A)
def compute(id: str):
    return do_compute(id)

# in another module
@deco(A)
def compute2(id: str):
    return do_compute2(id)

# **in another module**
a = A()
a.compute() # this should work
a.compute2() # this should work

My hope is the decorator should add the compute() function to class A, any object of A should have the compute() method.我希望装饰器应该将compute()函数添加到类 A,A 的任何对象都应该具有计算()方法。 However, in my test, this only works if i explicitly import compute into where an object of A is created.但是,在我的测试中,这仅在我将计算显式导入到创建 A 对象的位置时才有效。 I think i'm missing something obvious, but don't know how to fix it.我想我错过了一些明显的东西,但不知道如何解决它。 appreciate any help!感谢任何帮助!

I think this will be quite simpler using a decorator implemented as a class:我认为使用作为类实现的装饰器会更简单:

class deco:
    def __init__(self, cls):
        self.cls = cls

    def __call__(self, f):
        setattr(self.cls, f.__name__, f)
        return self.cls

class A:
    def __init__(self, val):
        self.val = val

@deco(A)
def compute(a_instance):
    print(a_instance.val)


A(1).compute()
A(2).compute()

outputs产出

1
2

But just because you can do it does not mean you should.但仅仅因为你可以做到,并不意味着你应该这样做。 This can become a debugging nightmare, and will probably give a hard time to any static code analyser or linter (PyCharm for example "complains" with Unresolved attribute reference 'compute' for class 'A' )这可能会成为调试的噩梦,并且可能会给任何静态代码分析器或 linter 带来困难(例如,PyCharm Unresolved attribute reference 'compute' for class 'A' “抱怨”)


Why doesn't it work out of the box when we split it to different modules (more specifically, when compute is defined in another module )?当我们将它拆分为不同的模块时(更具体地说,compute在另一个模块中定义时),为什么它不能开箱即用?

Assume the following:假设如下:

a.py一个.py

print('importing deco and A')

class deco:
    def __init__(self, cls):
        self.cls = cls

    def __call__(self, f):
        setattr(self.cls, f.__name__, f)
        return self.cls

class A:
    def __init__(self, val):
        self.val = val

b.py b.py

print('defining compute')

from a import A, deco

@deco(A)
def compute(a_instance):
    print(a_instance.val)

main.py主文件

from a import A

print('running main')

A(1).compute()
A(2).compute()

If we execute main.py we get the following:如果我们执行main.py我们会得到以下内容:

importing deco and A
running main
Traceback (most recent call last):
    A(1).compute()
AttributeError: 'A' object has no attribute 'compute'

Something is missing.缺了点什么。 defining compute is not outputted.不输出defining compute Even worse, compute is never defined, let alone getting bound to A .更糟糕的是, compute从未被定义,更不用说绑定到A

Why?为什么? because nothing triggered the execution of b.py .因为没有触发b.py的执行。 Just because it sits there does not mean it gets executed.仅仅因为它坐在那里并不意味着它会被执行。

We can force its execution by importing it.我们可以通过导入来强制执行它。 Feels kind of abusive to me, but it works because importing a file has a side-effect: it executes every piece of code that is not guarded by if __name__ == '__main__ , much like importing a module executes its __init__.py file.感觉有点侮辱我,但它有效,因为导入文件有副作用:它执行每一段不受if __name__ == '__main__保护的代码,就像导入模块执行它的__init__.py文件一样。

main.py主文件

from a import A
import b

print('running main')

A(1).compute()
A(2).compute()

outputs产出

importing deco and A
defining compute
running main
1
2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM