Частичная оценка метода класса Python на основе того, где он доступен

У меня есть то, что по существу следующее в python:

class X(object): pass class Y(object): @classmethod def method(cls, x_inst, *args, **kwargs): #do some work here that requires an instance of x pass 

То, что я хотел бы сделать, это добавить динамическое свойство ко всем экземплярам X допускающим доступ к Y который неявно заполняет первый параметр method с данным экземпляром. EG Я хотел бы, чтобы следующий код работал одинаково:

 # current x = X() result = Y.method(x, 1, 2, 3) # desired x = X() xYmethod(1, 2, 3) 

Есть несколько методов для нескольких подклассов, для которых я хотел бы реализовать это поведение. То, что я сделал в настоящее время, – создать класс YProxy который фактически возвращает X , а затем поместить в него часть кода. Однако это выглядит довольно непросто и трудно поддерживать:

 class X(object): @property def Y(self): return YProxy(self) class Y(object): @classmethod def method(cls, x_inst, *args, **kwargs): #do some work here that requires an instance of x pass class YProxy(object): def __init__(self, x_inst): self.x_inst = x_inst def method(self, *args, **kwargs): return Y.method(self.x_inst, *args, **kwargs) 

Есть ли способ условно частично оценить методы класса на объекте?

5 Solutions collect form web for “Частичная оценка метода класса Python на основе того, где он доступен”

Это можно сделать с помощью объекта Descriptor + объявление класса-контейнера, явно объявляющего классы, которые необходимо обернуть таким образом в вашем целевом классе.

Объектом дескриптора является любой объект, определяющий метод __get__ , который позволяет настраивать извлечение атрибута, когда дескриптор является частью класса. В этом случае мы хотим, чтобы, когда этот атрибут, который является классом «Y», извлекается из экземпляра, всякий раз, когда метод извлекается из этого класса, экземпляр вставляется в список параметров.

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

Переведя все это в Python, мы имеем:

 import types from functools import partial class APIWrapper(object): def __init__(self, apicls, instance): self._apicls = apicls self._instance = instance def __getattribute__(self, attr): apicls = object.__getattribute__(self, "_apicls") instance = object.__getattribute__(self,"_instance") obj = getattr(apicls, attr) if isinstance(obj, types.MethodType): return partial(obj,instance) return obj class APIProperty(object): def __init__(self, cls): self.cls = cls def __get__(self, instance, cls): return APIWrapper(self.cls, instance) class Y(object): @classmethod def method(cls, x, *args): print cls, x, args class X(object): Y = APIProperty(Y) #Example usage: x = X() xYmethod(1,2,3) 

(печатает <class '__main__.Y'> <__main__.X object at 0x18ad090> (1, 2, 3) при запуске)

Но я полагаю, вы не хотите писать

 Y = APIWrapper(Y) 

для каждого из классов, которые вы хотите обернуть таким образом. (И имеют эти классы, определенные после обернутого класса, так что Y уже был проанализирован при анализе тела X).

Это можно сделать с помощью метаклассов, декораторов классов, которые должны быть определены для каждого класса, который вы хотите применить к методам – ​​вместо этого я создал функцию, которая должна быть вызвана в конце определения модуля, где вы определяете ваш класс «X» – эта функция добавит желаемые классы в качестве атрибутов для каждого определенного класса (в моем примере я хочу, чтобы класс был помечен атрибутом «auto_api», но вам нужно подойти) – Таким образом, функция «auto_api» , определение классов X и Y становится таким же (с использованием того же APIProperty и APIWrapper, что и выше)

 def auto_api(api_classes, glob_dict): for key, value in glob_dict.items(): if isinstance(value, type) and hasattr(value, "auto_api"): for api_class in api_classes: setattr(value, api_class.__name__, APIProperty(api_class)) class X(object): auto_api = True class Y(object): @classmethod def method(cls, x, *args): print cls, x, args auto_api((Y,), globals()) #Example x = X() xYmethod(1,2,3) 

Не уверен, что это то, что вы хотите, но какая-то дисциплина интроспекции / varname может привести вас куда-нибудь:

 import functools class X(object): @property def Y(self): return YProxy(self) class Y(object): @classmethod def method(cls, x_inst, *a): print x_inst, a instancemethod = type(Y.method) class YProxy(object): def __init__(self, x_inst): self.x_inst = x_inst def __getattr__(self, k): obj = getattr(Y, k) if isinstance(obj, instancemethod) and obj.func_code.co_varnames[0] == 'cls': return functools.partial(obj, self.x_inst) return obj 

Я думаю, использование cls в качестве имени для первого arg для метода класса является общепринятым. self не очень уместен.

ETA: вы также можете проверить имя переменной «x_inst» или что-то подобное, если есть другие методы класса, или использовать декоратор в соответствующих классах, чтобы установить свойство для методов и т. Д. Дело в том, что вы можете делать то, что вам нужно, в __getattr__ если у вас есть программный способ отличить, какие методы должны поставляться с экземпляром, а какие нет.

 import inspect def callthing(): my_caller = inspect.stack()[1] args, varargs, keywords, locals = inspect.getargvalues(my_caller) print args[0] 

Когда вызывается вышеупомянутая callthing , она получит аргументы своего вызывающего абонента ( [1] – «стек стека выше текущего»), печатая первый. Вы даже можете использовать именованные аргументы для «получения значения аргумента с именем x_inst ». Возможно, @classmethod должен возвращать функцию, завернутую в вызов к чему-то вроде callthing ?

Вы также можете сделать ветвление в самом декораторе, используя inspect.getargspec(my_function) , имея в виду, что фактическое значение, предоставленное аргументом, недоступно в этой точке, поскольку функция строится, а не вызывается.

Почему вы связываете классы следующим образом? конечно

 >>> class X(object): ... def __init__(self, y): ... self.y = y ... ... def method(self, *args, **kwargs): ... self.y.method(self, *args, **kwargs) >>> class Y(object): ... @classmethod ... def method(cls, x, *args): ... print cls, x, args ... >>> y = Y() >>> x = X(y) >>> x.method(Ellipsis) <class '__main__.Y'> <__main__.X object at 0xb727612c> (Ellipsis,) 

будет работать нормально?


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

Но Python уже делает именно это, например, методы классов! Если у вас есть экземпляр X и вы вызываете метод на нем, этот метод будет принимать self как первый аргумент; ты знаешь что. Так что, конечно, имеет смысл использовать это для привязки аргументов?

Можно ли сделать что-то подобное?

 class X(object): def __init__(self): self.Y = Y(self) class Y(object): def __init__(self, x): self.x = x def method1(self, *args, **kwargs): pass x = X() xYmethod1() 
  • Преобразовать временную метку с эпохи в datetime.datetime
  • Смешивание двух аудиофайлов вместе с python
  • Какая из этих двух функций более «питоновая»?
  • В Python почему меньше (1)?
  • Должен ли я беспокоиться о круговых ссылках на Python?
  • Пропаривание вставки / вставкаВсегда - длительная задержка?
  • Измените цвет __init__ и других предопределенных элементов в подсветке синтаксиса Pycharm
  • Не удалось заставить конечные точки Google работать с несколькими службами
  •  
    Interesting Posts for Van-Lav

    Django: Как получить доступ к параметрам регулярного выражения URL-адресов внутри класса промежуточного программного обеспечения?

    Преобразование списка Python в столбец в CSV

    Загрузка большого файла происходит слишком медленно

    Как выполнить / отладить веб-приложение python?

    Как я могу получить эффект «умного резкости» на моих изображениях с помощью python?

    Программно сохраняя изображение на Django ImageField

    Как добавить свойства в класс с помощью декоратора, который принимает список имен в качестве аргумента?

    Почему Python возвращает отрицательное значение часового пояса?

    APScheduler не запускается?

    Клиентский тест Django возвращает анонимного пользователя после входа в систему

    PyTables, занимающиеся данными с размером, во много раз превышающим размер памяти

    Вычисление процента меры дисперсии для k-средних?

    Подклассификация словаря Python для переопределения __setitem__

    Передача STATIC_URL файла javascript с помощью django

    Python: прописывает коды клавиш для читаемого (vim-like?) Синтаксиса

    Python - лучший язык программирования в мире.