如何获得调用方法的类?

问题描述

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

解决方法

步行栈应该给出答案。
理想的答案应该是在调用方的堆栈框架中。

问题是,堆栈帧仅记录该功能
名称(例如:“ 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

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

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