Вставка словаря в класс Python с использованием декоратора - PullRequest
0 голосов
/ 22 октября 2018

Я пытаюсь создать оболочку, которая может принимать имя класса (не объекта) и вставлять словарь в каждый экземпляр класса.Ниже приведен фрагмент того, как я этого достигаю, когда оборачиваю существующую функцию.

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, чтобы он выглядел следующим образом:

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)

Это решение работает,но также несколько отвечает на мой вопрос, на который, я думаю, я могу сказать, что, возможно, я переусердствовал в своем решении, написав декоратор для этого, когда простая функция могла бы достичь этого.

Однако, аспект исходного вопроса все ещеостается - это хороший подход?

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

Я думаю, что могу сказать, что, может быть, я переусердствовал в своем решении

Ну, в общем-то, действительно ... 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"]

Но учтите, что вам все равно не нужно проходить через BaseClass или self, чтобы получить доступ к этому диктату из функций модуля.

Вы могли бы , конечно, передать либо класс, либо экземпляр вашим используемым функциям, но опять же, нет необходимости в причудливых хитростях - либо сделайте их методом класса, либо просто установите их как атрибутыкласса, Python позаботится о введении класса или экземпляра в качестве первого аргумента (вот пример, делающий их методы экземпляра):

# 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 в качестве первого аргумента, поэтому пользователь не может проверить их напрямую.

Теперь, может быть, есть и другие вещи, которые вы не упомянули в своем вопросе, но пока кажется, что вы действительно очень стараетесь усложнить простые вещи;

0 голосов
/ 22 октября 2018

То, как вы задаете вопросы, подсказывает мне, что вы не совсем понимаете, как python использует ссылки на объекты.

Декораторы не предназначены для использования с не вызываемыми объектами, поэтому это пояснение

принять имя класса (не объекта)

не совсем понятно.И нет никакой разницы в том, что вы хотите присоединить к классу - dict или function, вы все равно будете манипулировать только абстрактными ссылками.

Помимо всего этого, взгляните на этот фрагмент:

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

@patcher
class A:
    pass

Это покрывает ваши потребности?

...