LINUX.ORG.RU

Вызов метода классового поля в Python

 , ,


0

1

У меня есть 2 класса:

class A(object):
    def __init__(self, klass):
        self.klass = klass
    def foo(self, *args, **kwargs):
        pass
    def bar(self, *args, **kwargs):
        pass

class B(object):
    pass

a = A(B)
B.a = a
b = B()
Есть ли какой-нибудь способ отличить эти 2 вызова методов экземпляра класса A:
B.a.foo()
b.a.bar()
Хочется, чтобы foo мог работать только в случае, если он вызывается из самого класса B, а метод bar - только в случае, если он вызывается из экземпляра класса B. При этом, класс B не должен ничего знать о классе A (на самом деле, поле a устанавливается в другом месте через setattr). Возможно ли такое?


А юз кейс для этого какой?

Давно я не брал в руки питон, так что могу сильно ошибаться. Но могу предложить тебе переопределить setattr на классе A чтобы он делал 2 прокси при инициализации атрибута. Так же должны быть отдельные getattr на классе и инстансе, которые будут подцеплять нужные прокси.

Hater ★★
()
Ответ на: комментарий от Hater

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

Hater ★★
()

я походу тупой и нифига не понял, давай кусок кода побольше и чего ты хочешь таким достичь, будем посмотреть.

anonymous
()
Ответ на: комментарий от Hater

Юзкейс - ОРМ. B - готовый класс модели, который умеет в валидацию, сереализацию и т.д. Но он без слоя хранения. Хочется прозрачно встроить этот слой (класс A), причём так, чтобы он не зависел от способа хранения (SQL, NoSQL, файлы и т.д). Т.е. из модели будет вызываться что-то вроде Model.persist.find() для поиска и obj.persist.save() для сохранения, а поле persist будет брать на себя всю работу по хранению и восстановлению данных. Я конечно посмотрю, как это реализовано в других ОРМ, но на это время уйдет. А вообще, это больше вопрос любопытства.

memnek
() автор топика
Ответ на: комментарий от memnek

Мутил такое же, но для LDAP, но сабжевая задача не возникала. Можно же добавлять эти методы непосредственно к классам модели без persist, чем такое не устраивает? Тогда можно избежать проблему.

Hater ★★
()
Ответ на: комментарий от anonymous

B - готовый класс-модель. При создании класса-наследника, можно передавать всякие разные данные с настройками в виде встроенного класса. Метакласс класса B всё считывает и записывает. Хочется передать в модель класс-менеджер и его настройки, чтобы при создании дочерней модели этот класс-менеджер «инстансился» со своими настройками и работал бы с БД. Причём, для части операций нужен только сам класс (поиск, например), а для части нужен экземпляр модели. Вот я и хотел узнать, возможно ли отличить такие случаи?

memnek
() автор топика
Ответ на: комментарий от Hater

Можно, но модель уже имеет метакласс, а писать свой и совмещать эти метаклассы - та ещё проблема. Можно и без них обойтись, но я вообще хотел всё проще сделать: передавать в опциях (которые метакласс модели собирает и записывает в поле) класс и его параметры, создавать его экземпляр и записывать в поле, через которое всё будет работать. Получилось бы что-то типа DI.

memnek
() автор топика
Ответ на: комментарий от memnek

Ну т.е. звучит так, словно красота и не столь важна. Тогда persist можно вынести в начало цепочки. Что-то вроде EntityManager.find(Model, ....) и EntityManager.save(obj) (я здесь его переименовал в EntityManager).

Hater ★★
()
class MetaD(type):

  @property
  def a(cls):
    return cls._a
  
  
  @a.setter
  def a(cls, a_inst):
      class AProxy(object):
	  def __init__(self, inst):
	    self.__a = inst
	  def foo(self):
	    return self.__a.foo()
	  def get_inst(self):
	    return self.__a
      cls._a = AProxy(a_inst)
      
      
class B(object):

  @property
  def a(self):
    class AProxy1(object):
	def __init__(self, inst):
	  self.__a = inst
	def bar(self):
	  return self.__a.bar()

    return AProxy1(self.__class__._a.get_inst())
       
  __metaclass__ = MetaD

class A(object):
   def foo(self): print 'foo'
   def bar(self): print 'bar'

a = A()
B.a = a

b = B()

B.a.foo()
#B.a.bar()
b.a.bar()
#b.a.foo()

Ещё можно через inspect по стеку вверх пройти и посмотреть кто вызывает, но думаю это будет не менее накроманское решение.

Leron ★★
()

пацаны, я уже сто лет не писал на динамических языках, поэтому глуповатый вопрос: вы когда читаете код, как вы узнаете, какой тип аргументов ожидается? называете их def foo(dateArg, stringArg) или как-то иначе это решаете?

anonymous
()
Ответ на: комментарий от anonymous

Вот для этого в третьем питоне придумали аннотации :). А так, надо писать док-стринги, чтобы IDE/продвинутые редакторы могли подсказывать на их основе, ну или по коду догадываться, на худой конец.

Virtuos86 ★★★★★
()

Нашел на SO такой вариант:

from types import *

class Foo(object):
    def __init__(self):
        self.bar = methodize(bar, self)
        self.baz = 999

    @classmethod
    def bar(cls, baz):
        return 2 * baz


def methodize(func, instance):
    return MethodType(func, instance, instance.__class__)

def bar(self):
    return 4*self.baz


>>> Foo.bar(5)
10
>>> a=Foo()
>>> a.bar()
3996

memnek
() автор топика

Хочется, чтобы foo мог работать только в случае, если он вызывается из самого класса B, а метод bar - только в случае, если он вызывается из экземпляра класса B.

man архитектура
man ООП

anonymous
()

В понимании, откуда дергается твой a, тебе помогут дескрипторы. Как-то так.

А вот как эффективно каститься в нужный интерфейс или возвращать прокси-объект - сложно сходу сказать, зависит от того, что ты хочешь. Нужен там shared state между элементом класса и элементом инстанса, нет - вот это влияет. Есть пример прокси, но я его не проверял и не уверен, что нужно именно это.

nwalker
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.