Py学习  »  Python

使用decorator将字典插入python类

cyberbeast • 6 年前 • 2008 次点击  

我试图设计一个包装器,它可以接受类(不是对象)的名称,并在类的每个实例中插入一个字典。下面是我在包装现有函数时如何实现这一点的片段。

def insert_fn_into_class(cls):
    """Decorator function that consumes a function and inserts it into the cls class."""
    def decorator(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            return func(*args, **kwargs)
        setattr(cls, f'prefix_{func.__name__}', wrapper)

    return decorator

如何使用类似的模板来修饰字典并将其插入cls类。既然字典是不可调用的,那么如何设计这样的包装器呢?

更新

感谢您对此的建设性反馈。 一位堆栈溢出用户正确地指出,我未能解释为什么要这样做。所以说:

  1. 我正在尝试构建一个框架,它实际上可以使用一堆用户定义的函数并扩展现有的类。所以我有一节课 A ,以及用户定义的函数 f_1 , f_2 f_n 我想把它注入课堂 这样类的一个实例 obj_a = A() 可以调用以下函数 obj_a.f_1() . 我使用了一个类似于上面代码片段的decorator函数来实现这一点。
  2. 现在,每个用户定义的函数都有一些重复的代码,如果基类的所有实例 可以访问用户定义的词典。我实现这一点的思路是尝试修改现有的包装函数,将其添加到类中。然而,我知道字典是不可调用的,因此,这个问题。

我希望这足够详细。

更新2

看来还有进一步细化的余地。下面是一个例子,说明我前面描述的各种组件是什么样子的。

module_a.py

class BaseClass():
    def __init__(self):
        ...

    def base_class_method(self):
        ...

    def run(self):
        getattr(self, 'prefix_user_fn_1')()

def extend(cls=BaseClass):
    def decorator(func):
        def wrapper(self, *args, **kwargs):
            return func(*args, **kwargs)
        setattr(cls, f'prefix_{func.__name__}', wrapper)
    return decorator

user_defined_functions.py

from module_a import BaseClass, extend

@extend()
def user_fn_1():
    dict_ = {'a':'b', 'c':'d'}
    ...

@extend()
def user_fn_2():
    dict_ = {'my':'dict', 'c':'d'}
    ...

main.py

from module_a import BaseClass

b = BaseClass()
b.run()

每个用户函数都包含一个常用字典的子集。为了利用这一点,我认为如果这可以作为动态注入的基类属性的一部分来访问,那将是很方便的。

modified_user_defined_functions.py

# THIS WILL NOT WORK FOR OBVIOUS REASONS
from module_a import BaseClass, extend, common_config, insert_config_into_class

# @common_config ?? Is this even a good idea?
dict_ = {'my':'dict', 'a':'b', 'c':'d'}
insert_config_into_class(BaseClass, dict_)

@extend()
def user_fn_1(self):
    print(self.dict_)
    # do something with self.dict_
    ...

若要合并此项,我可能必须将run方法更改为 BaseClass 像这样:

modified_module_a.py

...
class BaseClass():
    ...

    def run(self):
        getattr(self, 'prefix_user_fn_1')(self)

更新3-潜在解决方案

def shared_config(data, cls=BaseClass):
    setattr(cls, 'SHARED_DICT', data)

这个解决方案是可行的,但也在一定程度上回答了我的问题,我想我可以说,当一个简单的函数可能实现这一点时,我可能通过为此编写一个decorator来过度设计我的解决方案。

然而,最初问题的一个方面仍然存在——这是一个好办法吗?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/40014
文章 [ 2 ]  |  最新文章 6 年前
bruno desthuilliers
Reply   •   1 楼
bruno desthuilliers    7 年前

我想我可以说也许我对我的解决方案设计得太过了

嗯,确实有点…FWW the whole thing looks quite overcomplicated .

首先:如果将dict定义为全局(模块)名称,则该模块中的所有函数都可以直接访问它:

# simple.py

SHARED_DATA = {"foo": "bar", "answer": 42}

def func1():
    print SHARED_DATA["foo"]

def func2():
    print SHARED_DATA["bar"]

所以我不认为在一个从另一个模块导入的类中“注入”它只是为了从这些函数访问它。

现在和你的装饰师:

def extend(cls=BaseClass):
    def decorator(func):
        def wrapper(self, *args, **kwargs):
            return func(*args, **kwargs)
        setattr(cls, f'prefix_{func.__name__}', wrapper)
    return decorator

如果目标是使函数成为类的属性,但不将其转换为实例方法,则可以使用 staticmethod

def extend(cls=BaseClass):
    def decorator(func):
        setattr(cls, f'prefix_{func.__name__}', staticmethod(func))
        return func
    return decorator

如果你有其他(无法解释的)理由 SHARED_DICT 类属性 BaseClass 或者另一个类),您确实可以提供一个配置函数:

# module_a
def configure(cls, data):
    cls.SHARED_DATA = data


# less_simple.py

from module_a import BaseClass, configure

SHARED_DATA = {"foo": "bar", "answer": 42}
configure(BaseClass, SHARED_DATA)

def func1():
    print SHARED_DATA["foo"]

def func2():
    print SHARED_DATA["bar"]

但请注意,你仍然不需要通过 基类 也不 self 从模块的函数访问此dict。

能够 当然,可以将类或实例传递给您使用过的已定义函数,但是在这里,不需要使用奇怪的装置-要么使它们成为classmethod,要么直接将它们设置为类的属性,python将负责将类或实例作为第一个参数(这里是example使它们成为实例方法):

# module_a
def configure(cls, data):
    cls.SHARED_DATA = data

def extend(cls=BaseClass):
    def decorator(func):
        setattr(cls, f'prefix_{func.__name__}', func)
        return func
    return decorator

# rube_goldberg.py

from module_a import BaseClass, configure, extend

SHARED_DATA = {"foo": "bar", "answer": 42}
configure(BaseClass, SHARED_DATA)

@extend
def func1(self):
    print SHARED_DATA["foo"]

@extend
def func2(self):
    print SHARED_DATA["bar"]

注意,这仍然是完全无用的装饰功能pov -他们不需要 自己 完全可以进入 SHARED_DATA ,但现在如果没有 基类 实例作为第一个参数,因此用户无法直接测试它们。

现在,也许你在问题中没有提到其他的事情,但到目前为止,你似乎正在努力使简单的事情变得复杂;-)

Slam
Reply   •   2 楼
Slam    7 年前

您提问的方式给了我一个提示,说明您不太了解python如何使用对象引用。

decorators不是用来处理不可调用的对象的,所以这是一个澄清

接受类的名称(不是对象)

不太清楚。而且您想附加到类dict或函数的内容没有区别,您无论如何只能操作抽象引用。

除此之外,看看这个片段:

def patcher(cls):
    def wrapper(*args, **kwargs):
        instance = cls(*args, **kwargs)
        instance.payload = {'my': 'dict'}
         return instance
    return wrapper

@patcher
class A:
    pass

它能满足你的需要吗?