Как лучше всего инициализировать объект подкласса, используя объект из родительского класса? - PullRequest
0 голосов
/ 26 июня 2018

Я хочу создать подкласс класса существующего пакета (исходный код которого я не хочу / не могу изменить). Объекты класса инициализируются только с использованием строки, а затем заполняются с использованием всех видов функций add. Минимальный пример может выглядеть так (без каких-либо add функций):

import copy


class Origin(object):
    def __init__(self, name):
        self.name = name
        self.dummy_list = [1, 2, 'a']
        self.dummy_stuff = {'a': [12, 'yt']}

    def make_copy(self):
        return copy.deepcopy(self)

    def dummy_function(self):
        return len(self.dummy_list)

Я хочу создать подкласс таким образом, чтобы я мог инициализировать его экземпляры, используя экземпляр Origin. Прямой путь будет

class BasedOnOrigin(Origin):
    def __init__(self, origin_instance, new_prop):
        Origin.__init__(self, origin_instance.name)
        self.dummy_list = copy.deepcopy(origin_instance.dummy_list)
        self.dummy_stuff = copy.deepcopy(origin_instance.dummy_stuff)
        self.new_prop = new_prop

Раздражает то, что мне нужно копировать все то, о чем мне нужно знать заранее.

Другой вариант будет

class BasedOnOrigin2(Origin):
    def __init__(self, origin_instance, new_prop):
        Origin.__init__(self, origin_instance.name)
        self = origin_instance.make_copy()
        self.new_prop = new_prop

но часть self = выглядит довольно нестандартно и new_prop не установлена, поэтому для этого мне понадобится дополнительная функция.

Есть ли стандартный способ сделать это?

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

from functools import partial

def add_function(obj, func):

    setattr(obj, func.__name__, partial(func, obj))

но это может раздражать, если есть (i) много функций для добавления и (ii) много экземпляров, к которым нужно добавить функции.

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

но часть self = выглядит довольно нестандартно, а new_prop не установлен

self - это просто локальная переменная, поэтому ее повторное связывание действительно влияет только на локальную область.

Есть ли стандартный способ сделать это?

Из того, что вы описываете, похоже, что ваша настоящая проблема в том, что у вас есть экземпляры класса, созданные другой библиотекой, которые вы не хотите / не можете изменять, и что вы действительно хотите, это добавить новые методы (и в конечном итоге переопределить некоторые методы) для этих объектов, но не может, так как вы можете указать этой библиотеке использовать свой собственный класс.

Если точка целиком и полностью "заменит" исходный класс своей собственной версией (таким образом, изменение повлияет на все экземпляры исходного класса), каноническим решением будет monkeypatch исходный класс * * 1016

from otherlib import TheClass

def patch_the_class():
    # we do this in a function to avoid
    # polluting the global namespace

    # add a new method

    def newmethod(self):
       # code here

    TheClass.newmethod = newmethod

    # override an existing method

    # keep a reference to the original so
    # we can still use it:
    _original = TheClass.some_method

    def mymethod(self, arg):
        something = _original(self, arg)
        # additional stuff here
        return something

    TheClass.some_method = mymethod

patch_the_class()           

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

Преимущество этого решения (по отношению к каждому экземпляру в отдельности) - это меньшая стоимость и гарантия того, что никто никогда не забудет исправить экземпляр.

Теперь обратите внимание, что обезьяньи патчи следует рассматривать либо как временное обходное решение, либо как крайнее средство взлома. Если библиотека, которую вы исправляете, является OSS, вы можете изменить ее, чтобы либо улучшить исходный класс, либо реализовать какой-то способ заставить конкретный класс использовать настраиваемый и внести его обратно.

0 голосов
/ 26 июня 2018

Я думаю, что лучший подход - это определить функцию, которая расширит оригинал origin instance, не копируя его, например,

def exdend(*origin_instances):
    def my_function_one(self):
        pass
    def my_function_two(self):
        pass

    for origin_instance in origin_instances:
        setattr(origin_instance, my_function_one.__name__, partial(my_function_one, origin_instance))
        setattr(origin_instance, my_function_two.__name__, partial(my_function_two, origin_instance))

    return origin_instances
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...