我在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)
有没有办法有条件地部分评估某个对象的类方法?
答案 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()