根据Python类方法的访问位置对其进行部分求值

2024-09-27 23:25:05 发布

您现在位置: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我希望以下代码的工作方式相同:

^{pr2}$

我想实现这个行为的几个子类上有几个方法。我目前所做的是创建一个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)

有什么方法可以有条件地对一个对象的类方法进行部分求值吗?在


Tags: 方法selfobjectdefargssomepassdo
3条回答
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)在decorator本身中进行分支,请记住,给定参数的实际此时不可用,因为函数是构造而不是调用的。在

你需要在类上显式地声明一个对象。在

描述符对象是定义__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)

但我想你不需要写信

^{pr2}$

对于每个要以这种方式包装的类。(并在封装的类之后定义这些类,以便在解析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)

不确定这是否是您想要的,但是一些内省/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作为类方法的第一个参数的名称是公认的风格。self不太合适。在

ETA:如果还有其他类方法,也可以检查名为“x\u inst”的变量名或类似名称,或者在相关类中使用修饰符来设置方法的属性&;c、 关键是,如果您有某种编程方法来区分哪些方法需要与实例一起提供,那么您可以在__getattr__中执行所需的操作

相关问题 更多 >

    热门问题