基于它的访问位置部分评估Python类方法

时间:2012-01-21 22:49:34

标签: 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的第一个参数。 E.G我希望以下代码的工作方式相同:

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

# desired
x = X()
x.Y.method(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 个答案:

答案 0 :(得分:2)

不确定这是否是你想要的,但是一些内省/ 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”的变量名称,或者在相关类中使用装饰器来设置方法的属性,& c。关键是你可以在__getattr__中做你需要的,如果你有一些程序化的方法来区分哪些方法需要提供一个实例,哪些方法不需要。

答案 1 :(得分:2)

可以使用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()
x.Y.method(1,2,3)

(运行时打印<class '__main__.Y'> <__main__.X object at 0x18ad090> (1, 2, 3)

但我想你不想写

Y = APIWrapper(Y) 

对于要以这种方式包装的每个类。 (并且在包装类之后定义这些类,以便在解析X body时已经解析了Y)。

这可以通过元类,类装饰器来完成,这些类必须为你想要应用方法的每个类定义 - 相反,我创建了一个在模块定义结束时调用的函数,你定义你的“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()
x.Y.method(1,2,3)

答案 2 :(得分:1)

import inspect

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

当调用上面的callthing时,它将获得其调用者的参数([1]是&#34;当前一个&#34;之上的堆栈帧,打印第一个。您甚至可以使用命名参数来获取名为x_inst&#34;的参数的值。也许@classmethod应该返回一个包含在callthing

之类的调用中的函数

您还可以使用inspect.getargspec(my_function)在装饰器本身中进行分支,请记住,该点的实际在此时是不可用的,因为该函数正在构建而不是被调用。

答案 3 :(得分:0)

为什么要像这样链接这些类?当然

>>> 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,)

会工作得很好吗?


你所要求的是有些矛盾的。如果x.Y实际上只是类对象Y,那么肯定它没有你想要的这个自动参数绑定属性。所以x.Y必须是某种代理对象,为你做绑定。

但是Python已经在类的实例方法上完成了这个!如果你有X的实例并且你在其上调用了一个方法,那么该方法将接收self作为第一个参数;你知道的。那么使用它来绑定参数肯定更有意义吗?

答案 4 :(得分:0)

是否有可能做这样的事情?

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()
x.Y.method1()