如何获取调用方法的类?

Posted

技术标签:

【中文标题】如何获取调用方法的类?【英文标题】:How to get the class from which a method was called? 【发布时间】:2020-12-03 17:46:11 【问题描述】:

get_calling_class 函数必须通过返回调用A.f 方法的方法的类来通过以下测试:

class A:
    def f(self): return get_calling_class()

class B(A):
    def g(self): return self.f()

class C(B):
    def h(self): return self.f()

c = C()
assert c.g() == B
assert c.h() == C

【问题讨论】:

其实你想知道的是什么类定义了被调用的方法,这可能是一个非常复杂的事情要确定。 @martineau 不完全是,我已经知道定义方法A.f的类:它是A。我想要调用方法A.f的类。 是的,但是类 B 定义了方法 g — 看起来你显然希望 c.g() 返回的类。 @martineau 是的,所以我想要调用A.f 的方法的类。 您已经方便地省略了为什么您需要知道哪个子类调用了该方法。听起来整个问题可能是由糟糕的设计引起的——在面向对象的编程中,很少需要此类信息。 【参考方案1】:

走堆栈应该会给出答案。 理想情况下,答案应该在调用者的堆栈帧中。

问题是,栈帧只记录函数 名称(例如:“f”、“g”、“h”等)关于的任何信息 课程丢失。试图对丢失的信息进行逆向工程, 通过导航类层次结构(与 堆栈帧),并没有让我走得太远,而且变得复杂。

所以,这是一种不同的方法: 将类信息注入堆栈帧 (例如,使用局部变量), 并从被调用的函数中读取。

import inspect

class A:
  def f(self):
    frame = inspect.currentframe()
    callerFrame = frame.f_back
    callerLocals = callerFrame.f_locals
    return callerLocals['cls']

class B(A):
  def g(self):
    cls = B
    return self.f()
    
  def f(self):
    cls = B
    return super().f()

class C(B):
  def h(self):
    cls = C
    return super(B, self).f()
  
  def f(self):
    cls = C
    return super().f()

c = C()
assert c.h() == C
assert c.g() == B
assert c.f() == B

相关:get-fully-qualified-method-name-from-inspect-stack


不修改子类的定义: 添加了一个“外部”装饰器,用于包装类方法。 (至少作为临时解决方案。)

import inspect

class Injector:
  def __init__(self, nameStr, valueStr):
    self.nameStr = nameStr
    self.valueStr = valueStr
  
  # Should inject directly in f's local scope / stack frame.
  # As is, it just adds another stack frame on top of f.
  def injectInLocals(self, f):
    def decorate(*args, **kwargs):
      exec(f'self.nameStr = self.valueStr')
      return f(*args, **kwargs)
    return decorate

class A:
  def f(self):
    frame = inspect.currentframe()
    callerDecoratorFrame = frame.f_back.f_back  # Note:twice
    callerDecoratorLocals = callerDecoratorFrame.f_locals
    return callerDecoratorLocals['cls']

class B(A):
  def g(self): return self.f()
  def f(self): return super().f()

class C(B):
  def h(self): return super(B, self).f()
  def f(self): return super().f()

bInjector = Injector('cls', B.__name__)
B.g = bInjector.injectInLocals(B.g)
B.f = bInjector.injectInLocals(B.f)

cInjector = Injector('cls', C.__name__)
C.h = cInjector.injectInLocals(C.h)
C.f = cInjector.injectInLocals(C.f)

c = C()
assert c.h() == C
assert c.g() == B
assert c.f() == B

我发现这个链接很有趣 (但这里没有利用元类):what-are-metaclasses-in-python

也许有人甚至可以替换函数定义*, 具有代码与原始代码重复的函数; 但是直接在他们的范围内添加了当地人/信息。

* 也许在类定义完成之后; 可能在类创建期间(使用元类)。

【讨论】:

感谢您的努力!我认为这也可能是一个好方法。您甚至可以通过将cls = X 替换为__class__ 来改进它。然而,我的一个限制是我只能修改基类 (A) 和最派生类 (C),而不能修改中间类 (B)。那么你会怎么处理呢?我想到了C 上的元类。 这可能会有所帮助:***.com/a/4885951/2326961 @Maggyero 元类很有趣,但我对它们不是很熟悉。暂时添加了临时解决方法,以后可能会回来。 (根据我对它们的了解,您可能希望将元类添加到类层次结构的根目录中,以便所有派生类都受到它的影响。) 非常感谢,我喜欢你的想法。 @Maggyero 这是一个有趣的问题;出乎意料的困难。

以上是关于如何获取调用方法的类?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用PowerMock调用私有方法并获取返回值?

如何在freemarker中调用java的方法

java中有哪些类型的方法,如何调用它们?

JAVA反射中通过Class.forname()如何带参数的方法怎么赋值和调用呀?

如何调用返回类方法指针的类方法?

如何在AJAX中调用.CS中的类