Подтвердить что ты не робот

Создание классов с множеством импортных функций здесь и там

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

Скажем, ObjectI и ObjectII и ObjectXI все используют некоторые функции в alotoffunc.py. И каждый из объектов использовал разные функции, но все объекты имеют переменную object.table.

alotoffunc.py:

def abc(obj, x):
  return obj.table(x) * 2

def efg(obj, x):
  return obj.table(x) * obj.table(x)

def hij(obj, x, y):
  return obj.table(x) * obj.table(y)

def klm(obj, x, y):
  return obj.table(x) *2 - obj.table(y)

И затем я импортирую функции и перегружаю их:

import alotoffunc

class ObjectI:
  def abc(self, x):
    return alotoffunc.abc(self, x)

  def efg(self, x):
    return alotoffunc.efg(self, x)

class ObjectII:
  def efg(self, x):
    return alotoffunc.efg(self, x)
  def klm(self, x, y): 
    return alotoffunc.klm(self, x, y)

class ObjectXI:
  def abc(self, x):
    return alotoffunc.abc(self, x)
  def klm(self, x, y):
    return alotoffunc.klm(self, x, y)

Теперь это выглядит как большой беспорядок, как мне следует строить класс объектов и упорядочивать alotoffunc.py?

4b9b3361

Ответ 1

(1) У вас может быть базовый класс, который реализует все методы, а затем переопределяет ненужные, чтобы поднять NotImplementedError в подклассах.

(2) У вас могут быть миксины для уменьшения повторения:

import alotoffunc

class MixinAbc:
    def abc(self, x):
        return alotoffunc.abc(self, x)

class MixinEfg:
    def efg(self, x):
        return alotoffunc.efg(self, x)

class MixinKlm:
    def klm(self, x, y):
        return alotoffunc.klm(self, x, y)

class ObjectI(MixinAbc, MixinEfg):
    pass

class ObjectII(MixinEfg, MixinKlm):
    pass    

class ObjectXI(MixinAbc, MixinKlm):
    pass

Вы также можете комбинировать этот метод с методом @cpburnz.

Ответ 2

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

import alotoffunc

class ObjectI:
  abc = alotoffunc.abc
  efg = alotoffunc.efg

class ObjectII:
  efg = alotoffunc.efg
  klm = alotoffunc.klm

class ObjectXI:
  abc = alotoffunc.abc
  klm = alotoffunc.klm

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

Ответ 3

Если бы я хотел избежать mixins, возможно, чтобы свести к минимуму непрозрачность моего кода, я бы просто сделал это следующим образом:

class ObjectI:
    from alotoffunc import abc, efg

class ObjectII:
    from alotoffunc import efg, klm

class ObjectXI:
    from alotoffunc import abc, klm

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

Если вы хотите, чтобы они были статическими методами, используйте staticmethod следующим образом:

class ObjectI:
    from alotoffunc import abc, efg
    abc = staticmethod(abc)

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

В случае, если функции, которые вы хотите импортировать, имеют какие-то логические группировки, то вам обязательно нужно использовать микшины или организовать функции в отдельные "модули mixin", чтобы вы могли даже сделать from mixinmodule import *. Для подхода mixin-class я считаю, что регулярный оператор импорта работает лучше, чем из-import, если только имена ваших функций не очень длинные, и вы хотите только их вводить один раз!

Ответ 4

Вы можете сделать:

from alotoffunc import *

И тогда, если, например, мы имеем переменные o1, o2, которые являются экземплярами Object1 и Object2, мы можем сделать:

abc(o1,x)
efg(o2,x)
...

Это не так "хорошо", как метод OO для вызова o1.abc(x), но он настолько удобен в обслуживании, что ему необходимо переопределить все методы.

Что делает from ___ import *, так это освободить вас от необходимости использовать имя пакета, например alotoffunc.abc и т.д.

Изменить

Как комментирует @JamesKing, это решение не работает, если у вас есть функция с тем же именем внутри ваших классов. Поскольку они кажутся бесполезными - они просто называют импортированные функции - я думал об их удалении. Если это не работает с вашим кодом, вы должны использовать from ___ import ___ as ___, как он сказал.

Ответ 5

Это подход "factory". Может быть сложнее, если у вас нет таких функций, но если у вас действительно есть "много", это может сработать.

import alotoffunc

class Builder(object):
    def __init__(self, objtype):
        objtypes = { 'ObjectI': ['abc', 'efg'],
                     'ObjectII': ['efg', 'klm'],
                     'ObjectXI': ['abc', 'klm']}

        for func in objtypes[objtype]:
            self.__dict__[func] = getattr(alotoffunc, func)

Ответ 6

Как насчет этого:

import alotoffunc
import types

class ObjectI:
    def __init__(self):
        setattr(self, abc, types.MethodType(alotoffunc.abc, self))
        setattr(self, efg, types.MethodType(alotoffunc.efg, self))

class ObjectII:
    def __init__(self):
        setattr(self, efg, types.MethodType(alotoffunc.efg, self))
        setattr(self, klm, types.MethodType(alotoffunc.klm, self))

class ObjectXI:
    # ...

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

Это связывает исходные функции с каждым экземпляром при создании. setattr() не требуется, но я предпочитаю, чтобы это сигнализировало, что происходит некоторая "магия".

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

Ответ 7

Я думаю, что более чистым способом достижения вашей цели является использование функции множественного наследования python. Многие программисты ненавидят MI, утверждая, что он создает грязный код или его трудно поддерживать. Пока вы будете осторожны при планировании своих классов, использование MI может быть очень полезным.

Одна из многих ссылок, возвращаемых Google

SomeClasses.py - Обычно каждый класс хранится в отдельном файле.

class ABCClass:
    def abc(obj, x):
    return obj.table(x) * 2

class DEFClass:
    def efg(obj, x):
        return obj.table(x) * obj.table(x)

class HIJClass:
    def hij(obj, x, y):
        return obj.table(x) * obj.table(y)

class KLMClass:
    def klm(obj, x, y):
        return obj.table(x) *2 - obj.table(y)

TheProject.py - дополнительный код не требуется, если вам действительно не нужно переопределять функциональные возможности базовых классов.

import SomeClasses #or each class file

class ObjectI (ABCClass, EFGClass):
    pass

class ObjectII (EFGClass, KLMClass):
    pass

class ObjectXI (ABCClass, KLMClass):
    pass

Конечно, это тривиальный пример, когда каждый базовый класс имеет только одну функцию. Реальный пример, который приходит на ум, заключается в упаковке изображений на общий ресурс текстуры. Вам нужна функциональность, которая приходит PIL.Image, но может сохранять изображения в виде листовых узлов в структуре данных дерева с валовым деревом. Вы можете использовать множественное наследование для получения методов обоих классов с использованием минимального кода. LeafImage будет иметь все методы обоих классов, и когда вы решите изменить TreeLeaf, в LeafImage дополнительный код не понадобится.

class LeafImage (PIL.Image, TreeLeaf):
    pass