簡體   English   中英

使用裝飾器將字典插入到 python 類中

[英]Inserting a dictionary into a python class using a decorator

我正在嘗試設計一個可以接受類(而不是對象)的名稱並將字典插入到類的每個實例中的包裝器。 下面是我在包裝現有函數時如何實現這一點的片段。

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我想注入到類A ,這樣類obj_a = A()的實例可以調用這些函數,例如obj_a.f_1() . 我已經使用類似於上面的代碼片段的裝飾器函數設法實現了這一點。
  2. 現在,每個用戶定義的函數都有一些重復的代碼,如果我的基類A所有實例都可以訪問用戶定義的字典,我認為我可以去掉這些代碼。 我實現這一目標的思考過程是嘗試修改我現有的包裝函數以將其添加到類中。 但是,我知道字典不可調用,因此是問題。

我希望這已經足夠詳細了。

更新 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()

每個用戶函數都包含一個常用字典的子集。 為了利用這一點,我認為如果可以將其作為動態注入的 BaseClass 屬性的一部分進行訪問會很方便。

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

為了合並這一點,我可能需要將BaseClass上的 run 方法更改為如下所示:

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)

這個解決方案有效,但也回答了我的問題,我想我可以說,當一個簡單的函數可以實現這一點時,我可能通過為此編寫一個裝飾器來過度設計我的解決方案。

但是,原始問題的一個方面仍然存在 - 這是一個好方法嗎?

你提問的方式給了我一個提示,你不太了解python如何使用對象引用。

裝飾器並不意味着與不可調用的對象一起使用,所以它是澄清

接受一個類的名稱(不是一個對象)

不是很清楚。 並且您想要附加到類的內容沒有區別 - dict 或函數,無論如何您只會操作抽象引用。

除此之外,看看這個片段:

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

@patcher
class A:
    pass

它是否滿足您的需求?

我想我可以說也許我過度設計了我的解決方案

嗯,確實有點...... FWIW 整個事情看起來非常復雜

首先第一件事:如果您將 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"]

但請注意,您仍然不需要通過BaseClassself來從模塊的函數訪問此 dict。

你當然可以再次通過其中的類或實例您使用自定義的功能,但在這里,沒有必要對一個奇怪的玩意兒-他們要么使類方法,或直接將它們設置為類的屬性,Python會采取注入類的護理或 instance 作為第一個參數(這里是一個使它們成為實例方法的例子):

# 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 來看,這仍然完全沒用——它們根本不需要self來訪問SHARED_DATA ,但現在它們不能在沒有BaseClass實例作為第一個參數的情況下執行,因此用戶無法直接測試它們。

現在也許您在問題中沒有提到其他一些事情,但到目前為止,您似乎正在努力使簡單的事情變得復雜;-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM